Skip to content

Commit 6c70553

Browse files
authored
Merge pull request #108 from morri-son/enhance-cli-release
Enhance cli release
2 parents 5f132ab + a8f735a commit 6c70553

File tree

2 files changed

+85
-87
lines changed

2 files changed

+85
-87
lines changed

.github/workflows/cli-release.yml

Lines changed: 24 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,18 @@ jobs:
6060
private-key: ${{ secrets.OCMBOT_PRIV_KEY }}
6161

6262
- name: Checkout Repository
63-
# Checkout repository for tagging
6463
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
6564
with:
6665
sparse-checkout: ${{ env.COMPONENT_PATH }}
6766
ref: ${{ github.event.inputs.branch }}
6867
token: ${{ steps.get_token.outputs.token }}
6968

7069
- name: Setup git config
71-
# Set committer for git identity
7270
run: |
7371
git config --global user.name "${{ github.actor }}"
7472
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
7573
76-
- name: Create ${{ needs.prepare.outputs.new_tag }}
77-
# Create and push tag if not existing
74+
- name: Create tag
7875
id: tag
7976
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
8077
env:
@@ -116,8 +113,7 @@ jobs:
116113
permissions:
117114
contents: write
118115
steps:
119-
- name: Decode changelog to file
120-
# Recreate changelog file from base64 string for release body
116+
- name: Decode changelog
121117
env:
122118
CHANGELOG_B64: ${{ needs.prepare.outputs.changelog_b64 }}
123119
run: echo "$CHANGELOG_B64" | base64 --decode > "${{ runner.temp }}/CHANGELOG.md"
@@ -235,32 +231,15 @@ jobs:
235231
permissions:
236232
contents: write
237233
packages: write
238-
outputs:
239-
set_latest: ${{ steps.promote_oci.outputs.set_latest }}
240234
steps:
241-
- name: Validate release preconditions
242-
env:
243-
RC_TAG: ${{ needs.prepare.outputs.new_tag }}
244-
PROMOTION_TAG: ${{ needs.prepare.outputs.promotion_tag }}
245-
run: |
246-
if [ -z "$RC_TAG" ]; then
247-
echo "::error::Missing RC tag from prepare step"
248-
exit 1
249-
fi
250-
if [ -z "$PROMOTION_TAG" ]; then
251-
echo "::error::Missing promotion tag"
252-
exit 1
253-
fi
254-
echo "✅ RC: $RC_TAG → Final: $PROMOTION_TAG"
255-
256235
- name: Generate App Token
257236
id: get_token
258237
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
259238
with:
260239
app-id: ${{ secrets.OCMBOT_APP_ID }}
261240
private-key: ${{ secrets.OCMBOT_PRIV_KEY }}
262241

