Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# A full semantic-version tag is version-specific release history. After the complete action test
# matrix passes, publish that release and move only its matching major tag so `@v1` users receive
# the new compatible version. The tagged commit must already be part of main.

name: release

on:
push:
tags: ["v*.*.*"]

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}

jobs:
validate:
runs-on: ubuntu-latest
outputs:
major: ${{ steps.version.outputs.major }}
release-commit: ${{ steps.version.outputs.release-commit }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate release tag
id: version
shell: bash
run: |
set -euo pipefail
tag="$GITHUB_REF_NAME"
if [[ ! "$tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo "release tag must be an exact version such as v1.1.0" >&2
exit 1
fi

major="v${BASH_REMATCH[1]}"
release_commit="$(git rev-parse "$tag^{}")"
git fetch origin main --no-tags
if ! git merge-base --is-ancestor "$release_commit" origin/main; then
echo "$tag does not point to a commit on main" >&2
exit 1
fi

echo "major=$major" >>"$GITHUB_OUTPUT"
echo "release-commit=$release_commit" >>"$GITHUB_OUTPUT"

test:
needs: validate
uses: ./.github/workflows/test.yml

release:
needs: [validate, test]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Publish versioned release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if ! gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
gh release create "$GITHUB_REF_NAME" \
--verify-tag \
--title "$GITHUB_REF_NAME" \
--generate-notes
fi

- name: Move major tag
env:
MAJOR_TAG: ${{ needs.validate.outputs.major }}
RELEASE_COMMIT: ${{ needs.validate.outputs.release-commit }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -fa "$MAJOR_TAG" "$RELEASE_COMMIT" -m "Update $MAJOR_TAG to $GITHUB_REF_NAME"
git push origin "refs/tags/$MAJOR_TAG" --force
38 changes: 35 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,57 @@ on:
push:
branches: [main]
pull_request:
workflow_call:

env:
SOTTO_TEST_VERSION: v0.4.0

jobs:
reject-invalid-version:
name: reject-invalid-version (${{ matrix.name }})
strategy:
fail-fast: false
matrix:
include:
- name: empty
version: ""
- name: floating
version: latest
- name: partial
version: v0.4
- name: prerelease
version: v0.4.0-rc.1
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Reject an unpinned version
- name: Reject invalid version
id: invalid
continue-on-error: true
uses: ./
with:
sotto-version: latest
sotto-version: ${{ matrix.version }}
- name: Check rejection
if: steps.invalid.outcome != 'failure'
env:
REJECTED_VERSION: ${{ matrix.version }}
run: |
echo "expected the action to reject '$REJECTED_VERSION'" >&2
exit 1

reject-missing-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Reject a version with no release
id: missing
continue-on-error: true
uses: ./
with:
sotto-version: v999.999.999
- name: Check rejection
if: steps.missing.outcome != 'failure'
run: |
echo "expected the action to reject an unpinned version" >&2
echo "expected the action to reject a version with no release" >&2
exit 1

install-and-verify:
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,19 @@ Give the step an `id` to use the resolved installation details in later steps:
This action is tagged independently of the `sotto` CLI's own version (`v0.1.0`, `v0.2.0`, ...) -
`sotto-action@v1` and `sotto-version: v0.4.0` are two unrelated version numbers. See
[getsotto/sotto#67](https://github.com/getsotto/sotto/issues/67) for why.

Use `@v1` to receive backwards-compatible action updates, a full tag such as `@v1.1.0` to pin a
version-specific action release, or a full commit SHA for the strongest workflow pinning.

## Releasing

Maintainers release the action by pushing an exact semantic-version tag from `main`:

```sh
git tag -a v1.1.0 -m "v1.1.0"
git push origin v1.1.0
```

The release workflow validates the tag, runs the complete target matrix, creates the versioned
`v1.1.0` GitHub release, then moves the compatible major tag (`v1`) to the same commit. Invalid tags
and tags whose commit is not on `main` fail without publishing or moving the major tag.
Loading