Adding publish-rpm-repository action/workflow - #16
Conversation
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
seantronsen
left a comment
There was a problem hiding this comment.
Overall this seems fine to me. Were you able to test it against one of our existing repos (example: I used metadata-service when testing the gpg signing workflows)?
Aside from that, I only came across some nitpicks and out-of-scope ideas for future edits. All optional.
| - `actions/publish-rpm-repository`: Generates durable-key-signed repository metadata and safely publishes RPM repositories to S3 | ||
| - `.github/workflows/go-build-release.yml`: Reusable workflow for GoReleaser builds | ||
| - `.github/workflows/docker-build-release.yml`: Reusable workflow for multi-arch container image builds | ||
| - `.github/workflows/build-publish-container-goreleaser.yml`: Builds and publishes a container image via GoReleaser | ||
| - `.github/workflows/build-rpm-quadlet.yml`: Builds a caller repo's podman quadlet RPM | ||
| - `.github/workflows/gpg-sign-artifacts.yml`: Signs unsigned RPM artifacts with a per-run ephemeral key | ||
| - `.github/workflows/validate-rpm-quadlet.yml`: Validates a signed quadlet RPM's installed file list | ||
| - `.github/workflows/release-signed-artifacts.yml`: Publishes a GitHub Release with signed RPMs and public keys | ||
| - `.github/workflows/publish-rpm-repository.yml`: Publishes aggregated RPMs through S3 and CloudFront using GitHub OIDC |
There was a problem hiding this comment.
nitpick: it's a bit jarring for both the workflow and the action to have the same name.
| repository-path: stable/el9/x86_64 | ||
| s3-bucket: ${{ vars.RPM_REPOSITORY_BUCKET }} | ||
| aws-region: us-east-1 | ||
| aws-role-arn: ${{ vars.RPM_REPOSITORY_PUBLISHER_ROLE_ARN }} | ||
| aws-account-id: ${{ vars.AWS_ACCOUNT_ID }} | ||
| cloudfront-distribution-id: ${{ vars.RPM_REPOSITORY_DISTRIBUTION_ID }} | ||
| public-base-url: https://rpm.openchami.org | ||
| package-master-fingerprint: ${{ vars.MASTER_FPR }} | ||
| metadata-signing-key-fingerprint: ${{ vars.RPM_REPOSITORY_SIGNING_KEY_FINGERPRINT }} |
There was a problem hiding this comment.
Reminder for @seantronsen
We might want to create a github issue that focuses on standardizing varnames + usage across the recently created workflows + actions. This is out of scope for this PR, but if it's to happen it should occur before these are used in the many downstream repos under the org.
| steps: | ||
| - name: Checkout publisher action at the reusable workflow commit | ||
| uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3 | ||
| with: |
There was a problem hiding this comment.
Is there a reason we are using the full ref associated with the tag and not just the tag itself? This repo doesn't appear to have a standard (some tags, some full refs).
Out of scope, but should we standardize later on? If so, I'll create a tracking issue.
| artifact-name-signed-rpms: | ||
| description: 'Name of the signed RPM artifact to publish' | ||
| type: string |
There was a problem hiding this comment.
Grammar mismatch. Var name is plural, doc comment is singular.
| artifact-name-package-signing-keys: | ||
| description: 'Name of the certified ephemeral package public-key artifact' | ||
| type: string | ||
| default: 'public-keys' | ||
| required: false |
There was a problem hiding this comment.
Ibid, see above grammar comment.
There was a problem hiding this comment.
I do like this being an external script. We might want to refactor the other actions at some point to follow this design. Out of scope for now.
| rpm_path=${INPUT_RPM_PATH:?rpm-path is required} | ||
| repository_path=${INPUT_REPOSITORY_PATH:?repository-path is required} | ||
| s3_bucket=${INPUT_S3_BUCKET:-} | ||
| distribution_id=${INPUT_CLOUDFRONT_DISTRIBUTION_ID:-} | ||
| public_base_url=${INPUT_PUBLIC_BASE_URL:-} | ||
| package_keys_path=${INPUT_PACKAGE_SIGNING_KEYS_PATH:?package-signing-keys-path is required} | ||
| package_master_public_key=${INPUT_PACKAGE_MASTER_PUBLIC_KEY_ASC:?package master public key is required} | ||
| package_master_fingerprint=${INPUT_PACKAGE_MASTER_FINGERPRINT:?package master fingerprint is required} | ||
| authorized_package_repo_fingerprints=${INPUT_AUTHORIZED_PACKAGE_REPO_FINGERPRINTS:?authorized package repo fingerprints are required} | ||
| package_key_bundle_filename=${INPUT_PACKAGE_KEY_BUNDLE_FILENAME:-RPM-GPG-KEY-OpenCHAMI-Packages} | ||
| signing_key_b64=${INPUT_METADATA_SIGNING_KEY_ARMORED_B64:?metadata signing key is required} | ||
| signing_fingerprint=${INPUT_METADATA_SIGNING_KEY_FINGERPRINT:?metadata signing fingerprint is required} | ||
| signing_passphrase=${INPUT_METADATA_SIGNING_KEY_PASSPHRASE:-} | ||
| key_filename=${INPUT_REPOSITORY_KEY_FILENAME:-RPM-GPG-KEY-OpenCHAMI} | ||
| require_signed_rpms=$(normalize_bool "${INPUT_REQUIRE_SIGNED_RPMS:-true}") | ||
| dry_run=$(normalize_bool "${INPUT_DRY_RUN:-false}") |
There was a problem hiding this comment.
nitpick on code style: I'll defer to your judgement on this, but to me this part seems unnecessarily verbose since the script already requires the env vars utilized here. if the goal is to simply shorten the var names for future usage, we could just shorten the env vars and reference them directly?
As for checking + normalizing, we could use a loop func for this. Pasting an example below from one of my external projects:
function check-variable-assignments {
if [ "${#}" -eq 0 ]; then
echo "error: check-variable-assignments received zero arguments" >&2
return 1
fi
local return_status=0
while [ "${#}" -ne 0 ]; do
local name="${1}"
if [[ -v "${name}" ]]; then
if [[ -n "${!name}" ]]; then
echo "${name}=${!name}" >&2
else
echo "${name}: set but empty" >&2
return_status=1
fi
else
echo "${name}: required, but unset" >&2
return_status=1
fi
shift
done
return "${return_status}"
}
# usage
check-variable-assignments \
CERTIFICATE_AUTHORITY_URL \
CERTIFICATE_AUTHORITY_PROVISIONER \
FILEPATH_CERTIFICATE_AUTHORITY_CERT \
FILEPATH_CERTIFICATE_AUTHORITY_PASSWD \
SVC_DOMAIN \
FILEPATH_CERT \
FILEPATH_KEY \
FILEPATH_COMBO \
FILEPATH_COMBO_PFX \
EXPECTED_UID \
EXPECTED_GID \
SVC_KEY_TYPE \
SVC_KEY_CURVE || exitNote
This is a little bit longer, but I'd argue:
- it's more readable + maintainable in the long run
- easy to reuse
- it checks all var settings at the same time without erroring out until the end, meaning at runtime you'll discover all issues / typos simultaneously instead of one per execution.
| if [[ "$dry_run" == 'false' ]]; then | ||
| echo 'Restoring existing immutable packages from S3...' | ||
| aws s3 sync "s3://$s3_bucket/$repository_path/Packages/" "$packages_dir/" \ | ||
| --exclude '*' --include '*.rpm' --only-show-errors | ||
| aws s3 sync "s3://$s3_bucket/$repository_path/keys/packages/" "$package_keys_dir/" \ | ||
| --exclude '*' --include '*.asc' --only-show-errors | ||
| fi |
There was a problem hiding this comment.
For now, this is out of scope, but later on when we want to start supporting other distros, we might want to refactor this out to another github action.
Description
This pull request introduces a new reusable GitHub Action and workflow for securely publishing RPM repositories to S3, including robust documentation and CI enhancements. The main focus is on enabling safe, verifiable, and automated publication of signed RPM packages, with strong key management and metadata integrity guarantees. The changes add both the action implementation and comprehensive usage documentation, as well as integration into the CI pipeline for testing.
New RPM Repository Publication Action and Workflow
Major new features and documentation:
1. New reusable workflow and action for RPM repository publication
.github/workflows/publish-rpm-repository.ymlandactions/publish-rpm-repository/action.ymlto aggregate signed RPMs, generate and sign repository metadata, and publish to S3 with optional CloudFront invalidation. This includes concurrency controls, robust key handling, and a safe publication order to ensure metadata integrity. [1] [2]2. Comprehensive documentation for the new publication process
README.mdand a dedicatedactions/publish-rpm-repository/README.md, covering publication model, security, inputs/outputs, and client trust configuration. Usage examples and security best practices are included. [1] [2] [3] [4]3. CI/CD and linting enhancements
.github/workflows/ci.ymlto add atest-rpm-repository-publisherjob that exercises the new publisher inside a container, and adds a new.github/actionlint.yamlto suppress false positives for workflow-specific properties. [1] [2]4. Security and compliance
5. README enhancements and cross-linking
README.mdfor discoverability, and adds cross-references between documentation files for easier navigation. [1] [2] [3]These changes collectively provide a robust, auditable, and easy-to-use solution for publishing signed RPM repositories in a secure and automated fashion, with clear guidance for both direct and workflow-based usage.
Checklist
make test(or equivalent) locally and all tests passgit commit -s) with my real name and email<filename>.licensesidecarLICENSES/directoryType of Change
For more info, see Contributing Guidelines.