Repository navigation
chore(release): publisher-collection-v0.1.0 #2
Workflow file for this run
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: Release collection | |
| # A node-collection-vX.Y.Z tag releases the `decdn.node` Ansible collection, a | |
| # publisher-collection-vX.Y.Z tag the `decdn.publisher` one, each on its own version; | |
| # the `decdn-node` Helm chart has its own workflow (release-chart.yml). See | |
| # RELEASING.md. Each step derives the collection (node|publisher) and the version from | |
| # the tag, which the gate step has checked. | |
| # | |
| # `build` always runs: it gates the tag (galaxy.yml and the collection changelog must | |
| # agree on X.Y.Z), re-runs the collection checks, builds the collection, and uploads it | |
| # with SHA256SUMS as a workflow artifact. `publish` makes it public (Galaxy, and a | |
| # GitHub Release) ONLY when the repository variable PUBLISH_ENABLED is 'true'. Until | |
| # then a tag push is a dry run. A manual dispatch is always a dry run. | |
| on: | |
| push: | |
| tags: ['node-collection-v[0-9]+.[0-9]+.[0-9]+', 'publisher-collection-v[0-9]+.[0-9]+.[0-9]+'] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: Version to dry-run, e.g. node-collection-v0.1.0 (nothing is published) | |
| required: true | |
| permissions: | |
| contents: read | |
| concurrency: | |
| # Per workflow: a manual dry run of each, both from main, must not queue behind | |
| # (or cancel) the other. | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false # never cut a half-published release short | |
| env: | |
| TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} | |
| # `bash -eo pipefail`, not the default `bash -e`, so a failure anywhere in a pipe | |
| # fails the step. | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Gate the tag against the collection and its changelog | |
| # The gate accepts every artifact's tag; a manual dispatch could pass the chart's, | |
| # so refuse it here, before anything is built. | |
| run: | | |
| [[ "$TAG" == node-collection-v* || "$TAG" == publisher-collection-v* ]] \ | |
| || { echo "::error::$TAG is not a collection tag (node-collection-vX.Y.Z or publisher-collection-vX.Y.Z)"; exit 1; } | |
| scripts/check-release-version.sh "$TAG" --notes release-notes.md | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install collection build + import tooling | |
| run: python -m pip install --upgrade ansible-core ansible-lint galaxy-importer | |
| - name: Build and validate the collection | |
| working-directory: ansible | |
| env: | |
| ANSIBLE_COLLECTIONS_PATH: collections | |
| run: | | |
| make deps | |
| make "galaxy-check-${TAG%-collection-v*}" | |
| - name: Collect the collection and write checksums | |
| run: | | |
| mkdir -p dist | |
| cp ansible/build/decdn-"${TAG%-collection-v*}"-"${TAG##*-collection-v}".tar.gz dist/ | |
| (cd dist && sha256sum -- * > SHA256SUMS) | |
| # Not a release asset, so outside SHA256SUMS: the Release body. | |
| cp release-notes.md dist/ | |
| cat dist/SHA256SUMS | |
| - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 | |
| with: | |
| name: release-${{ env.TAG }} | |
| path: dist/ | |
| if-no-files-found: error | |
| publish: | |
| needs: build | |
| if: vars.PUBLISH_ENABLED == 'true' && github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| # One collection publish at a time, so the Latest decision below cannot race another | |
| # version's. Job-level, so a dry run (which never reaches this job) cannot queue | |
| # behind or displace a real publish. | |
| concurrency: | |
| group: ${{ github.workflow }}-publish | |
| cancel-in-progress: false | |
| # Protect this environment with required reviewers in the repository settings. | |
| environment: release | |
| permissions: | |
| contents: write # the GitHub Release | |
| steps: | |
| - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 | |
| with: | |
| name: release-${{ env.TAG }} | |
| path: dist | |
| - name: Verify the artifacts are the ones build checksummed | |
| working-directory: dist | |
| run: sha256sum --check SHA256SUMS | |
| - name: Require the Galaxy API key | |
| env: | |
| GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }} | |
| run: | | |
| [ -n "$GALAXY_API_KEY" ] || { echo "::error::secret GALAXY_API_KEY is not set"; exit 1; } | |
| # The repository's Latest follows decdn.node, the node operator's collection: a | |
| # decdn.publisher release is never Latest, and a node release is Latest only when | |
| # no higher node release exists (a patch to an older line must not take Latest | |
| # from a newer one). Explicit, because gh's default leaves it to GitHub's | |
| # date-and-version guess across every release, the chart's included. Before | |
| # Galaxy, so a failing API call stops the job while it can still re-run. | |
| - name: Decide whether this release becomes Latest | |
| id: latest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| latest=false newest="" | |
| if [[ "$TAG" == node-collection-v* ]]; then | |
| releases="$(gh release list --repo "$GITHUB_REPOSITORY" --limit 1000 --json tagName --jq '.[].tagName')" | |
| newest="$(printf '%s\n' "$releases" "$TAG" \ | |
| | sed -nE 's/^node-collection-v([0-9]+\.[0-9]+\.[0-9]+)$/\1/p' | sort -V | tail -n1)" | |
| if [[ "$newest" == "${TAG##*-collection-v}" ]]; then latest=true; fi | |
| fi | |
| echo "latest=$latest" >> "$GITHUB_OUTPUT" | |
| echo "Latest: $latest (newest node collection release: ${newest:-n/a})" | |
| # The runner's system Python is externally managed (PEP 668): pip into it fails. | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install ansible-core | |
| run: python3 -m pip install --upgrade ansible-core | |
| # decdn.publisher installs only if Galaxy can resolve its decdn.node dependency. | |
| # scripts/release.sh checks origin's node-collection tags, but a tag is not an | |
| # upload (one pushed while PUBLISH_ENABLED was off, or whose publish failed), so | |
| # ask Galaxy itself, before anything is published: re-run this job once decdn.node | |
| # is up. | |
| - name: Require decdn.publisher's decdn.node dependency on Galaxy | |
| run: | | |
| [[ "$TAG" == publisher-collection-v* ]] || exit 0 | |
| tarball="dist/decdn-${TAG%-collection-v*}-${TAG##*-collection-v}.tar.gz" | |
| need="$(tar -xzOf "$tarball" MANIFEST.json \ | |
| | python3 -c 'import json, sys; print(json.load(sys.stdin)["collection_info"]["dependencies"]["decdn.node"])')" | |
| echo "decdn.publisher requires decdn.node $need" | |
| ansible-galaxy collection download "decdn.node:$need" --no-deps -p "$(mktemp -d)" \ | |
| || { echo "::error::could not resolve decdn.node $need on Galaxy (see the error above): if it is not published, publish a node-collection release first; then re-run this job"; exit 1; } | |
| # Not idempotent: Galaxy refuses a version that already exists, so a re-run | |
| # after this step succeeded fails here. RELEASING.md covers recovery. | |
| - name: Publish the collection to Galaxy | |
| env: | |
| GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }} | |
| run: ansible-galaxy collection publish dist/decdn-"${TAG%-collection-v*}"-"${TAG##*-collection-v}".tar.gz --api-key "$GALAXY_API_KEY" | |
| # Last, so the Release page only appears once the collection is live. | |
| - name: Create the GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| LATEST: ${{ steps.latest.outputs.latest }} | |
| run: | | |
| [[ "$LATEST" == true || "$LATEST" == false ]] || { echo "::error::no Latest decision ('$LATEST')"; exit 1; } | |
| gh release create "$TAG" --repo "$GITHUB_REPOSITORY" \ | |
| --title "decdn.${TAG%-collection-v*} collection ${TAG##*-collection-v}" --latest="$LATEST" \ | |
| --notes-file dist/release-notes.md \ | |
| dist/decdn-"${TAG%-collection-v*}"-*.tar.gz dist/SHA256SUMS |