name: Release PR
on:
push:
branches: [master]
pull_request:
branches: [master]
types: [closed]
permissions:
contents: read
concurrency:
group: release-pr-${{ github.event_name }}
cancel-in-progress: true
jobs:
prepare:
if: "${{ github.event_name == 'push' && !startsWith(github.event.head_commit.message, 'release: v') }}"
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
changelog: ${{ steps.changelog.outputs.content }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
fetch-depth: 0
persist-credentials: false
- uses: taiki-e/install-action@065d6a08a14e61e89fb0a4c10eecdbdef39c7d8e with:
tool: git-cliff
- id: version
name: Compute next version
shell: bash
run: |
version="$(git cliff --bumped-version)"
version="${version#v}"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]
if git rev-parse --verify --quiet "refs/tags/v$version"; then
echo "No releasable changes since v$version"
exit 0
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Prepare release files
if: steps.version.outputs.version != ''
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' Cargo.toml
SKIM_DEFAULT_OPTIONS='' cargo run -- --man > man/man1/sk.1
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell bash > shell/completion.bash
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell zsh > shell/completion.zsh
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell fish > shell/completion.fish
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell nushell > shell/completion.nu
git cliff -p CHANGELOG.md -t "v$VERSION" -u
cargo generate-lockfile
echo "$VERSION" > shell/version.txt
git diff --binary -- CHANGELOG.md Cargo.lock Cargo.toml man shell > "$RUNNER_TEMP/release.patch"
test -s "$RUNNER_TEMP/release.patch"
- id: changelog
name: Render release changelog
if: steps.version.outputs.version != ''
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
delimiter="changelog-$(cat /proc/sys/kernel/random/uuid)"
{
echo "content<<$delimiter"
git cliff -u -t "v$VERSION" --strip all
echo "$delimiter"
} >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a if: steps.version.outputs.version != ''
with:
name: release-${{ github.sha }}
path: ${{ runner.temp }}/release.patch
if-no-files-found: error
update-pr:
needs: prepare
if: needs.prepare.outputs.version != ''
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
name: release-${{ github.sha }}
path: ${{ runner.temp }}/release
- name: Create the release commit
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
git fetch origin master
test "$(git rev-parse origin/master)" = "$GITHUB_SHA"
git apply "$RUNNER_TEMP/release/release.patch"
unexpected="$(git diff --name-only | grep -Ev '^(CHANGELOG.md|Cargo.lock|Cargo.toml|man/|shell/)' || true)"
test -z "$unexpected"
git switch -c release-pr
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git add CHANGELOG.md Cargo.lock Cargo.toml man shell
git commit -m "release: v$VERSION"
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 with:
client-id: ${{ vars.SKIM_RS_BOT_CLIENT_ID }}
private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Push branch and create or update PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ needs.prepare.outputs.version }}
CHANGELOG: ${{ needs.prepare.outputs.changelog }}
shell: bash
run: |
auth="$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 -w0)"
git -c http.extraheader="AUTHORIZATION: basic $auth" push --force origin HEAD:release-pr
cat > "$RUNNER_TEMP/pr-body.md" <<EOF
Automated release preparation for v$VERSION.
This branch is rebuilt from master as one commit on every push. Merge it with the repository's required squash merge; the resulting commit will be tagged v$VERSION and picked up by the release workflow.
EOF
printf '%s\n' "$CHANGELOG" >> "$RUNNER_TEMP/pr-body.md"
pr="$(gh pr list --base master --head release-pr --state open --json number --jq '.[0].number')"
if [[ -n "$pr" ]]; then
gh pr edit "$pr" --title "release: v$VERSION" --body-file "$RUNNER_TEMP/pr-body.md"
else
gh pr create --base master --head release-pr --title "release: v$VERSION" --body-file "$RUNNER_TEMP/pr-body.md"
fi
tag:
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.head.ref == 'release-pr' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
persist-credentials: false
sparse-checkout: Cargo.toml
- id: release
name: Validate release merge
env:
PR_TITLE: ${{ github.event.pull_request.title }}
shell: bash
run: |
version="$(awk -F '"' '/^version = "/ { print $2; exit }' Cargo.toml)"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]
test "$PR_TITLE" = "release: v$version"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 with:
client-id: ${{ vars.SKIM_RS_BOT_CLIENT_ID }}
private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}
permission-contents: write
- name: Tag the squash commit
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
SHA: ${{ github.event.pull_request.merge_commit_sha }}
TAG: ${{ steps.release.outputs.tag }}
run: gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$TAG" -f sha="$SHA"