fix(workflow): remove unnecessary permissions #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "CI/CD publish" | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| types: [closed] | |
| branches: | |
| - main | |
| permissions: | |
| contents: write | |
| issues: write | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: "🔐 Generate app token" | |
| uses: actions/create-github-app-token@v2 | |
| id: app-token | |
| with: | |
| app-id: ${{ vars.GH_VERSIONING_APP_ID || vars.NHS_GH_VERSIONING_APP_ID }} | |
| private-key: ${{ secrets.GH_VERSIONING_APP_PRIVATE_KEY || secrets.NHS_GH_VERSIONING_APP_PRIVATE_KEY }} | |
| - name: "🙈 Mask app token" | |
| run: echo "::add-mask::${{ steps.app-token.outputs.token }}" | |
| - name: "📦 Checkout repository" | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ steps.app-token.outputs.token }} | |
| persist-credentials: false | |
| - name: "🔑 Import and configure commit signing key" | |
| env: | |
| GIT_USER_NAME: ${{ vars.GIT_SIGNING_BOT_NAME || vars.NHS_GIT_SIGNING_BOT_NAME }} | |
| GIT_USER_EMAIL: ${{ vars.GIT_SIGNING_BOT_EMAIL || vars.NHS_GIT_SIGNING_BOT_EMAIL }} | |
| GPG_PKEY: ${{ secrets.GIT_SIGNING_BOT_GPG_PRIVATE_KEY || secrets.NHS_GIT_SIGNING_BOT_GPG_PRIVATE_KEY }} | |
| GPG_PASS: ${{ secrets.GIT_SIGNING_BOT_GPG_PASSPHRASE || secrets.NHS_GIT_SIGNING_BOT_GPG_PASSPHRASE }} | |
| run: | | |
| mkdir -p ~/.gnupg | |
| chmod 700 ~/.gnupg | |
| echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf | |
| echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf | |
| export GPG_TTY=$(tty) | |
| echo "$GPG_PKEY" | gpg --batch --import | |
| key_id=$(gpg --batch --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}') | |
| git config --global user.name "$GIT_USER_NAME" | |
| git config --global user.email "$GIT_USER_EMAIL" | |
| git config --global user.signingkey "$key_id" | |
| git config --global gpg.program gpg | |
| git config --global commit.gpgsign true | |
| if [ -n "$GPG_PASS" ]; then | |
| gpgconf --kill gpg-agent || true | |
| gpgconf --launch gpg-agent || true | |
| echo "warmup" | gpg --batch --yes --passphrase "$GPG_PASS" --pinentry-mode loopback --sign >/dev/null 2>&1 || true | |
| fi | |
| # Commit signature debug info | |
| gpg --list-secret-keys --keyid-format LONG | |
| git config --get user.name | |
| git config --get user.email | |
| gpg --fingerprint "$key_id" | |
| - name: "🚀 Run release process" | |
| uses: cycjimmy/semantic-release-action@ba330626c4750c19d8299de843f05c7aa5574f62 # v5.0.2 | |
| id: release | |
| with: | |
| extra_plugins: | | |
| @semantic-release/git | |
| @semantic-release/github | |
| @semantic-release/exec | |
| conventional-changelog-conventionalcommits | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| GIT_AUTHOR_NAME: ${{ vars.GIT_SIGNING_BOT_NAME || vars.NHS_GIT_SIGNING_BOT_NAME }} | |
| GIT_AUTHOR_EMAIL: ${{ vars.GIT_SIGNING_BOT_EMAIL || vars.NHS_GIT_SIGNING_BOT_EMAIL }} | |
| GIT_COMMITTER_NAME: ${{ vars.GIT_SIGNING_BOT_NAME || vars.NHS_GIT_SIGNING_BOT_NAME }} | |
| GIT_COMMITTER_EMAIL: ${{ vars.GIT_SIGNING_BOT_EMAIL || vars.NHS_GIT_SIGNING_BOT_EMAIL }} | |
| - name: "🔓 Login to container registry" | |
| uses: docker/login-action@v3 | |
| if: steps.release.outputs.new_release_published == 'true' | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: "🐳 Build and push container image" | |
| id: build | |
| if: steps.release.outputs.new_release_published == 'true' | |
| env: | |
| IMAGE_NAME: ghcr.io/${{ github.repository }} | |
| VERSION: ${{ steps.release.outputs.new_release_version }} | |
| run: | | |
| docker build -t ${IMAGE_NAME}:app-${VERSION} ./app | |
| docker tag ${IMAGE_NAME}:app-${VERSION} ${IMAGE_NAME}:app-latest | |
| docker push ${IMAGE_NAME}:app-${VERSION} | |
| docker push ${IMAGE_NAME}:app-latest | |
| digest=$(docker inspect --format='{{index .Id}}' ${IMAGE_NAME}:app-${VERSION}) | |
| echo "image-digest=${digest}" >> $GITHUB_OUTPUT | |
| - name: "🧩 Generate SBOM" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: ghcr.io/${{ github.repository }}:app-${{ steps.release.outputs.new_release_version }} | |
| format: cyclonedx-json | |
| output-file: sbom-${{ github.event.repository.name }}-app-${{ steps.release.outputs.new_release_version }}.cdx.json | |
| - name: "🔍 Scan for CVEs" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| uses: anchore/scan-action@v7 | |
| with: | |
| sbom: sbom-${{ github.event.repository.name }}-app-${{ steps.release.outputs.new_release_version }}.cdx.json | |
| fail-build: false | |
| severity-cutoff: medium | |
| only-fixed: true | |
| output-format: table | |
| - name: "📤 Upload SBOM artefact" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-${{ steps.release.outputs.new_release_version }}-app | |
| path: sbom-${{ github.event.repository.name }}-app-${{ steps.release.outputs.new_release_version }}.cdx.json | |
| - name: "🔏 Sign container image" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| env: | |
| COSIGN_PUBLIC_KEY: ${{ secrets.COSIGN_PUBLIC_KEY || secrets.NHS_COSIGN_PUBLIC_KEY }} | |
| COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY || secrets.NHS_COSIGN_PRIVATE_KEY }} | |
| COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD || secrets.NHS_COSIGN_PASSWORD }} | |
| IMAGE_NAME: ghcr.io/${{ github.repository }} | |
| VERSION: ${{ steps.release.outputs.new_release_version }} | |
| run: | | |
| wget --no-verbose https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 | |
| sudo mv cosign-linux-amd64 /usr/local/bin/cosign | |
| sudo chmod +x /usr/local/bin/cosign | |
| echo "$COSIGN_PRIVATE_KEY" > cosign.key | |
| echo "$COSIGN_PUBLIC_KEY" > cosign.pub | |
| cosign sign --key cosign.key --tlog-upload=true ${IMAGE_NAME}:app-${VERSION} | |
| cosign verify --key cosign.pub ${IMAGE_NAME}:app-${VERSION} | |
| cosign sign --key cosign.key --tlog-upload=true ${IMAGE_NAME}:app-latest | |
| cosign verify --key cosign.pub ${IMAGE_NAME}:app-latest | |
| - name: "🧾 Generate build provenance" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| uses: actions/attest-build-provenance@v3 | |
| with: | |
| subject-name: ghcr.io/${{ github.repository }} | |
| subject-digest: ${{ steps.build.outputs.image-digest }} | |
| push-to-registry: false | |
| show-summary: true | |
| - name: "🪄 Update release notes with image info" | |
| if: steps.release.outputs.new_release_published == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ steps.release.outputs.new_release_version }} | |
| run: | | |
| existing_notes=$(gh release view "v${VERSION}" \ | |
| --repo ${{ github.repository }} \ | |
| --json body \ | |
| -q '.body') | |
| updated_notes="${existing_notes}<br/><br/>🐳 image published to [GitHub Container Registry](https://github.com/${{ github.repository }}/pkgs/container/${{ github.event.repository.name }})" | |
| gh release edit "v${VERSION}" \ | |
| --repo ${{ github.repository }} \ | |
| --notes "$updated_notes" |