263-
- name: Checkout Repository
242+
- name: Checkout
264243
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
265244
with:
266245
sparse-checkout: ${{ env.COMPONENT_PATH }}
@@ -273,7 +252,8 @@ jobs:
273252
git config --global user.name "${{ github.actor }}"
274253
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
275254
276-
- name: Create final tag from RC commit
255+
- name: Create final tag
256+
# Points final tag to same commit as RC tag (no rebuild needed)
277257
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
278258
env:
279259
RC_TAG: ${{ needs.prepare.outputs.new_tag }}
@@ -282,29 +262,16 @@ jobs:
282262
github-token: ${{ steps.get_token.outputs.token }}
283263
script: |
284264
const { execFileSync } = require("child_process");
285-
const rcTag = process.env.RC_TAG;
286-
const finalTag = process.env.FINAL_TAG;
287-
288-
if (!rcTag || !finalTag) {
289-
core.setFailed("Missing RC_TAG or FINAL_TAG");
290-
return;
291-
}
292-
293-
try {
294-
execFileSync("git", ["rev-parse", `refs/tags/${finalTag}`], { stdio: "pipe" });
295-
core.setFailed(`Final tag ${finalTag} already exists. Refusing to overwrite immutable tag.`);
296-
return;
265+
const { RC_TAG: rcTag, FINAL_TAG: finalTag } = process.env;
266+
try {
267+
execFileSync("git", ["rev-parse", `refs/tags/${finalTag}`], { stdio: "pipe" });
268+
core.setFailed(`Tag ${finalTag} already exists`);
269+
return;
297270
} catch {}
298-
299271
const rcSha = execFileSync("git", ["rev-parse", `refs/tags/${rcTag}^{commit}`], { stdio: "pipe" }).toString().trim();
300-
if (!rcSha) {
301-
core.setFailed(`Could not resolve commit for RC tag ${rcTag}`);
302-
return;
303-
}
304-
305272
execFileSync("git", ["tag", "-a", finalTag, rcSha, "-m", `Promote ${rcTag} to ${finalTag}`]);
306273
execFileSync("git", ["push", "origin", `refs/tags/${finalTag}`]);
307-
core.info(`✅ Created final tag ${finalTag} from ${rcTag} at ${rcSha}`);
274+
core.info(`✅ Created final tag ${finalTag} from ${rcTag} (${rcSha.substring(0,7)})`);
308275
309276
- name: Setup ORAS
310277
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1
@@ -317,52 +284,19 @@ jobs:
317284
password: ${{ secrets.GITHUB_TOKEN }}
318285

319286
- name: Promote OCI image tags
320-
id: promote_oci
321-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
322287
env:
323288
RC_VERSION: ${{ needs.prepare.outputs.new_version }}
324289
FINAL_VERSION: ${{ needs.prepare.outputs.promotion_version }}
325290
TARGET_REPO: ${{ env.REGISTRY }}/${{ github.repository_owner }}/cli
326-
with:
327-
script: |
328-
const { execFileSync } = require('child_process');
329-
const { RC_VERSION: rc, FINAL_VERSION: final, TARGET_REPO: repo } = process.env;
330-
331-
if (!rc || !final) { core.setFailed('Missing RC_VERSION or FINAL_VERSION'); return; }
332-
333-
// Get highest existing final version from GitHub releases (not OCI)
334-
let highest = '';
335-
try {
336-
const releases = await github.rest.repos.listReleases({
337-
owner: context.repo.owner,
338-
repo: context.repo.repo,
339-
per_page: 100,
340-
});
341-
highest = releases.data
342-
.filter(r => !r.prerelease && r.tag_name.startsWith('cli/v'))
343-
.map(r => r.tag_name.replace('cli/v', ''))
344-
.filter(t => /^\d+\.\d+\.\d+$/.test(t))
345-
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
346-
.pop() || '';
347-
} catch (e) { core.warning(`Could not fetch existing releases: ${e.message}`); }
348-
349-
// Set :latest only if this version is >= the current highest
350-
const setLatest = !highest || final.localeCompare(highest, undefined, { numeric: true }) >= 0;
351-
const tagArgs = setLatest ? [final, "latest"] : [final];
352-
353-
execFileSync("oras", ["tag", `${repo}:${rc}`, ...tagArgs], { stdio: 'inherit' });
354-
core.info(setLatest ? `✅ Tagged :${final} and :latest` : `⚠️ Tagged :${final} (${highest} is higher)`);
355-
356-
// Export setLatest for use in release_final job
357-
core.setOutput('set_latest', setLatest ? 'true' : 'false');
358-
359-
await core.summary.addHeading('OCI Image Promotion').addTable([
360-
[{data: 'Field', header: true}, {data: 'Value', header: true}],
361-
['Source', `${repo}:${rc}`],
362-
['Final', `${repo}:${final}`],
363-
['Highest existing', highest || '(none)'],
364-
['Latest', setLatest ? 'Yes' : `No (${highest} > ${final})`],
365-
]).write();
291+
SET_LATEST: ${{ needs.prepare.outputs.set_latest }}
292+
run: |
293+
if [ "$SET_LATEST" = "true" ]; then
294+
oras tag "${TARGET_REPO}:${RC_VERSION}" "${FINAL_VERSION}" "latest"
295+
echo "✅ Tagged :${FINAL_VERSION} and :latest"
296+
else
297+
oras tag "${TARGET_REPO}:${RC_VERSION}" "${FINAL_VERSION}"
298+
echo "⚠️ Tagged :${FINAL_VERSION} (not setting :latest)"
299+
fi
366300
367301
# --------------------------------------------------------
368302
# 7. RELEASE FINAL: Create GitHub final release
@@ -405,7 +339,8 @@ jobs:
405339
TARGET_REPO: ${{ env.REGISTRY }}/${{ github.repository_owner }}/cli
406340
BINARIES_DIR: ${{ runner.temp }}/binaries
407341
NOTES_FILE: ${{ runner.temp }}/CHANGELOG.md
408-
SET_LATEST: ${{ needs.promote_final.outputs.set_latest }}
342+
SET_LATEST: ${{ needs.prepare.outputs.set_latest }}
343+
HIGHEST_FINAL_VERSION: ${{ needs.prepare.outputs.highest_final_version }}
409344
with:
410345
github-token: ${{ secrets.GITHUB_TOKEN }}
411346
script: |
@@ -467,6 +402,7 @@ jobs:
467402
}
468403
469404
const releaseUrl = created.data.html_url;
405+
const highestFinal = process.env.HIGHEST_FINAL_VERSION || '(none)';
470406
const ociTags = setLatest
471407
? `${targetRepo}:${finalVersion}, ${targetRepo}:latest`
472408
: `${targetRepo}:${finalVersion}`;
@@ -477,6 +413,7 @@ jobs:
477413
[{data: 'Field', header: true}, {data: 'Value', header: true}],
478414
['Final Tag', finalTag],
479415
['Promoted from RC', rcTag],
416+
['Highest Final Version', highestFinal],
480417
['OCI Tags', ociTags],
481418
['GitHub Latest', setLatest ? 'Yes' : 'No (older version)'],
482419
])

