This is an automated email from the ASF dual-hosted git repository.
olabusayo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-infrastructure.git
The following commit(s) were added to refs/heads/main by this push:
new 91847b4 Make `gpg`, `svn` and `nexus` credentials optional
91847b4 is described below
commit 91847b4f79f339f581f17050b2b586944340021e
Author: olabusayoT <[email protected]>
AuthorDate: Tue Nov 4 12:23:29 2025 -0500
Make `gpg`, `svn` and `nexus` credentials optional
- we only care about them if do_publish is true. do_publish is based on the
value of `publish` input as well as whether it is a tag, snapshot or asf build.
- if do_publish is true, then `gpg_signing_key` is required and it'd error
out if not provided. If do_publish is false, we generate a key that expires in
1 day for signing and verification (public key is stored as artifact, while
private key is used for signing) without input from the user
- we write the SVN credentials to the default svn config dir servers file
~/.subversion/servers, so the post step doesn't have to worry about svn
credentials, it can access it thanks to main step
- update comments and documentation
DAFFODIL-3041
---
actions/release-candidate/README.md | 42 +++-----
actions/release-candidate/action.yml | 12 +--
actions/release-candidate/dist/main/index.js | 154 ++++++++++++++++++---------
actions/release-candidate/dist/post/index.js | 25 +++--
actions/release-candidate/src/main.js | 154 ++++++++++++++++++---------
actions/release-candidate/src/post.js | 25 +++--
6 files changed, 264 insertions(+), 148 deletions(-)
diff --git a/actions/release-candidate/README.md
b/actions/release-candidate/README.md
index 8dff629..81e8a10 100644
--- a/actions/release-candidate/README.md
+++ b/actions/release-candidate/README.md
@@ -70,18 +70,18 @@ This is useful for testing the workflow using workflow
dispatch.
## Inputs
-| Input | Required | Default | Description |
-|-----------------|----------|---------|-------------|
-| tlp_dir | yes | | Directory of the top level project in
dist/dev/ |
-| project_name | yes | | Human readable name of the project |
-| project_id | yes | | ID of the project, used in source
artifact file name |
-| project_dir | no | "" | Directory for the project in
dev/dist/<tlp_dir>/. Omit if at the root |
-| gpg_signing_key | yes | | Key used to sign artifacts |
-| svn_username | yes | | Username for publishing release
artifacts to SVN dev/dist |
-| svn_password | yes | | Password for publishing release
artifacts to SVN dev/dist |
-| nexus_username | yes | | Username for publishing release
artifacts to Nexus |
-| nexus_password | yes | | Password for publishing release
artifacts to Nexus |
-| publish | no | false | Enable/disabling publish artifacts.
Must be explicitly set to true to enable publishing. May be ignored depending
on other factors. |
+| Input | Required | Default | Description |
+|-----------------|---------------|---------|-------------|
+| tlp_dir | yes | | Directory of the top level
project in dist/dev/ |
+| project_name | yes | | Human readable name of the
project |
+| project_id | yes | | ID of the project, used in
source artifact file name |
+| project_dir | no | "" | Directory for the project in
dev/dist/<tlp_dir>/. Omit if at the root |
+| gpg_signing_key | if publishing | | Key used to sign artifacts |
+| svn_username | if publishing | | Username for publishing release
artifacts to SVN dev/dist |
+| svn_password | if publishing | | Password for publishing release
artifacts to SVN dev/dist |
+| nexus_username | if publishing | | Username for publishing release
artifacts to Nexus |
+| nexus_password | if publishing | | Password for publishing release
artifacts to Nexus |
+| publish | no | false | Enable/disabling publish
artifacts. Must be explicitly set to true to enable publishing. May be ignored
depending on other factors. |
## Outputs
@@ -165,22 +165,11 @@ Perform the following steps to test changes to the
daffodil-infrastructure repo
1. Update the `uses` action of the `ASF Release Candidate` step in the
`.github/workflows/release-candidate.yml` file of the repository to be
tested on. Then,
- push your changes to your fork of the test repository.
-2. Add the secrets required by the `ASF Release Candidate` step to your
- test repository. The following secrets are required:
-
- - DAFFODIL_GPG_SECRET_KEY (any private key without a passphrase)
- - DAFFODIL_SVN_DEV_USERNAME
- - DAFFODIL_SVN_DEV_PASSWORD,
- - NEXUS_STAGE_DEPLOYER_USER
- - NEXUS_STAGE_DEPLOYER_PW
-
- The other secrets should be set to non-empty dummy values and not actual
- usernames/passwords. They will not be used.
-3. Now you can generate a release by going to the Actions tab of your test
fork,
+ push your changes to your fork of the test repository.
+2. Now you can generate a release by going to the Actions tab of your test
fork,
and selecting the workflow that uses the release-candidate action.
Click the `Run workflow` button, and select the branch you just pushed to.
-4. Once the run is complete, you can download the release from the
`Artifacts`
+3. Once the run is complete, you can download the release from the `Artifacts`
tab under `Summary`. After downloading the release, extract it.
If you want to validate things with the check-release container, follow its
steps
@@ -194,3 +183,4 @@ podman run -it --rm \
--volume <ARTIFACT-DIR>:/release \
daffodil-check-release "NA" "NA" /release
```
+> Note: The public key needed to verify the artifacts is available in the
release-download directory.
\ No newline at end of file
diff --git a/actions/release-candidate/action.yml
b/actions/release-candidate/action.yml
index 2e08049..170d860 100644
--- a/actions/release-candidate/action.yml
+++ b/actions/release-candidate/action.yml
@@ -33,23 +33,23 @@ inputs:
default: ""
gpg_signing_key:
description: Key used to sign artifacts
- required: true
+ required: false
svn_username:
description: Username for publishing release artifacts to SVN dev/dist
- required: true
+ required: false
svn_password:
description: Password for publishing release artifacts to SVN dev/dist
- required: true
+ required: false
nexus_username:
description: Username for publishing release artifacts to Nexus
- required: true
+ required: false
nexus_password:
description: Password for publishing release artifacts to Nexus
- required: true
+ required: false
publish:
description: Enable/disabling publish artifacts. Must be explcitly set to
true to enable publishing. Maybe ignored depending on other factors.
required: false
- default: false
+ default: "false"
outputs:
artifact_dir:
diff --git a/actions/release-candidate/dist/main/index.js
b/actions/release-candidate/dist/main/index.js
index c9ce528..7b89c02 100644
--- a/actions/release-candidate/dist/main/index.js
+++ b/actions/release-candidate/dist/main/index.js
@@ -31855,46 +31855,66 @@ async function run() {
const tlp_dir = core.getInput("tlp_dir", { required: true });
const project_id = core.getInput("project_id", { required: true
});
const project_dir = core.getInput("project_dir");
- const gpg_signing_key = core.getInput("gpg_signing_key", {
required: true });
- const svn_username = core.getInput("svn_username", { required:
true });
- const svn_password = core.getInput("svn_password", { required:
true });
- const nexus_username = core.getInput("nexus_username", {
required: true });
- const nexus_password = core.getInput("nexus_password", {
required: true });
- let publish = core.getBooleanInput("publish");
-
- // import signing key into gpg and get it's key id
- let gpg_import_stdout = ""
- await exec("gpg", ["--batch", "--import", "--import-options",
"import-show"], {
- input: Buffer.from(gpg_signing_key),
- listeners: {
- stdout: (data) => { gpg_import_stdout +=
data.toString(); }
- }
- });
- const gpg_signing_key_id =
gpg_import_stdout.match("[0-9A-Z]{40}")[0];
- console.info("Using gpgp key id: " + gpg_signing_key_id);
+ const publish = core.getBooleanInput("publish");
+
+ // get the actual project version, this requires a 'VERSION'
file at
+ // the root of the repository
+ const project_version =
fs.readFileSync("VERSION").toString().trim();
+ const is_snapshot = project_version.includes("-SNAPSHOT");
+ const is_apache = process.env.GITHUB_REPOSITORY_OWNER ==
"apache";
+ const gitTagPrefix = "refs/tags/";
+ const is_tagged = github.context.eventName == "push" &&
github.context.ref.startsWith(gitTagPrefix);
+ // The publish setting must be explicitly set to true to
actually publish artifacts. Note that
+ // this is required but not sufficient as even if this setting
is true, a number of other factors (usually
+ // related to testing) can disable publishing (e.g. non-tagged,
snapshot build, non-ASF repository)
+ const do_publish = publish && is_tagged && !is_snapshot &&
is_apache
+
+ if (!do_publish) {
+ core.warning("Publishing disabled:")
+ if (!publish) core.warning("- Published releases must
set the 'publish' setting to 'true'")
+ if (!is_tagged) core.warning("- Published releases must
be triggered via a tag")
+ if (is_snapshot) core.warning("- Published releases
must have non-snapshot versions")
+ if (!is_apache) core.warning("- Published releases must
come from an ASF repository")
+ }
+
+ // we only require the gpg_singing_key if we are actually going
to publish artifacts. If it is
+ // not required and not provided, we generate a temporary key
so the workflow can still
+ // sign artifacts
+ const gpg_signing_key = core.getInput("gpg_signing_key",
{required: do_publish});
+ if (gpg_signing_key.trim() === "") {
+ // Generate keypair (non-interactive)
+ await exec("gpg", ["--batch", "--yes", "--passphrase",
'', "--quick-generate-key",
+ "Apache Daffodil Test Release
<[email protected]>" , "default", "default", "1d"], {
+ silent: true
+ });
+ } else {
+ // import signing key into gpg
+ await exec("gpg", ["--batch", "--import",
"--import-options", "import-show"], {
+ input: Buffer.from(gpg_signing_key)
+ });
+ }
- // tags must be signed with a committers key, download and
import committer
- // keys for verification later
- let committer_keys = "";
- await exec("curl", [`https://downloads.apache.org/${ tlp_dir
}/KEYS`], {
+ // Capture the key id of the most recent generated/imported key
+ let gpg_list_secret_keys_stdout = "";
+ await exec("gpg", ["--list-secret-keys", "--with-colons"], {
silent: true,
listeners: {
- stdout: (data) => { committer_keys +=
data.toString(); }
+ stdout: (data) => {
+ gpg_list_secret_keys_stdout +=
data.toString();
+ }
}
});
- await exec("gpg", ["--batch", "--import"], {
- input: Buffer.from(committer_keys)
- });
+ const gpg_signing_key_id = gpg_list_secret_keys_stdout
+ .split('\n')
+ .findLast(l => l.startsWith("fpr"))
+ .split(':')[9];
- // get the actual project version, this requires a 'VERSION'
file at
- // the root of the repository
- const project_version =
fs.readFileSync("VERSION").toString().trim();
+ console.info("Using gpgp key id: " + gpg_signing_key_id);
// figure out the release version. This should follow the
pattern
// 'v<VERSION>-rcX', where <VERSION> is the value from the
VERSION file
- const gitTagPrefix = "refs/tags/";
let release_version = "";
- if (github.context.eventName == "push" &&
github.context.ref.startsWith(gitTagPrefix)) {
+ if (is_tagged) {
// this was triggered by the push of a tag, the tag
name will be the
// version used
release_version =
github.context.ref.slice(gitTagPrefix.length);
@@ -31908,29 +31928,32 @@ async function run() {
// triggered from a tag, so we fetch it manually so we
can verify its tag
await exec("git", ["fetch", "origin", "--deepen=1",
`+${ github.context.ref }:${ github.context.ref }`]);
- // make sure the tag is signed by a committer in the
KEYS file, this
- // command fails if the tag does not verify.
- await exec("git", ["tag", "--verify", release_version]);
+ if (do_publish) {
+ // if publishing, tags must be signed with a
committers key, download and import committer
+ // keys for verification
+ let committer_keys = "";
+ await exec("curl",
[`https://downloads.apache.org/${tlp_dir}/KEYS`], {
+ silent: true,
+ listeners: {
+ stdout: (data) => {
+ committer_keys +=
data.toString();
+ }
+ }
+ });
+ await exec("gpg", ["--batch", "--import"], {
+ input: Buffer.from(committer_keys)
+ });
+
+ // make sure the tag is signed by a committer
in the KEYS file, this
+ // command fails if the tag does not verify.
+ await exec("git", ["tag", "--verify",
release_version]);
+ }
} else {
- // this was not triggered by a tag, maybe is was
manually triggered via
- // workflow_dispatch or a normal commit. We should only
publish from tags,
- // so we disable publishing. We also set the
release_version so that it has the
+ // this was not triggered by a tag, maybe it was
manually triggered via
+ // workflow_dispatch or a normal commit. We also set
the release_version so that it has the
// same format as a tag (e.g. v1.2.3-rc1)
- core.warning("Action not triggered from tag, publishing
disabled");
release_version = `v${ project_version }-rc0`;
- publish = false;
}
-
- const is_snapshot = project_version.includes("-SNAPSHOT");
-
- // disable publishing for snapshot builds or non-ASF builds.
Note that
- // publishing could still be disabled if the publish input was
explicitly set
- // to false
- if (publish && (is_snapshot ||
process.env.GITHUB_REPOSITORY_OWNER != "apache")) {
- core.warning("Publishing disabled for snapshot versions
and from non-apache repositories");
- publish = false;
- }
-
// the name of the directory where we store release artifacts
doesn't actually
// matter since it is never published anywhere. The one time
where this isn't true
// is if publishing is disabled, in which case this directory
is made available as
@@ -31950,14 +31973,41 @@ async function run() {
fs.appendFileSync(`${ sbt_dir }/plugins/build.sbt`,
'addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, `pgpSigningKey :=
Some("${ gpg_signing_key_id }")\n`);
- // enable SBT for publishing SBOM
+ // enable SBT for publishing SBOM either locally or remotely
fs.appendFileSync(`${ sbt_dir }/plugins/build.sbt`,
'addSbtPlugin("com.github.sbt" %% "sbt-sbom" % "0.4.0")\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'bomFormat :=
"xml"\n');
- if (publish) {
+ if (do_publish) {
+ // if publishing is enabled, we must configure sbt to
write to a config file for
+ // post to read from
+ const svn_username = core.getInput("svn_username", {
required: true });
+ const svn_password = core.getInput("svn_password", {
required: true });
+
+ // Create the default config directory if it doesn't
exist
+ const svn_config_dir = `${ os.homedir }/.subversion`;
+ fs.mkdirSync(`${ svn_config_dir }`, { recursive: true
});
+
+ // Write to/Overwrite the 'servers' file inside it
+ const servers_file = path.join(svn_config_dir,
'servers');
+ const servers_content = `
+[global]
+store-plaintext-passwords = yes
+store-plaintext-creds = yes
+
+[groups]
+default = *
+
+[default]
+username = ${svn_username}
+password = ${svn_password}
+`;
+ fs.writeFileSync(servers_file, servers_content.trim(),
{ mode: 0o600 });
+
// if publishing is enabled, publishing to the apache
staging repository
// with the provided credentials. We must diable
gigahorse since that fails
// to publish on some systems
+ const nexus_username = core.getInput("nexus_username",
{ required: true });
+ const nexus_password = core.getInput("nexus_password",
{ required: true });
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'ThisBuild
/ updateOptions := updateOptions.value.withGigahorse(false)\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, `ThisBuild
/ credentials += Credentials("Sonatype Nexus Repository Manager",
"repository.apache.org", "${ nexus_username }", "${ nexus_password }")\n`);
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'ThisBuild
/ publishTo := Some("Apache Staging Distribution Repository" at
"https://repository.apache.org/service/local/staging/deploy/maven2")\n');
@@ -32017,7 +32067,7 @@ async function run() {
// export state information for the post step
core.saveState("artifact_dir", artifact_dir);
core.saveState("gpg_signing_key_id", gpg_signing_key_id);
- core.saveState("publish", publish);
+ core.saveState("do_publish", do_publish);
core.saveState("release_version", release_version);
} catch (error) {
diff --git a/actions/release-candidate/dist/post/index.js
b/actions/release-candidate/dist/post/index.js
index 3337e6d..84cb73c 100644
--- a/actions/release-candidate/dist/post/index.js
+++ b/actions/release-candidate/dist/post/index.js
@@ -125521,12 +125521,10 @@ const { exec } = __nccwpck_require__(95236);
async function run() {
try {
const project_name = core.getInput("project_name", { required:
true });
- const svn_username = core.getInput("svn_username", { required:
true });
- const svn_password = core.getInput("svn_password", { required:
true });
const artifact_dir = core.getState("artifact_dir");
const gpg_signing_key_id = core.getState("gpg_signing_key_id");
- const publish = core.getState("publish") === "true";
+ const do_publish = core.getState("do_publish") === "true";
const release_version = core.getState("release_version");
// sign/checksum all artifacts
@@ -125551,9 +125549,9 @@ async function run() {
}
}
- if (publish) {
+ if (do_publish) {
await exec("svn", ["add", artifact_dir]);
- await exec("svn", ["commit", "--username",
svn_username, "--password", svn_password, "--message", `Stage ${ project_name }
${ release_version }`, artifact_dir]);
+ await exec("svn", ["commit", "--message", `Stage ${
project_name } ${ release_version }`, artifact_dir]);
} else {
// if publishing was disabled then this action was
likely just triggered
// just for testing, so upload the maven-local and
artifact directories so
@@ -125561,6 +125559,21 @@ async function run() {
// release-download directory since it could contain
files that already
// exist in the SVN checkout and were not artifacts
created by this action
const release_dir = `${ os.tmpdir() }/release-download`;
+
+ const public_key_file = `${ release_dir
}/public-key.asc`;
+ // if publishing is disabled, store public key as
artifact so it can be downloaded
+ // by the post step for verification
+ let public_key = "";
+ await exec("gpg", ["--armor", "--export",
gpg_signing_key_id], {
+ silent: true,
+ listeners: {
+ stdout: (data) => {
+ public_key += data.toString();
+ }
+ }
+ });
+ fs.appendFileSync(`${ public_key_file }`, public_key);
+
const svn_artifacts = fs.readdirSync(artifact_dir, {
recursive: true, withFileTypes: true });
const maven_artifacts = fs.readdirSync(`${ release_dir
}/maven-local`, { recursive: true, withFileTypes: true });
const upload_artifacts = [...svn_artifacts,
...maven_artifacts]
@@ -125568,7 +125581,7 @@ async function run() {
.filter((dirent) =>
!dirent.parentPath.split("/").includes(".svn"))
.map((dirent) => `${ dirent.parentPath }/${
dirent.name }`);
const artifact_client = new DefaultArtifactClient();
- artifact_client.uploadArtifact("release-download",
upload_artifacts, os.tmpdir(), {
+ artifact_client.uploadArtifact("release-download",
[...upload_artifacts, public_key_file], os.tmpdir(), {
compressionLevel: 0,
retentionDays: 1
});
diff --git a/actions/release-candidate/src/main.js
b/actions/release-candidate/src/main.js
index 274dea8..815b59c 100644
--- a/actions/release-candidate/src/main.js
+++ b/actions/release-candidate/src/main.js
@@ -26,46 +26,66 @@ async function run() {
const tlp_dir = core.getInput("tlp_dir", { required: true });
const project_id = core.getInput("project_id", { required: true
});
const project_dir = core.getInput("project_dir");
- const gpg_signing_key = core.getInput("gpg_signing_key", {
required: true });
- const svn_username = core.getInput("svn_username", { required:
true });
- const svn_password = core.getInput("svn_password", { required:
true });
- const nexus_username = core.getInput("nexus_username", {
required: true });
- const nexus_password = core.getInput("nexus_password", {
required: true });
- let publish = core.getBooleanInput("publish");
-
- // import signing key into gpg and get it's key id
- let gpg_import_stdout = ""
- await exec("gpg", ["--batch", "--import", "--import-options",
"import-show"], {
- input: Buffer.from(gpg_signing_key),
- listeners: {
- stdout: (data) => { gpg_import_stdout +=
data.toString(); }
- }
- });
- const gpg_signing_key_id =
gpg_import_stdout.match("[0-9A-Z]{40}")[0];
- console.info("Using gpgp key id: " + gpg_signing_key_id);
+ const publish = core.getBooleanInput("publish");
+
+ // get the actual project version, this requires a 'VERSION'
file at
+ // the root of the repository
+ const project_version =
fs.readFileSync("VERSION").toString().trim();
+ const is_snapshot = project_version.includes("-SNAPSHOT");
+ const is_apache = process.env.GITHUB_REPOSITORY_OWNER ==
"apache";
+ const gitTagPrefix = "refs/tags/";
+ const is_tagged = github.context.eventName == "push" &&
github.context.ref.startsWith(gitTagPrefix);
+ // The publish setting must be explicitly set to true to
actually publish artifacts. Note that
+ // this is required but not sufficient as even if this setting
is true, a number of other factors (usually
+ // related to testing) can disable publishing (e.g. non-tagged,
snapshot build, non-ASF repository)
+ const do_publish = publish && is_tagged && !is_snapshot &&
is_apache
+
+ if (!do_publish) {
+ core.warning("Publishing disabled:")
+ if (!publish) core.warning("- Published releases must
set the 'publish' setting to 'true'")
+ if (!is_tagged) core.warning("- Published releases must
be triggered via a tag")
+ if (is_snapshot) core.warning("- Published releases
must have non-snapshot versions")
+ if (!is_apache) core.warning("- Published releases must
come from an ASF repository")
+ }
+
+ // we only require the gpg_singing_key if we are actually going
to publish artifacts. If it is
+ // not required and not provided, we generate a temporary key
so the workflow can still
+ // sign artifacts
+ const gpg_signing_key = core.getInput("gpg_signing_key",
{required: do_publish});
+ if (gpg_signing_key.trim() === "") {
+ // Generate keypair (non-interactive)
+ await exec("gpg", ["--batch", "--yes", "--passphrase",
'', "--quick-generate-key",
+ "Apache Daffodil Test Release
<[email protected]>" , "default", "default", "1d"], {
+ silent: true
+ });
+ } else {
+ // import signing key into gpg
+ await exec("gpg", ["--batch", "--import",
"--import-options", "import-show"], {
+ input: Buffer.from(gpg_signing_key)
+ });
+ }
- // tags must be signed with a committers key, download and
import committer
- // keys for verification later
- let committer_keys = "";
- await exec("curl", [`https://downloads.apache.org/${ tlp_dir
}/KEYS`], {
+ // Capture the key id of the most recent generated/imported key
+ let gpg_list_secret_keys_stdout = "";
+ await exec("gpg", ["--list-secret-keys", "--with-colons"], {
silent: true,
listeners: {
- stdout: (data) => { committer_keys +=
data.toString(); }
+ stdout: (data) => {
+ gpg_list_secret_keys_stdout +=
data.toString();
+ }
}
});
- await exec("gpg", ["--batch", "--import"], {
- input: Buffer.from(committer_keys)
- });
+ const gpg_signing_key_id = gpg_list_secret_keys_stdout
+ .split('\n')
+ .findLast(l => l.startsWith("fpr"))
+ .split(':')[9];
- // get the actual project version, this requires a 'VERSION'
file at
- // the root of the repository
- const project_version =
fs.readFileSync("VERSION").toString().trim();
+ console.info("Using gpgp key id: " + gpg_signing_key_id);
// figure out the release version. This should follow the
pattern
// 'v<VERSION>-rcX', where <VERSION> is the value from the
VERSION file
- const gitTagPrefix = "refs/tags/";
let release_version = "";
- if (github.context.eventName == "push" &&
github.context.ref.startsWith(gitTagPrefix)) {
+ if (is_tagged) {
// this was triggered by the push of a tag, the tag
name will be the
// version used
release_version =
github.context.ref.slice(gitTagPrefix.length);
@@ -79,29 +99,32 @@ async function run() {
// triggered from a tag, so we fetch it manually so we
can verify its tag
await exec("git", ["fetch", "origin", "--deepen=1",
`+${ github.context.ref }:${ github.context.ref }`]);
- // make sure the tag is signed by a committer in the
KEYS file, this
- // command fails if the tag does not verify.
- await exec("git", ["tag", "--verify", release_version]);
+ if (do_publish) {
+ // if publishing, tags must be signed with a
committers key, download and import committer
+ // keys for verification
+ let committer_keys = "";
+ await exec("curl",
[`https://downloads.apache.org/${tlp_dir}/KEYS`], {
+ silent: true,
+ listeners: {
+ stdout: (data) => {
+ committer_keys +=
data.toString();
+ }
+ }
+ });
+ await exec("gpg", ["--batch", "--import"], {
+ input: Buffer.from(committer_keys)
+ });
+
+ // make sure the tag is signed by a committer
in the KEYS file, this
+ // command fails if the tag does not verify.
+ await exec("git", ["tag", "--verify",
release_version]);
+ }
} else {
- // this was not triggered by a tag, maybe is was
manually triggered via
- // workflow_dispatch or a normal commit. We should only
publish from tags,
- // so we disable publishing. We also set the
release_version so that it has the
+ // this was not triggered by a tag, maybe it was
manually triggered via
+ // workflow_dispatch or a normal commit. We also set
the release_version so that it has the
// same format as a tag (e.g. v1.2.3-rc1)
- core.warning("Action not triggered from tag, publishing
disabled");
release_version = `v${ project_version }-rc0`;
- publish = false;
}
-
- const is_snapshot = project_version.includes("-SNAPSHOT");
-
- // disable publishing for snapshot builds or non-ASF builds.
Note that
- // publishing could still be disabled if the publish input was
explicitly set
- // to false
- if (publish && (is_snapshot ||
process.env.GITHUB_REPOSITORY_OWNER != "apache")) {
- core.warning("Publishing disabled for snapshot versions
and from non-apache repositories");
- publish = false;
- }
-
// the name of the directory where we store release artifacts
doesn't actually
// matter since it is never published anywhere. The one time
where this isn't true
// is if publishing is disabled, in which case this directory
is made available as
@@ -121,14 +144,41 @@ async function run() {
fs.appendFileSync(`${ sbt_dir }/plugins/build.sbt`,
'addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, `pgpSigningKey :=
Some("${ gpg_signing_key_id }")\n`);
- // enable SBT for publishing SBOM
+ // enable SBT for publishing SBOM either locally or remotely
fs.appendFileSync(`${ sbt_dir }/plugins/build.sbt`,
'addSbtPlugin("com.github.sbt" %% "sbt-sbom" % "0.4.0")\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'bomFormat :=
"xml"\n');
- if (publish) {
+ if (do_publish) {
+ // if publishing is enabled, we must configure sbt to
write to a config file for
+ // post to read from
+ const svn_username = core.getInput("svn_username", {
required: true });
+ const svn_password = core.getInput("svn_password", {
required: true });
+
+ // Create the default config directory if it doesn't
exist
+ const svn_config_dir = `${ os.homedir }/.subversion`;
+ fs.mkdirSync(`${ svn_config_dir }`, { recursive: true
});
+
+ // Write to/Overwrite the 'servers' file inside it
+ const servers_file = path.join(svn_config_dir,
'servers');
+ const servers_content = `
+[global]
+store-plaintext-passwords = yes
+store-plaintext-creds = yes
+
+[groups]
+default = *
+
+[default]
+username = ${svn_username}
+password = ${svn_password}
+`;
+ fs.writeFileSync(servers_file, servers_content.trim(),
{ mode: 0o600 });
+
// if publishing is enabled, publishing to the apache
staging repository
// with the provided credentials. We must diable
gigahorse since that fails
// to publish on some systems
+ const nexus_username = core.getInput("nexus_username",
{ required: true });
+ const nexus_password = core.getInput("nexus_password",
{ required: true });
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'ThisBuild
/ updateOptions := updateOptions.value.withGigahorse(false)\n');
fs.appendFileSync(`${ sbt_dir }/build.sbt`, `ThisBuild
/ credentials += Credentials("Sonatype Nexus Repository Manager",
"repository.apache.org", "${ nexus_username }", "${ nexus_password }")\n`);
fs.appendFileSync(`${ sbt_dir }/build.sbt`, 'ThisBuild
/ publishTo := Some("Apache Staging Distribution Repository" at
"https://repository.apache.org/service/local/staging/deploy/maven2")\n');
@@ -188,7 +238,7 @@ async function run() {
// export state information for the post step
core.saveState("artifact_dir", artifact_dir);
core.saveState("gpg_signing_key_id", gpg_signing_key_id);
- core.saveState("publish", publish);
+ core.saveState("do_publish", do_publish);
core.saveState("release_version", release_version);
} catch (error) {
diff --git a/actions/release-candidate/src/post.js
b/actions/release-candidate/src/post.js
index 9da3f9e..45663b8 100644
--- a/actions/release-candidate/src/post.js
+++ b/actions/release-candidate/src/post.js
@@ -28,12 +28,10 @@ const { exec } = require('@actions/exec');
async function run() {
try {
const project_name = core.getInput("project_name", { required:
true });
- const svn_username = core.getInput("svn_username", { required:
true });
- const svn_password = core.getInput("svn_password", { required:
true });
const artifact_dir = core.getState("artifact_dir");
const gpg_signing_key_id = core.getState("gpg_signing_key_id");
- const publish = core.getState("publish") === "true";
+ const do_publish = core.getState("do_publish") === "true";
const release_version = core.getState("release_version");
// sign/checksum all artifacts
@@ -58,9 +56,9 @@ async function run() {
}
}
- if (publish) {
+ if (do_publish) {
await exec("svn", ["add", artifact_dir]);
- await exec("svn", ["commit", "--username",
svn_username, "--password", svn_password, "--message", `Stage ${ project_name }
${ release_version }`, artifact_dir]);
+ await exec("svn", ["commit", "--message", `Stage ${
project_name } ${ release_version }`, artifact_dir]);
} else {
// if publishing was disabled then this action was
likely just triggered
// just for testing, so upload the maven-local and
artifact directories so
@@ -68,6 +66,21 @@ async function run() {
// release-download directory since it could contain
files that already
// exist in the SVN checkout and were not artifacts
created by this action
const release_dir = `${ os.tmpdir() }/release-download`;
+
+ const public_key_file = `${ release_dir
}/public-key.asc`;
+ // if publishing is disabled, store public key as
artifact so it can be downloaded
+ // by the post step for verification
+ let public_key = "";
+ await exec("gpg", ["--armor", "--export",
gpg_signing_key_id], {
+ silent: true,
+ listeners: {
+ stdout: (data) => {
+ public_key += data.toString();
+ }
+ }
+ });
+ fs.appendFileSync(`${ public_key_file }`, public_key);
+
const svn_artifacts = fs.readdirSync(artifact_dir, {
recursive: true, withFileTypes: true });
const maven_artifacts = fs.readdirSync(`${ release_dir
}/maven-local`, { recursive: true, withFileTypes: true });
const upload_artifacts = [...svn_artifacts,
...maven_artifacts]
@@ -75,7 +88,7 @@ async function run() {
.filter((dirent) =>
!dirent.parentPath.split("/").includes(".svn"))
.map((dirent) => `${ dirent.parentPath }/${
dirent.name }`);
const artifact_client = new DefaultArtifactClient();
- artifact_client.uploadArtifact("release-download",
upload_artifacts, os.tmpdir(), {
+ artifact_client.uploadArtifact("release-download",
[...upload_artifacts, public_key_file], os.tmpdir(), {
compressionLevel: 0,
retentionDays: 1
});