-
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathaction.yml
More file actions
324 lines (285 loc) Β· 13.2 KB
/
action.yml
File metadata and controls
324 lines (285 loc) Β· 13.2 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
name: 'Craft Prepare Release'
description: 'Prepare a new release using Craft'
inputs:
version:
description: >
Version to release. Can be a semver string (e.g., "1.2.3"),
a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
required: false
merge_target:
description: Target branch to merge into. Uses the default branch as a fallback.
required: false
force:
description: Force a release even when there are release-blockers
required: false
default: 'false'
blocker_label:
description: Label that blocks releases
required: false
default: 'release-blocker'
publish_repo:
description: Repository for publish issues (owner/repo format)
required: false
git_user_name:
description: Git committer name
required: false
git_user_email:
description: Git committer email
required: false
path:
description: The path that Craft will run inside
required: false
default: '.'
craft_config_from_merge_target:
description: Use the craft config from the merge target branch
required: false
default: 'false'
craft_version:
description: >
Version of Craft to install (tag or "latest").
Defaults to the action ref (e.g., "v2") if not specified.
required: false
default: ''
outputs:
version:
description: The resolved version being released
value: ${{ steps.craft.outputs.version }}
branch:
description: The release branch name
value: ${{ steps.craft.outputs.branch }}
sha:
description: The commit SHA on the release branch
value: ${{ steps.craft.outputs.sha }}
previous_tag:
description: The tag before this release (for diff links)
value: ${{ steps.craft.outputs.previous_tag }}
changelog:
description: The changelog for this release (may be truncated for large repos)
value: ${{ steps.craft.outputs.changelog }}
changelog_file:
description: Path to the full changelog file (available when running in GitHub Actions)
value: ${{ steps.craft.outputs.changelog_file }}
issue_url:
description: The URL of the created publish request issue
value: ${{ steps.request-publish.outputs.issue_url }}
runs:
using: 'composite'
steps:
- id: killswitch
name: Check release blockers
shell: bash
run: |
if [[ '${{ inputs.force }}' != 'true' ]] && gh issue list -l '${{ inputs.blocker_label }}' -s open | grep -q '^[0-9]\+[[:space:]]'; then
echo "::error::Open release-blocking issues found (label: ${{ inputs.blocker_label }}), cancelling release..."
gh api -X POST repos/:owner/:repo/actions/runs/$GITHUB_RUN_ID/cancel
fi
- name: Set git user
shell: bash
env:
GIT_USER_NAME: ${{ inputs.git_user_name || github.actor }}
GIT_USER_EMAIL: ${{ inputs.git_user_email || format('{0}+{1}@users.noreply.github.com', github.actor_id, github.actor) }}
run: |
echo "GIT_COMMITTER_NAME=${GIT_USER_NAME}" >> $GITHUB_ENV
echo "GIT_AUTHOR_NAME=${GIT_USER_NAME}" >> $GITHUB_ENV
echo "EMAIL=${GIT_USER_EMAIL}" >> $GITHUB_ENV
# === Install Craft ===
# For dogfooding (getsentry/craft): try build artifact from build job
# For all repos (or if artifact unavailable): download from release
- name: Download Craft from build artifact
id: craft-artifact
if: github.repository == 'getsentry/craft'
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
with:
name: craft-binary
path: /tmp/craft-artifact
- name: Install Craft from artifact or release
shell: bash
run: |
set -euo pipefail
if [[ -f /tmp/craft-artifact/dist/craft ]]; then
echo "Installing Craft from build artifact..."
sudo install -m 755 /tmp/craft-artifact/dist/craft /usr/local/bin/craft
else
# Download from release (for external repos or if artifact unavailable)
# Use explicit craft_version input if provided, otherwise fall back to github.action_ref
CRAFT_VERSION="${{ inputs.craft_version }}"
if [[ -z "$CRAFT_VERSION" ]]; then
CRAFT_VERSION="${{ github.action_ref }}"
fi
if [[ "$CRAFT_VERSION" == "latest" || -z "$CRAFT_VERSION" ]]; then
echo "Downloading latest Craft release..."
CRAFT_URL=$(curl -fsSL "https://api.github.com/repos/getsentry/craft/releases/latest" \
| jq -r '.assets[] | select(.name == "craft") | .browser_download_url')
else
CRAFT_URL="https://github.com/getsentry/craft/releases/download/${CRAFT_VERSION}/craft"
echo "Downloading Craft ${CRAFT_VERSION} from: ${CRAFT_URL}"
# Fallback to latest if specified version doesn't have a release
if ! curl -sfI "$CRAFT_URL" >/dev/null 2>&1; then
echo "Release not found for version '${CRAFT_VERSION}', falling back to latest..."
CRAFT_URL=$(curl -fsSL "https://api.github.com/repos/getsentry/craft/releases/latest" \
| jq -r '.assets[] | select(.name == "craft") | .browser_download_url')
fi
fi
# Verify we have a valid URL
if [[ -z "$CRAFT_URL" ]]; then
echo "::error::Failed to determine Craft download URL. The GitHub API may have failed or the release asset is missing."
exit 1
fi
echo "Installing Craft from: ${CRAFT_URL}"
sudo curl -fsSL -o /usr/local/bin/craft "$CRAFT_URL"
sudo chmod +x /usr/local/bin/craft
# Verify the binary was downloaded successfully
if [[ ! -s /usr/local/bin/craft ]]; then
echo "::error::Downloaded Craft binary is empty or missing"
exit 1
fi
fi
- name: Craft Prepare
id: craft
shell: bash
env:
CRAFT_LOG_LEVEL: Debug
working-directory: ${{ inputs.path }}
run: |
# Ensure we have origin/HEAD set
git remote set-head origin --auto
# Build command with optional flags
CRAFT_ARGS=""
if [[ '${{ inputs.craft_config_from_merge_target }}' == 'true' && -n '${{ inputs.merge_target }}' ]]; then
CRAFT_ARGS="--config-from ${{ inputs.merge_target }}"
fi
# Version is optional - if not provided, Craft uses versioning.policy from config
VERSION_ARG=""
if [[ -n '${{ inputs.version }}' ]]; then
VERSION_ARG="${{ inputs.version }}"
fi
craft prepare $VERSION_ARG $CRAFT_ARGS
- name: Read Craft Targets
id: craft-targets
shell: bash
working-directory: ${{ inputs.path }}
env:
CRAFT_LOG_LEVEL: Warn
run: |
targets=$(craft targets | jq -r '.[]|" - [ ] \(.)"')
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
echo "targets<<EOF" >> "$GITHUB_OUTPUT"
echo "$targets" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
- name: Request publish
id: request-publish
shell: bash
env:
# NOTE: CHANGELOG is read from a file to avoid E2BIG errors.
# Large changelogs (e.g. sentry's monthly releases) can exceed the ~2 MB
# Linux ARG_MAX limit when passed as an environment variable.
CHANGELOG_FILE: ${{ steps.craft.outputs.changelog_file }}
TARGETS: ${{ steps.craft-targets.outputs.targets }}
RESOLVED_VERSION: ${{ steps.craft.outputs.version }}
RELEASE_BRANCH: ${{ steps.craft.outputs.branch }}
RELEASE_SHA: ${{ steps.craft.outputs.sha }}
RELEASE_PREVIOUS_TAG: ${{ steps.craft.outputs.previous_tag || 'HEAD' }}
SUBDIRECTORY: ${{ inputs.path != '.' && format('/{0}', inputs.path) || '' }}
MERGE_TARGET: ${{ inputs.merge_target || '(default)' }}
PUBLISH_REPO: ${{ inputs.publish_repo || format('{0}/publish', github.repository_owner) }}
run: |
# Resolve "self" to the current repository
if [[ "$PUBLISH_REPO" == "self" ]]; then
PUBLISH_REPO="$GITHUB_REPOSITORY"
fi
if [[ -z "$RESOLVED_VERSION" ]]; then
echo "::error::Craft did not output a version. This is unexpected."
exit 1
fi
# Read changelog from file to avoid E2BIG.
# Produced by craft >= 2.22.0; older versions don't produce the file
# and the publish issue will be created without a changelog section.
CHANGELOG=""
if [[ -n "${CHANGELOG_FILE:-}" && -f "$CHANGELOG_FILE" ]]; then
CHANGELOG=$(cat "$CHANGELOG_FILE")
fi
# GitHub issue bodies are limited to ~65 536 characters. Truncate
# the changelog to stay under that limit with room for the rest of
# the body template (~2 KB of surrounding markdown).
MAX_CHANGELOG_CHARS=60000
if [[ ${#CHANGELOG} -gt $MAX_CHANGELOG_CHARS ]]; then
CHANGELOG="${CHANGELOG:0:$MAX_CHANGELOG_CHARS}"$'\n\n---\n*Changelog truncated for issue body.*'
fi
title="publish: ${GITHUB_REPOSITORY}${SUBDIRECTORY}@${RESOLVED_VERSION}"
# Check if issue already exists by listing all open issues and filtering by exact title match.
# We avoid GitHub search API to bypass indexing delays and query syntax edge cases.
# gh issue list returns issues sorted by creation date (most recent first), so we take
# the first match to handle the theoretical case of duplicate titles.
existing_issue=$(gh -R "$PUBLISH_REPO" issue list --json title,url,number,body | jq -r --arg t "$title" '[.[] | select(.title == $t)] | first // empty')
existing_issue_url=""
existing_issue_number=""
existing_body=""
if [[ -n "$existing_issue" ]]; then
existing_issue_url=$(echo "$existing_issue" | jq -r '.url')
existing_issue_number=$(echo "$existing_issue" | jq -r '.number')
existing_body=$(echo "$existing_issue" | jq -r '.body')
fi
# Extract checked targets from the existing body.
# Targets appear as " - [x] targetName" or " - [X] targetName" in markdown.
# We only look within the "### Targets" section to avoid matching checkboxes in the changelog.
# We store them in an associative array for O(1) lookup when rebuilding targets.
declare -A checked_targets
if [[ -n "$existing_body" ]]; then
# Extract only the Targets section (between "### Targets" and "Checked targets will be skipped")
targets_section=$(echo "$existing_body" | sed -n '/### Targets/,/Checked targets will be skipped/p')
while IFS= read -r target; do
[[ -n "$target" ]] && checked_targets["$target"]=1
done < <(echo "$targets_section" | grep -oE '^\s*-\s*\[[xX]\]\s+\S+' | sed 's/.*\[[xX]\][[:space:]]*//')
fi
# Apply preserved checked states to the new targets list.
# For each target in the new list, check if it was marked as checked in the original.
if [[ ${#checked_targets[@]} -gt 0 ]]; then
new_targets=""
while IFS= read -r line; do
# Extract target name from " - [ ] targetName" format
target_name=$(echo "$line" | sed 's/.*\[ \][[:space:]]*//')
if [[ -n "$target_name" && -n "${checked_targets[$target_name]+x}" ]]; then
# This target was checked in the original, preserve that state
line=$(echo "$line" | sed 's/\[ \]/[x]/')
fi
new_targets+="${line}"$'\n'
done <<< "$TARGETS"
# Remove trailing newline
TARGETS="${new_targets%$'\n'}"
fi
# Build changelog section if available
if [[ -n "$CHANGELOG" ]]; then
CHANGELOG_SECTION="
---
<details open>
<summary>π Changelog</summary>
${CHANGELOG}
</details>"
else
CHANGELOG_SECTION=""
fi
body="Requested by: @${GITHUB_ACTOR}
Merge target: ${MERGE_TARGET}
Quick links:
- [View changes](https://github.com/${GITHUB_REPOSITORY}/compare/${RELEASE_PREVIOUS_TAG}...${RELEASE_BRANCH})
- [View check runs](https://github.com/${GITHUB_REPOSITORY}/commit/${RELEASE_SHA}/checks/)
Assign the **accepted** label to this issue to approve the release.
### Targets
${TARGETS}
Checked targets will be skipped (either already published or user-requested skip). Uncheck to retry a target.
${CHANGELOG_SECTION}"
if [[ -n "$existing_issue_number" ]]; then
# Try to update existing issue with fresh body (preserving checked target states)
# This may fail if the token doesn't have permission to update issues in the publish repo
if gh issue edit "$existing_issue_number" -R "$PUBLISH_REPO" --body "$body" 2>/dev/null; then
echo "::notice::Updated existing publish request: ${existing_issue_url}"
else
echo "::warning::Could not update existing issue (permission denied). Using existing issue as-is."
fi
echo "issue_url=${existing_issue_url}" >> "$GITHUB_OUTPUT"
else
# Create new issue
issue_url=$(gh issue create -R "$PUBLISH_REPO" --title "$title" --body "$body")
echo "::notice::Created publish request: ${issue_url}"
echo "issue_url=${issue_url}" >> "$GITHUB_OUTPUT"
fi