diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 04d2ba1..05aaae7 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -15,13 +15,43 @@ defaults: shell: bash jobs: - publish: + images: if: >- - github.event.workflow_run.event == 'push' && + (github.event.workflow_run.event == 'push' || github.event.workflow_run.event == 'workflow_dispatch') && (github.event.workflow_run.head_branch == 'main' || startsWith(github.event.workflow_run.head_branch, 'v')) && github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-26.04 + timeout-minutes: 5 + permissions: + actions: read + outputs: + available: ${{ steps.images.outputs.available }} + steps: + - name: Check for tested images + id: images + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RUN_ID: ${{ github.event.workflow_run.id }} + SOURCE_EVENT: ${{ github.event.workflow_run.event }} + run: | + count=$(gh api --paginate --slurp "repos/$GH_REPO/actions/runs/$RUN_ID/artifacts?per_page=100" | \ + jq '[.[].artifacts[].name | select(. == "docker-linux-amd64" or . == "docker-linux-arm64")] | unique | length') + if [[ "$count" == 2 ]]; then + echo 'available=true' >> "$GITHUB_OUTPUT" + elif [[ "$count" == 0 && "$SOURCE_EVENT" == workflow_dispatch ]]; then + echo 'available=false' >> "$GITHUB_OUTPUT" + echo 'Skipping Docker publication: packaging-only manual run.' >> "$GITHUB_STEP_SUMMARY" + else + echo 'Expected tested images for both amd64 and arm64.' >&2 + exit 1 + fi + + publish: + needs: images + if: needs.images.outputs.available == 'true' + runs-on: ubuntu-26.04 environment: docker-publish timeout-minutes: 15 permissions: diff --git a/.github/workflows/manual-release.yml b/.github/workflows/manual-release.yml new file mode 100644 index 0000000..64a727f --- /dev/null +++ b/.github/workflows/manual-release.yml @@ -0,0 +1,50 @@ +name: Create Release + +on: + workflow_dispatch: + +permissions: + contents: read + +defaults: + run: + shell: bash + +concurrency: + group: create-release + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-26.04 + timeout-minutes: 5 + permissions: + contents: write + actions: write + steps: + - name: Require main + run: | + if [[ "$GITHUB_REF" != refs/heads/main ]]; then + echo 'Create Release must be run from main.' >&2 + exit 1 + fi + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: main + fetch-depth: 0 + - name: Create and push release tag + id: tag + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + ./release.sh origin + tag="v$(git show HEAD:VERSION)" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "Created \`$tag\`." >> "$GITHUB_STEP_SUMMARY" + - name: Start release pipeline + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RELEASE_TAG: ${{ steps.tag.outputs.tag }} + # A tag pushed with GITHUB_TOKEN does not trigger push workflows. + run: gh workflow run release.yml --ref "$RELEASE_TAG" -f publish=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fcb8d99..7983685 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,11 @@ on: branches: [main] tags: ['v*'] workflow_dispatch: + inputs: + publish: + description: Publish the selected version tag and Docker images + type: boolean + default: false permissions: contents: read @@ -21,7 +26,7 @@ concurrency: jobs: checks: - if: github.event_name == 'push' + if: github.event_name == 'push' || (inputs.publish && startsWith(github.ref, 'refs/tags/v')) uses: ./.github/workflows/ci.yml build: @@ -45,7 +50,13 @@ jobs: env: ARCH: ${{ matrix.arch }} BUILD_RELEASE: ${{ github.event_name != 'push' || github.ref_type == 'tag' }} + EXPORT_IMAGE: ${{ github.event_name == 'push' || (inputs.publish && startsWith(github.ref, 'refs/tags/v')) }} steps: + - name: Require version tag for publication + if: inputs.publish && !startsWith(github.ref, 'refs/tags/v') + run: | + echo 'Publishing requires a vMAJOR.MINOR.PATCH tag. Use Create Release to tag main.' >&2 + exit 1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -171,12 +182,12 @@ jobs: if-no-files-found: error compression-level: 0 - name: Export tested image - if: github.event_name == 'push' + if: env.EXPORT_IMAGE == 'true' env: IMAGE: livepeer:test-${{ matrix.arch }} run: docker save --output "$RUNNER_TEMP/docker-$ARCH.tar" "$IMAGE" - name: Save tested image - if: github.event_name == 'push' + if: env.EXPORT_IMAGE == 'true' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: docker-linux-${{ matrix.arch }} @@ -187,7 +198,7 @@ jobs: publish: needs: [checks, build] - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + if: startsWith(github.ref, 'refs/tags/v') && (github.event_name == 'push' || inputs.publish) runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: diff --git a/docs/development.md b/docs/development.md index 06e6b76..fbc85eb 100644 --- a/docs/development.md +++ b/docs/development.md @@ -16,8 +16,10 @@ builder in `docker/Dockerfile` aligned with it. ## Releases Set the [VERSION](../VERSION) file to the next `MAJOR.MINOR.PATCH` number without -a `v` prefix, leading zeroes, prerelease suffix, or build metadata. Commit it, -then run the release helper with the Git remote name: +a `v` prefix, leading zeroes, prerelease suffix, or build metadata. Commit it to +`main`, then run **Create Release** from GitHub Actions. + +You can also run the release helper locally with the Git remote name: ```sh ./release.sh github @@ -26,9 +28,17 @@ then run the release helper with the Git remote name: The helper creates and pushes a `vMAJOR.MINOR.PATCH` tag. Release tags cannot be updated or deleted, and published releases are immutable. The [Release workflow](../.github/workflows/release.yml) on GitHub publishes -bundles after CI and packaging tests pass. To test packaging before tagging, run -**Release** manually from GitHub Actions; manual runs save artifacts without -publishing a release. +bundles after CI and packaging tests pass. Successful publication also starts +Docker image publishing. + +If **Create Release** creates the tag but fails to start **Release**, run: + +```sh +gh workflow run release.yml --repo livepeer/node --ref vMAJOR.MINOR.PATCH -f publish=true +``` + +If **Release** or **Publish Docker** fails, choose **Re-run failed jobs** before +its image artifacts expire. ## Checks