From 45c9f0fd834c1b0e5a3eb6509e22c25faa05f686 Mon Sep 17 00:00:00 2001 From: Travis Raines <571832+rainest@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:15:11 -0500 Subject: [PATCH 1/2] feat: add Docker build and release call workflow Action` Add a workflow that takes a Dockerfile, target, a registry, and desired platforms, builds images, and pushes them to the registry, and creates attestations. Keeps some of the inputs from the Goreleaser workflow, but not all. I removed some stuff that looked unecessary (because it could be done in the Dockerfile instead) or where it looked like there were better options for reasonable defaults. If the pushed ref is a tag, create a release for it, unless the release already exists. Signed-off-by: Travis Raines <571832+rainest@users.noreply.github.com> --- .github/workflows/docker-build-release.yml | 164 +++++++++++++++++++++ .github/workflows/go-build-release.yml | 30 ++++ 2 files changed, 194 insertions(+) create mode 100644 .github/workflows/docker-build-release.yml diff --git a/.github/workflows/docker-build-release.yml b/.github/workflows/docker-build-release.yml new file mode 100644 index 0000000..ddb4c70 --- /dev/null +++ b/.github/workflows/docker-build-release.yml @@ -0,0 +1,164 @@ +name: Build image +run-name: Dockeer image build for ${{ github.event.push.ref }} + +on: + workflow_call: + inputs: + fetch-depth: + description: 'Git fetch depth for checkout' + required: false + type: number + default: 0 + fetch-tags: + description: 'Whether to fetch tags during checkout' + required: false + type: number + default: 1 + cgo-enabled: + description: 'Set CGO_ENABLED environment variable' + required: false + type: string + default: '' + additional-env-vars: + description: 'Additional environment variables to set (KEY=value format, one per line)' + required: false + type: string + default: '' + registry-name: + description: 'Container registry name (e.g., ghcr.io/openchami/project-name) to Generate build provenance for container' + required: true + type: string + docker-file: + description: 'Dockerfile to use' + required: false + type: string + default: 'Dockerfile' + docker-target: + description: 'Dockerfile target to build' + required: false + type: string + default: 'main' + platforms: + description: 'Platforms to build for' + type: string + default: 'linux/amd64, linux/arm64' + +permissions: write-all # Necessary for the generate-build-provenance action with containers + +jobs: + build-push-images: + environment: 'Docker Push' + runs-on: ubuntu-latest + steps: + - name: checkout repository + uses: actions/checkout@v4.2.2 + with: + fetch-depth: 0 + + - name: Setup golang + uses: actions/setup-go@v5.5.0 + with: + go-version-file: go.mod + + - run: echo "GOPATH=$(go env GOPATH)" >> "$GITHUB_ENV" + + - run: echo "GOCACHE=$(go env GOCACHE)" >> "$GITHUB_ENV" + + - name: Set CGO_ENABLED if specified + if: ${{ inputs.cgo-enabled != '' }} + run: echo "CGO_ENABLED=${{ inputs.cgo-enabled }}" >> "$GITHUB_ENV" + + - name: Set additional environment variables + if: ${{ inputs.additional-env-vars != '' }} + run: | + # Process additional environment variables + echo "${{ inputs.additional-env-vars }}" | while IFS= read -r line; do + if [[ -n "$line" && "$line" == *"="* ]]; then + echo "$line" >> "$GITHUB_ENV" + fi + done + + - name: Set up Docker Buildkit + uses: docker/setup-buildx-action@v3.11.1 + + - name: Cache Docker layers + uses: actions/cache@v4.2.3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Fill image meta + id: meta + uses: docker/metadata-action@v5.8.0 + with: + images: ${{ inputs.registry-name }} + tags: | + # branch event + type=ref,event=branch + # tag event + type=ref,event=tag + # pull request event + type=ref,event=pr + + - name: Auth to registry + uses: docker/login-action@v3.5.0 + with: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + + - name: Build and push image + id: docker_build + uses: docker/build-push-action@v6.18.0 + with: + push: true + file: ${{ inputs.docker-file }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # Unclear how https://docs.docker.com/build/ci/github-actions/attestations/ interacts with + # multi-stage builds and cache. A separate build and cache step is useful if we copy the + # same binary to multiple images, but a single invocation may still be able to handle that? + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + provenance: mode=max + sbom: true + target: ${{ inputs.docker-target }} + platforms: ${{ inputs.platforms }} + build-args: | + "TAG=${{ steps.meta.outputs.version }}" + "COMMIT=${{ github.sha }}" + "REPO_INFO=https://github.com/${{ github.repository }}.git" + "GOPATH=${{ env.GOPATH}}" + "GOCACHE=${{ env.GOCACHE}}" + "CGO_ENABLED=${{ env.CGO_ENABLED }}" + "CC=${{ env.CC }}" + - name: Attest + uses: actions/attest-build-provenance@v3 + id: attest + with: + subject-name: ${{ inputs.registry-name }} + subject-digest: ${{ steps.docker_build.outputs.digest }} + push-to-registry: true + + publish-release: + runs-on: ubuntu-latest + needs: build-push-images + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + permissions: + contents: write + steps: + - name: Parse semver string + id: semver_parser + uses: booxmedialtd/ws-action-parse-semver@v1.4.7 + with: + input_string: ${{ github.event.ref }} + version_extractor_regex: 'refs/tags/v(.*)$' + - uses: ncipollo/release-action@v1.18.0 + with: + # by default this will use the tag push tag as the tag and name + # if we want to trigger tagging from the workflow, "tag" and "commit" + # need to be set to create a new one + prerelease: ${{ steps.semver_parser.outputs.prerelease != '' }} + skipIfReleaseExists: true diff --git a/.github/workflows/go-build-release.yml b/.github/workflows/go-build-release.yml index 26635c5..9c8896e 100644 --- a/.github/workflows/go-build-release.yml +++ b/.github/workflows/go-build-release.yml @@ -58,6 +58,16 @@ on: required: false type: string default: 'auto' + devel: + description: 'Create and upload a development build' + type: boolean + required: false + default: false + ref: + description: 'The commit hash for development builds' + required: false + type: string + default: '' permissions: write-all # Necessary for the generate-build-provenance action with containers @@ -86,6 +96,7 @@ jobs: with: fetch-tags: ${{ inputs.fetch-tags }} fetch-depth: ${{ inputs.fetch-depth }} + ref: ${{ inputs.ref }} - name: Set build environment variables run: | @@ -161,3 +172,22 @@ jobs: subject-name: ${{ inputs.registry-name }} subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} push-to-registry: true + + # Credit to the https://github.com/goreleaser/goreleaser/issues/2828#issuecomment-1311662146 workaround + # for nightly/snapshot builds being locked behind the paid version of goreleaser. + - name: List snapshot images + # types are hard vov https://github.com/actions/runner/issues/2238 + if: true + run: | + docker image ls --format "{{.Repository}}:{{.Tag}}" | \ + grep -e "$GITHUB_REPOSITORY:.*$(git rev-parse --short HEAD).*" | \ + paste -sd ' ' /dev/stdin > images + - name: Push snapshot images + if: true + run: | + cat images | xargs -d ' ' -I{} -n1 sh -c "docker push {}" + - name: Create and push manifest for :snapshot tag + if: true + run: | + docker manifest create $GITHUB_REPOSITORY:snapshot $(cat images) + docker manifest push $GITHUB_REPOSITORY:snapshot From 7a57d8e62163319bdd8aede6a95b7bce2ebdfc09 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Wed, 5 Nov 2025 17:33:36 -0500 Subject: [PATCH 2/2] Addressing linter issues Signed-off-by: Alex Lovell-Troy --- .github/workflows/ci.yml | 9 ++-- .github/workflows/go-build-release.yml | 57 ++++++++++++++++---------- actions/gpg-ephemeral-key/action.yml | 2 +- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04b97e0..48b2fb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: actionlint - uses: raven-actions/actionlint@v1 + uses: raven-actions/actionlint@v2.0.1 test-actions: runs-on: ubuntu-latest @@ -50,15 +50,16 @@ jobs: sudo apt-get install -y ruby ruby-dev build-essential rpm gnupg gem install --user-install --no-document fpm # Ensure Ruby gem bin dir is in PATH for future steps - echo "$(ruby -e 'print Gem.bindir')" >> $GITHUB_PATH + ruby -e 'print Gem.bindir' >> "$GITHUB_PATH" - name: Build dummy RPM run: | # Ensure Gem.bindir is in PATH so fpm can be found - export PATH="$(ruby -e 'print Gem.bindir'):$PATH" + gem_bindir=$(ruby -e 'print Gem.bindir') + export PATH="${gem_bindir}:$PATH" # Show where fpm is - echo "Gem.bindir is: $(ruby -e 'print Gem.bindir')" + echo "Gem.bindir is: ${gem_bindir}" which /root/.local/share/gem/ruby/3.0.0/bin/fpm || { echo "ERROR: fpm not found"; exit 1; } /root/.local/share/gem/ruby/3.0.0/bin/fpm --version mkdir -p dist diff --git a/.github/workflows/go-build-release.yml b/.github/workflows/go-build-release.yml index 9c8896e..df510d2 100644 --- a/.github/workflows/go-build-release.yml +++ b/.github/workflows/go-build-release.yml @@ -100,14 +100,24 @@ jobs: - name: Set build environment variables run: | - echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV - echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV - echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV - echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV + if git diff-index --quiet HEAD --; then + git_state='clean' + else + git_state='dirty' + fi + build_host=$(hostname) + go_version=$(go version | awk '{print $3}') + build_user=$(whoami) + { + echo "GIT_STATE=${git_state}" + echo "BUILD_HOST=${build_host}" + echo "GO_VERSION=${go_version}" + echo "BUILD_USER=${build_user}" + } >> "$GITHUB_ENV" - name: Set CGO_ENABLED if specified if: ${{ inputs.cgo-enabled != '' }} - run: echo "CGO_ENABLED=${{ inputs.cgo-enabled }}" >> $GITHUB_ENV + run: echo "CGO_ENABLED=${{ inputs.cgo-enabled }}" >> "$GITHUB_ENV" - name: Set additional environment variables if: ${{ inputs.additional-env-vars != '' }} @@ -115,7 +125,7 @@ jobs: # Process additional environment variables echo "${{ inputs.additional-env-vars }}" | while IFS= read -r line; do if [[ -n "$line" && "$line" == *"="* ]]; then - echo "$line" >> $GITHUB_ENV + echo "$line" >> "$GITHUB_ENV" fi done @@ -127,15 +137,16 @@ jobs: id: snapshot_flags run: | if [[ "${{ inputs.snapshot }}" == "true" ]]; then - echo "flags=--snapshot" >> $GITHUB_OUTPUT + echo "flags=--snapshot" >> "$GITHUB_OUTPUT" elif [[ "${{ inputs.snapshot }}" == "false" ]]; then - echo "flags=" >> $GITHUB_OUTPUT + echo "flags=" >> "$GITHUB_OUTPUT" else # auto mode - snapshot if not a tag - if [[ "${{ github.ref }}" != refs/tags/v* ]]; then - echo "flags=--snapshot" >> $GITHUB_OUTPUT + github_ref="${{ github.ref }}" + if [[ "$github_ref" != refs/tags/v* ]]; then + echo "flags=--snapshot" >> "$GITHUB_OUTPUT" else - echo "flags=" >> $GITHUB_OUTPUT + echo "flags=" >> "$GITHUB_OUTPUT" fi fi @@ -152,13 +163,16 @@ jobs: id: process_goreleaser_output if: ${{ !contains(steps.snapshot_flags.outputs.flags, '--snapshot') }} run: | - echo "const fs = require('fs');" > process.js - echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js - echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js - echo "console.log(firstNonNullDigest);" >> process.js - echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js + { + echo "const fs = require('fs');" + echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' + echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" + echo "console.log(firstNonNullDigest);" + echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" + } > process.js node process.js - echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT + digest=$(cat digest.txt) + echo "digest=${digest}" >> "$GITHUB_OUTPUT" - name: Attest Binaries uses: actions/attest-build-provenance@v1 @@ -179,15 +193,16 @@ jobs: # types are hard vov https://github.com/actions/runner/issues/2238 if: true run: | + short_sha=$(git rev-parse --short HEAD) docker image ls --format "{{.Repository}}:{{.Tag}}" | \ - grep -e "$GITHUB_REPOSITORY:.*$(git rev-parse --short HEAD).*" | \ + grep -e "$GITHUB_REPOSITORY:.*${short_sha}.*" | \ paste -sd ' ' /dev/stdin > images - name: Push snapshot images if: true run: | - cat images | xargs -d ' ' -I{} -n1 sh -c "docker push {}" + xargs -d ' ' -I{} -n1 sh -c "docker push {}" < images - name: Create and push manifest for :snapshot tag if: true run: | - docker manifest create $GITHUB_REPOSITORY:snapshot $(cat images) - docker manifest push $GITHUB_REPOSITORY:snapshot + docker manifest create "$GITHUB_REPOSITORY:snapshot" "$(cat images)" + docker manifest push "$GITHUB_REPOSITORY:snapshot" diff --git a/actions/gpg-ephemeral-key/action.yml b/actions/gpg-ephemeral-key/action.yml index 70b2027..0d6df0b 100644 --- a/actions/gpg-ephemeral-key/action.yml +++ b/actions/gpg-ephemeral-key/action.yml @@ -1,7 +1,7 @@ name: 'Generate and Sign Ephemeral GPG Key' author: 'OpenCHAMI' branding: - icon: 'key' + icon: 'lock' color: 'purple' description: 'Creates an ephemeral GPG key per build and signs it with a repo-scoped subkey'