-
Notifications
You must be signed in to change notification settings - Fork 0
160 lines (149 loc) · 6.38 KB
/
Copy pathrelease.yml
File metadata and controls
160 lines (149 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
name: Release
# Cut a release: verify, tag, publish.
#
# Releasing was a `git tag` by hand, and the moving `v0` alongside it was moved
# by hand too -- so a release was two commands nothing checked, and forgetting
# the second one silently left every consumer on the previous version.
#
# --- what a release is here -----------------------------------------------
#
# There is no build and nothing to attach. The artefact is this repository at a
# commit, so the tag IS the release. That also means there is no provenance to
# attest, unlike a repository that ships a binary.
#
# Note what this deliberately does NOT do, in contrast to the sibling
# komizo-actions: it does not rewrite any `uses:` refs before tagging. That
# machinery exists there because its `deploy` action composes five siblings,
# and GitHub resolves each inner `uses:` independently of the caller's ref --
# so a consumer's SHA pin would cover one file and leave five floating. Neither
# action here composes a sibling, so there is nothing to rewrite and a copy of
# that script would be ceremony guarding a problem this repository does not
# have.
#
# --- immutability ---------------------------------------------------------
#
# Every version is its own tag and repository policy is never to move one. The
# workflow refuses to overwrite a tag. GitHub nevertheless documents only a
# full commit SHA as an immutable action pin because tags can be moved/deleted.
#
# The old moving `v0` is the thing this replaces. It matches 'v[0-9]*' but not
# the three-part pattern, so the version scan below filters it out rather than
# parsing it as a version and bumping it into something meaningless.
on:
workflow_dispatch:
inputs:
bump:
description: Version bump
required: true
type: choice
options:
- patch
- minor
- major
# Nothing by default; each job asks for what it needs.
permissions: {}
concurrency: release-${{ github.repository }}
jobs:
# The same checks CI runs, from the same file. Running them again against this
# exact commit is not redundant: CI passing on this SHA earlier is a claim
# about that run, and a release is what sixteen other repositories actually
# execute. Real installs on both supported platforms are the only meaningful
# verification this repository has.
verify:
uses: ./.github/workflows/ci.yml
# Explicit, and required. The workflow-level `permissions: {}` above grants
# `contents: none`, and a called workflow cannot ask for more than its
# caller has -- ci.yml declares `contents: read`, so without this the run
# fails at STARTUP with no jobs and no log, which is a hard failure to read
# backwards from. Jobs that call a reusable workflow have to be granted
# what it declares.
permissions:
contents: read
release:
needs: verify
runs-on: ubuntu-latest
# Bounded, so a step that hangs fails here rather than sitting until the
# runner's own timeout hours later.
timeout-minutes: 20
permissions:
# Create the tag and the release. No commit is pushed -- unlike
# komizo-actions, nothing is rewritten before tagging.
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version comes from the tag history, which a shallow clone
# does not have.
fetch-depth: 0
- name: Releases come from main
run: |
set -euo pipefail
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo "::error::run releases from the main branch, not $GITHUB_REF"
exit 1
fi
- name: Determine release version
id: release
env:
BUMP: ${{ inputs.bump }}
run: |
set -euo pipefail
git fetch --tags --force
# Three-part tags only. `v0` matches the glob and would otherwise be
# read as a version and bumped into nonsense.
latest="$(git tag --list 'v[0-9]*' | sed 's/^v//' \
| grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)"
if [ -z "$latest" ]; then
case "$BUMP" in
major) version="1.0.0" ;;
minor) version="0.1.0" ;;
patch) version="0.0.1" ;;
*) echo "::error::unsupported bump: $BUMP"; exit 1 ;;
esac
else
IFS=. read -r major minor patch <<< "$latest"
case "$BUMP" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
*) echo "::error::unsupported bump: $BUMP"; exit 1 ;;
esac
version="${major}.${minor}.${patch}"
fi
tag="v${version}"
# Never move an existing tag. That is the property the version is
# for: whoever pinned it must keep getting the same files.
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "::error::$tag already exists -- releases are immutable, pick the next version"
exit 1
fi
echo "previous: ${latest:+v}${latest:-none}"
echo "releasing: $tag"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Publish the release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
# --generate-notes writes the commit list since the previous tag,
# which is the honest default for a repository with no build to
# describe. The upgrade line is appended because it is the one thing
# a consumer has to do and it is the same every time.
cat > notes.md <<NOTES
## Upgrading
\`\`\`yaml
- uses: ${GITHUB_REPOSITORY}/install@${TAG}
- uses: ${GITHUB_REPOSITORY}/publish@${TAG}
\`\`\`
Every version has its own tag, and repository policy is never to move
one. For an immutable pin, use the release's full commit SHA; GitHub
tags can be moved or deleted.
NOTES
# --target creates the tag at this commit; there is no separate
# `git tag` and push to forget.
gh release create "$TAG" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--notes-file notes.md \
--generate-notes