Skip to content

Commit f2ff1aa

Browse files
authored
Merge pull request #42 from MikeGarde/fix/release-process
Simplify and stabilize release process logic
2 parents 59c7b7a + 52e4634 commit f2ff1aa

2 files changed

Lines changed: 29 additions & 166 deletions

File tree

.github/workflows/release.yml

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,113 +14,44 @@ on:
1414
description: Release version (X.Y.Z)
1515
required: true
1616
type: string
17-
pull_request:
18-
types: [closed]
19-
branches:
20-
- develop
21-
- main
2217

2318
permissions:
2419
actions: write
2520
contents: write
26-
pull-requests: read
2721

2822
concurrency:
29-
group: release-${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || github.event.pull_request.base.ref }}
23+
group: release-${{ inputs.target_branch }}
3024
cancel-in-progress: false
3125

3226
env:
3327
CARGO_TERM_COLOR: always
34-
MAIN_BASE_VERSION: ${{ vars.MAIN_BASE_VERSION }}
35-
DEVELOP_BASE_VERSION: ${{ vars.DEVELOP_BASE_VERSION }}
3628

3729
jobs:
3830
plan:
39-
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true
4031
runs-on: ubuntu-latest
4132
outputs:
42-
should_release: ${{ steps.plan.outputs.should_release }}
43-
reason: ${{ steps.plan.outputs.reason }}
44-
bump: ${{ steps.plan.outputs.bump }}
45-
base_version: ${{ steps.plan.outputs.base_version }}
4633
previous_version: ${{ steps.plan.outputs.previous_version }}
4734
version: ${{ steps.plan.outputs.version }}
4835
target_branch: ${{ steps.plan.outputs.target_branch }}
49-
source_branch: ${{ steps.plan.outputs.source_branch }}
50-
merge_sha: ${{ steps.context.outputs.merge_sha }}
5136
steps:
5237
- name: Checkout selected branch
53-
if: github.event_name == 'workflow_dispatch'
5438
uses: actions/checkout@v4
5539
with:
5640
ref: ${{ inputs.target_branch }}
5741
fetch-depth: 0
5842

59-
- name: Checkout merge commit
60-
if: github.event_name == 'pull_request'
61-
uses: actions/checkout@v4
62-
with:
63-
ref: ${{ github.event.pull_request.merge_commit_sha }}
64-
fetch-depth: 0
65-
6643
- name: Fetch tags
6744
run: git fetch --force --tags
6845

69-
- name: Resolve base versions
70-
id: bases
71-
shell: bash
72-
run: |
73-
set -euo pipefail
74-
75-
current_version="$(
76-
sed -nE 's/^version = "([0-9]+\.[0-9]+\.[0-9]+)"/\1/p' Cargo.toml | head -n1
77-
)"
78-
79-
if [[ -z "${current_version}" ]]; then
80-
echo "Could not determine the current Cargo version." >&2
81-
exit 1
82-
fi
83-
84-
echo "main_base_version=${MAIN_BASE_VERSION:-$current_version}" >> "$GITHUB_OUTPUT"
85-
echo "develop_base_version=${DEVELOP_BASE_VERSION:-$current_version}" >> "$GITHUB_OUTPUT"
86-
87-
- name: Resolve release context
88-
id: context
89-
shell: bash
90-
run: |
91-
set -euo pipefail
92-
93-
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
94-
echo "target_branch=${{ inputs.target_branch }}" >> "$GITHUB_OUTPUT"
95-
echo "source_branch=workflow_dispatch" >> "$GITHUB_OUTPUT"
96-
echo "release_version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
97-
echo "merge_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
98-
else
99-
echo "target_branch=${{ github.event.pull_request.base.ref }}" >> "$GITHUB_OUTPUT"
100-
echo "source_branch=${{ github.event.pull_request.head.ref }}" >> "$GITHUB_OUTPUT"
101-
echo "release_version=" >> "$GITHUB_OUTPUT"
102-
echo "merge_sha=${{ github.event.pull_request.merge_commit_sha }}" >> "$GITHUB_OUTPUT"
103-
fi
104-
10546
- name: Plan release
10647
id: plan
10748
env:
108-
TARGET_BRANCH: ${{ steps.context.outputs.target_branch }}
109-
SOURCE_BRANCH: ${{ steps.context.outputs.source_branch }}
110-
RELEASE_VERSION: ${{ steps.context.outputs.release_version }}
111-
MAIN_BASE_VERSION: ${{ steps.bases.outputs.main_base_version }}
112-
DEVELOP_BASE_VERSION: ${{ steps.bases.outputs.develop_base_version }}
49+
TARGET_BRANCH: ${{ inputs.target_branch }}
50+
RELEASE_VERSION: ${{ inputs.version }}
11351
run: devops/ga-release-plan.sh
11452