.github/workflows/release-candidate-version.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ on:
3030
changelog_b64:
3131
description: "Changelog encoded in base64 for release body"
3232
value: ${{ jobs.prepare.outputs.changelog_b64 }}
33+
set_latest:
34+
description: "Whether to set :latest tag (true if this version >= highest existing final)"
35+
value: ${{ jobs.prepare.outputs.set_latest }}
36+
highest_final_version:
37+
description: "Highest existing final version for this component (for logging)"
38+
value: ${{ jobs.prepare.outputs.highest_final_version }}
3339

3440
jobs:
3541
prepare:
@@ -44,6 +50,8 @@ jobs:
4450
promotion_tag: ${{ steps.compute.outputs.promotion_tag }}
4551
promotion_version: ${{ steps.compute.outputs.promotion_version }}
4652
changelog_b64: ${{ steps.summarize.outputs.changelog_b64 }}
53+
set_latest: ${{ steps.latest.outputs.set_latest }}
54+
highest_final_version: ${{ steps.latest.outputs.highest_final_version }}
4755
steps:
4856
# Checkout the repository and target branch
4957
- name: Checkout Repository
@@ -112,3 +120,56 @@ jobs:
112120
const encoded = Buffer.from(log).toString("base64");
113121
core.setOutput("changelog_b64", encoded);
114122
await core.summary.addRaw(log).write();
123+
124+
- name: Determine if release should be latest
125+
id: latest
126+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
127+
env:
128+
COMPONENT_PATH: ${{ inputs.component_path }}
129+
PROMOTION_VERSION: ${{ steps.compute.outputs.promotion_version }}
130+
with:
131+
github-token: ${{ secrets.GITHUB_TOKEN }}
132+
script: |
133+
const { COMPONENT_PATH: componentPath, PROMOTION_VERSION: promotionVersion } = process.env;
134+
135+
// Build tag prefix based on component path
136+
const tagPrefix = `${componentPath}/v`;
137+
138+
// Find highest existing final version from GitHub releases
139+
let highestFinal = '';
140+
try {
141+
const releases = await github.rest.repos.listReleases({
142+
owner: context.repo.owner,
143+
repo: context.repo.repo,
144+
per_page: 100,
145+
});
146+
highestFinal = releases.data
147+
.filter(r => !r.prerelease && r.tag_name.startsWith(tagPrefix))
148+
.map(r => r.tag_name.replace(tagPrefix, ''))
149+
.filter(v => /^\d+\.\d+\.\d+$/.test(v))
150+
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
151+
.pop() || '';
152+
} catch (e) {
153+
core.warning(`Could not fetch existing releases: ${e.message}`);
154+
}
155+
156+
// Set :latest only if this version is >= the current highest
157+
const setLatest = !highestFinal || promotionVersion.localeCompare(highestFinal, undefined, { numeric: true }) >= 0;
158+
159+
core.setOutput('set_latest', setLatest ? 'true' : 'false');
160+
core.setOutput('highest_final_version', highestFinal || '(none)');
161+
162+
core.info(setLatest
163+
? `✅ Will set :latest (${promotionVersion} >= ${highestFinal || 'none'})`
164+
: `⚠️ Will NOT set :latest (${promotionVersion} < ${highestFinal})`);
165+
166+
// Add to summary
167+
await core.summary
168+
.addHeading('Latest Tag Decision', 3)
169+
.addTable([
170+
[{ data: 'Field', header: true }, { data: 'Value', header: true }],
171+
['Final Version', promotionVersion],
172+
['Highest Final Version', highestFinal || '(none)'],
173+
['Will Set Latest', setLatest ? '✅ Yes' : '⚠️ No'],
174+
])
175+
.write();

0 commit comments

Comments
 (0)