115-
- name: Stop on non-release branches
116-
if: steps.plan.outputs.should_release != 'true'
117-
shell: bash
118-
run: |
119-
echo "Skipping release: ${{ steps.plan.outputs.reason }}"
120-
12153
prepare_release:
12254
needs: plan
123-
if: needs.plan.outputs.should_release == 'true'
12455
runs-on: ubuntu-latest
12556
outputs:
12657
release_sha: ${{ steps.release_commit.outputs.release_sha }}
@@ -142,17 +73,6 @@ jobs:
14273
exit 1
14374
fi
14475
145-
- name: Ensure release commit includes merged change
146-
if: github.event_name == 'pull_request'
147-
shell: bash
148-
run: |
149-
set -euo pipefail
150-
151-
if ! git merge-base --is-ancestor "${{ needs.plan.outputs.merge_sha }}" HEAD; then
152-
echo "The target branch no longer contains merge commit ${{ needs.plan.outputs.merge_sha }}." >&2
153-
exit 1
154-
fi
155-
15676
- name: Configure git author
15777
run: |
15878
git config user.name "github-actions[bot]"
@@ -189,7 +109,6 @@ jobs:
189109
190110
create_release:
191111
needs: [plan, prepare_release]
192-
if: needs.plan.outputs.should_release == 'true'
193112
runs-on: ubuntu-latest
194113
steps:
195114
- name: Check out release commit
@@ -205,7 +124,7 @@ jobs:
205124
set -euo pipefail
206125
207126
NOTES_ARGS=()
208-
if [[ "${{ needs.plan.outputs.previous_version }}" != "${{ needs.plan.outputs.base_version }}" ]]; then
127+
if [[ -n "${{ needs.plan.outputs.previous_version }}" ]]; then
209128
NOTES_ARGS+=(-f previous_tag_name="${{ needs.plan.outputs.previous_version }}")
210129
fi
211130

devops/ga-release-plan.sh

Lines changed: 25 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,122 +3,66 @@
33
set -euo pipefail
44

55
TARGET_BRANCH="${TARGET_BRANCH:-${1:-}}"
6-
SOURCE_BRANCH="${SOURCE_BRANCH:-${2:-}}"
7-
MAIN_BASE_VERSION="${MAIN_BASE_VERSION:-}"
8-
DEVELOP_BASE_VERSION="${DEVELOP_BASE_VERSION:-}"
9-
RELEASE_VERSION="${RELEASE_VERSION:-}"
6+
RELEASE_VERSION="${RELEASE_VERSION:-${2:-}}"
107

118
if [[ -z "${TARGET_BRANCH}" ]]; then
12-
echo "usage: TARGET_BRANCH=<main|develop> SOURCE_BRANCH=<branch> RELEASE_VERSION=X.Y.Z MAIN_BASE_VERSION=X.Y.Z DEVELOP_BASE_VERSION=X.Y.Z $0" >&2
9+
echo "usage: TARGET_BRANCH=<main|develop> RELEASE_VERSION=X.Y.Z $0" >&2
1310
exit 1
1411
fi
1512

16-
semver_pattern='^[0-9]+\.[0-9]+\.[0-9]+$'
17-
18-
require_semver() {
19-
local value="$1"
20-
local label="$2"
21-
22-
if [[ ! "${value}" =~ ${semver_pattern} ]]; then
23-
echo "${label} must be a semantic version like 1.0.0" >&2
24-
exit 1
25-
fi
26-
}
27-
28-
require_semver "${MAIN_BASE_VERSION}" "MAIN_BASE_VERSION"
29-
require_semver "${DEVELOP_BASE_VERSION}" "DEVELOP_BASE_VERSION"
30-
31-
if [[ -n "${RELEASE_VERSION}" ]]; then
32-
require_semver "${RELEASE_VERSION}" "RELEASE_VERSION"
33-
fi
34-
3513
case "${TARGET_BRANCH}" in
36-
main)
37-
BASE_VERSION="${MAIN_BASE_VERSION}"
38-
;;
39-
develop)
40-
BASE_VERSION="${DEVELOP_BASE_VERSION}"
41-
;;
14+
main|develop) ;;
4215
*)
4316
echo "Unsupported target branch: ${TARGET_BRANCH}" >&2
4417
exit 1
4518
;;
4619
esac
4720

48-
BASE_MAJOR="${BASE_VERSION%%.*}"
49-
TAG_PATTERN="^${BASE_MAJOR}\\.[0-9]+\\.[0-9]+$"
21+
semver_pattern='^[0-9]+\.[0-9]+\.[0-9]+$'
22+
23+
if [[ ! "${RELEASE_VERSION}" =~ ${semver_pattern} ]]; then
24+
echo "RELEASE_VERSION must be a semantic version like 1.0.0" >&2
25+
exit 1
26+
fi
27+
28+
# Newest released version (empty when no semver tags exist yet).
5029
PREVIOUS_VERSION="$(
5130
git tag --list \
52-
| grep -E "${TAG_PATTERN}" || true
31+
| grep -E "${semver_pattern}" || true
5332
)"
5433
PREVIOUS_VERSION="$(
5534
printf '%s\n' "${PREVIOUS_VERSION}" \
5635
| sort -V \
5736
| tail -n 1
5837
)"
59-
PREVIOUS_VERSION="${PREVIOUS_VERSION:-${BASE_VERSION}}"
38+
39+
# Baseline for the next version: the newest of the latest tag and the version
40+
# currently in Cargo.toml, so the requested version can never move backwards.
41+
CARGO_VERSION="$(
42+
sed -nE 's/^version = "([0-9]+\.[0-9]+\.[0-9]+)"/\1/p' Cargo.toml | head -n1
43+
)"
6044

6145
CURRENT_VERSION="$(
62-
printf '%s\n%s\n' "${BASE_VERSION}" "${PREVIOUS_VERSION}" \
46+
printf '%s\n%s\n' "${PREVIOUS_VERSION}" "${CARGO_VERSION}" \
6347
| sort -V \
6448
| tail -n 1
6549
)"
6650

67-
if [[ -n "${RELEASE_VERSION}" ]]; then
68-
SHOULD_RELEASE="true"
69-
REASON=""
70-
BUMP="manual"
71-
72-
RELEASE_MAJOR="${RELEASE_VERSION%%.*}"
73-
if [[ "${RELEASE_MAJOR}" != "${BASE_MAJOR}" ]]; then
74-
echo "RELEASE_VERSION ${RELEASE_VERSION} does not match the ${TARGET_BRANCH} release line ${BASE_MAJOR}.x.x" >&2
75-
exit 1
76-
fi
77-
78-
if [[ "$(printf '%s\n%s\n' "${CURRENT_VERSION}" "${RELEASE_VERSION}" | sort -V | tail -n 1)" != "${RELEASE_VERSION}" ]] || [[ "${RELEASE_VERSION}" == "${CURRENT_VERSION}" ]]; then
79-
echo "RELEASE_VERSION ${RELEASE_VERSION} must be newer than ${CURRENT_VERSION}" >&2
80-
exit 1
81-
fi
82-
83-
VERSION="${RELEASE_VERSION}"
84-
elif [[ "${SOURCE_BRANCH}" == feat/* ]]; then
85-
SHOULD_RELEASE="true"
86-
REASON=""
87-
BUMP="minor"
88-
IFS='.' read -r MAJOR MINOR PATCH <<< "${CURRENT_VERSION}"
89-
MINOR=$((MINOR + 1))
90-
PATCH=0
91-
VERSION="${MAJOR}.${MINOR}.${PATCH}"
92-
elif [[ "${SOURCE_BRANCH}" == bugfix/* ]]; then
93-
SHOULD_RELEASE="true"
94-
REASON=""
95-
BUMP="patch"
96-
IFS='.' read -r MAJOR MINOR PATCH <<< "${CURRENT_VERSION}"
97-
PATCH=$((PATCH + 1))
98-
VERSION="${MAJOR}.${MINOR}.${PATCH}"
99-
else
100-
SHOULD_RELEASE="false"
101-
REASON="Source branch must start with feat/ or bugfix/."
102-
BUMP="none"
103-
VERSION=""
51+
newest="$(printf '%s\n%s\n' "${CURRENT_VERSION}" "${RELEASE_VERSION}" | sort -V | tail -n 1)"
52+
if [[ "${newest}" != "${RELEASE_VERSION}" ]] || [[ "${RELEASE_VERSION}" == "${CURRENT_VERSION}" ]]; then
53+
echo "RELEASE_VERSION ${RELEASE_VERSION} must be newer than ${CURRENT_VERSION}" >&2
54+
exit 1
10455
fi
10556

57+
VERSION="${RELEASE_VERSION}"
58+
10659
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
10760
{
108-
echo "should_release=${SHOULD_RELEASE}"
109-
echo "reason=${REASON}"
110-
echo "bump=${BUMP}"
111-
echo "base_version=${BASE_VERSION}"
11261
echo "previous_version=${PREVIOUS_VERSION}"
11362
echo "version=${VERSION}"
11463
echo "target_branch=${TARGET_BRANCH}"
115-
echo "source_branch=${SOURCE_BRANCH}"
11664
} >> "${GITHUB_OUTPUT}"
11765
fi
11866

119-
echo "should_release=${SHOULD_RELEASE}"
120-
echo "reason=${REASON}"
121-
echo "bump=${BUMP}"
122-
echo "base_version=${BASE_VERSION}"
12367
echo "previous_version=${PREVIOUS_VERSION}"
12468
echo "version=${VERSION}"

0 commit comments

Comments
 (0)