Skip to content

Commit f126fef

Browse files
committed
copy over final state from enhance-cli-release branch
On-behalf-of: Gerald Morrison (SAP) <gerald.morrison@sap.com> Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
1 parent b11b237 commit f126fef

File tree

4 files changed

+119
-52
lines changed

4 files changed

+119
-52
lines changed

.github/scripts/prepare-registry-constructor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-check
22
import fs from 'fs';
33
import yaml from 'js-yaml';
4-
import {computeNextVersions} from "./compute-rc-version.js";
4+
import {computeNextVersions} from "./release-versioning.js";
55
import {execSync} from "child_process";
66
import {dirname} from "path";
77

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,44 @@ export function parseVersion(tag) {
216216
return version.split(".").map(Number);
217217
}
218218

219+
// --------------------------
220+
// Latest release determination
221+
// --------------------------
222+
223+
/** GitHub Actions entrypoint for determining if release should be latest */
224+
export async function determineLatestRelease({ core, github, context }) {
225+
const { COMPONENT_PATH: componentPath, PROMOTION_VERSION: promotionVersion } = process.env;
226+
if (!componentPath || !promotionVersion) return core.setFailed("Missing COMPONENT_PATH or PROMOTION_VERSION");
227+
228+
const tagPrefix = `${componentPath}/v`;
229+
let releases = [];
230+
try {
231+
releases = (await github.rest.repos.listReleases({ owner: context.repo.owner, repo: context.repo.repo, per_page: 100 })).data;
232+
} catch (e) {
233+
core.setFailed(`Could not fetch releases: ${e.message}`);
234+
return;
235+
}
236+
237+
const highestFinal = extractHighestFinalVersion(releases, tagPrefix);
238+
const setLatest = shouldSetLatest(promotionVersion, highestFinal);
239+
240+
core.setOutput('set_latest', setLatest ? 'true' : 'false');
241+
core.setOutput('highest_final_version', highestFinal || '(none)');
242+
core.info(setLatest ? `✅ Will set :latest (${promotionVersion} >= ${highestFinal || 'none'})` : `⚠️ Will NOT set :latest (${promotionVersion} < ${highestFinal})`);
243+
244+
await core.summary.addRaw('---').addEOL().addHeading('Latest Tag Decision', 2)
245+
.addTable([[{ data: 'Field', header: true }, { data: 'Value', header: true }], ['Final Version', promotionVersion], ['Highest Final Version', highestFinal || '(none)'], ['Will Set Latest', setLatest ? '✅ Yes' : '⚠️ No']]).write();
246+
}
247+
248+
/** Extract highest final (non-prerelease) version from releases */
249+
export function extractHighestFinalVersion(releases, tagPrefix) {
250+
const versions = releases.filter(r => !r.prerelease && r.tag_name.startsWith(tagPrefix))
251+
.map(r => r.tag_name.replace(tagPrefix, '')).filter(v => /^\d+\.\d+\.\d+$/.test(v));
252+
if (!versions.length) return '';
253+
return versions.sort((a, b) => isStableNewer(`v${a}`, `v${b}`) ? 1 : -1).pop();
254+
}
255+
256+
/** Determine if promotion version should be tagged as latest */
257+
export function shouldSetLatest(promotionVersion, highestFinal) {
258+
return !highestFinal || !isStableNewer(`v${highestFinal}`, `v${promotionVersion}`);
259+
}

.github/scripts/compute-rc-version.test.js renamed to .github/scripts/release-versioning.test.js

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import assert from "assert";
2-
import { computeNextVersions, isStableNewer, parseBranch, parseVersion } from "./compute-rc-version.js";
2+
import {
3+
computeNextVersions,
4+
isStableNewer,
5+
parseBranch,
6+
parseVersion,
7+
extractHighestFinalVersion,
8+
shouldSetLatest,
9+
} from "./release-versioning.js";
310

411
// ----------------------------------------------------------
512
// parseVersion tests
@@ -119,6 +126,68 @@ assert.ok(
119126
"Should return false if no stable tag"
120127
);
121128

122-
// Note: sortVersions was removed - using git's native --sort=version:refname instead
129+
// ----------------------------------------------------------
130+
// extractHighestFinalVersion tests
131+
// ----------------------------------------------------------
132+
const mockReleases = [
133+
{ prerelease: false, tag_name: "cli/v0.1.0" },
134+
{ prerelease: true, tag_name: "cli/v0.1.1-rc.1" },
135+
{ prerelease: false, tag_name: "cli/v0.1.2" },
136+
{ prerelease: false, tag_name: "cli/v0.2.0" },
137+
{ prerelease: false, tag_name: "other/v1.0.0" },
138+
{ prerelease: true, tag_name: "cli/v0.3.0-rc.1" },
139+
];
140+
141+
assert.strictEqual(
142+
extractHighestFinalVersion(mockReleases, "cli/v"),
143+
"0.2.0",
144+
"Should return highest non-prerelease version for prefix"
145+
);
146+
147+
assert.strictEqual(
148+
extractHighestFinalVersion(mockReleases, "other/v"),
149+
"1.0.0",
150+
"Should filter by tag prefix"
151+
);
152+
153+
assert.strictEqual(
154+
extractHighestFinalVersion([], "cli/v"),
155+
"",
156+
"Should return empty string for no releases"
157+
);
158+
159+
assert.strictEqual(
160+
extractHighestFinalVersion([{ prerelease: true, tag_name: "cli/v0.1.0-rc.1" }], "cli/v"),
161+
"",
162+
"Should return empty string if only prereleases exist"
163+
);
164+
165+
// ----------------------------------------------------------
166+
// shouldSetLatest tests
167+
// ----------------------------------------------------------
168+
assert.ok(
169+
shouldSetLatest("0.2.0", ""),
170+
"Should return true if no existing final version"
171+
);
172+
173+
assert.ok(
174+
shouldSetLatest("0.2.0", "0.1.0"),
175+
"Should return true if promotion > highest"
176+
);
177+
178+
assert.ok(
179+
shouldSetLatest("0.2.0", "0.2.0"),
180+
"Should return true if promotion == highest"
181+
);
182+
183+
assert.ok(
184+
!shouldSetLatest("0.1.0", "0.2.0"),
185+
"Should return false if promotion < highest"
186+
);
187+
188+
assert.ok(
189+
shouldSetLatest("0.10.0", "0.9.0"),
190+
"Should handle numeric comparison correctly (0.10 > 0.9)"
191+
);
123192

124-
console.log("✅ All tests passed.");
193+
console.log("✅ All tests passed.");

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

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
new_version: ${{ steps.compute.outputs.new_version }}
4646
base_version: ${{ steps.compute.outputs.base_version }}
4747
promotion_tag: ${{ steps.compute.outputs.promotion_tag }}
48-
changelog_artifact: changelog-${{ steps.compute.outputs.new_tag }}
48+
changelog_artifact: changelog-${{ inputs.component_path }}-${{ steps.compute.outputs.new_version }}
4949
set_latest: ${{ steps.latest.outputs.set_latest }}
5050
highest_final_version: ${{ steps.latest.outputs.highest_final_version }}
5151
steps:
@@ -70,7 +70,7 @@ jobs:
7070
with:
7171
github-token: ${{ secrets.GITHUB_TOKEN }}
7272
script: |
73-
const script = await import('${{ github.workspace }}/.github/scripts/compute-rc-version.js');
73+
const script = await import('${{ github.workspace }}/.github/scripts/release-versioning.js');
7474
await script.default({ core, github, context });
7575
7676
- name: Generate changelog with git-cliff
@@ -119,7 +119,7 @@ jobs:
119119
- name: Upload changelog artifact
120120
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
121121
with:
122-
name: changelog-${{ steps.compute.outputs.new_tag }}
122+
name: changelog-${{ inputs.component_path }}-${{ steps.compute.outputs.new_version }}
123123
path: ${{ runner.temp }}/CHANGELOG.md
124124
retention-days: 90
125125
if-no-files-found: error
@@ -133,48 +133,5 @@ jobs:
133133
with:
134134
github-token: ${{ secrets.GITHUB_TOKEN }}
135135
script: |
136-
const { COMPONENT_PATH: componentPath, PROMOTION_VERSION: promotionVersion } = process.env;
137-
138-
// Build tag prefix based on component path
139-
const tagPrefix = `${componentPath}/v`;
140-
141-
// Find highest existing final version from GitHub releases
142-
let highestFinal = '';
143-
try {
144-
const releases = await github.rest.repos.listReleases({
145-
owner: context.repo.owner,
146-
repo: context.repo.repo,
147-
per_page: 100,
148-
});
149-
highestFinal = releases.data
150-
.filter(r => !r.prerelease && r.tag_name.startsWith(tagPrefix))
151-
.map(r => r.tag_name.replace(tagPrefix, ''))
152-
.filter(v => /^\d+\.\d+\.\d+$/.test(v))
153-
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
154-
.pop() || '';
155-
} catch (e) {
156-
core.warning(`Could not fetch existing releases: ${e.message}`);
157-
}
158-
159-
// Set :latest only if this version is >= the current highest
160-
const setLatest = !highestFinal || promotionVersion.localeCompare(highestFinal, undefined, { numeric: true }) >= 0;
161-
162-
core.setOutput('set_latest', setLatest ? 'true' : 'false');
163-
core.setOutput('highest_final_version', highestFinal || '(none)');
164-
165-
core.info(setLatest
166-
? `✅ Will set :latest (${promotionVersion} >= ${highestFinal || 'none'})`
167-
: `⚠️ Will NOT set :latest (${promotionVersion} < ${highestFinal})`);
168-
169-
// Add to summary with separator
170-
await core.summary
171-
.addRaw('---')
172-
.addEOL()
173-
.addHeading('Latest Tag Decision', 2)
174-
.addTable([
175-
[{ data: 'Field', header: true }, { data: 'Value', header: true }],
176-
['Final Version', promotionVersion],
177-
['Highest Final Version', highestFinal || '(none)'],
178-
['Will Set Latest', setLatest ? '✅ Yes' : '⚠️ No'],
179-
])
180-
.write();
136+
const script = await import('${{ github.workspace }}/.github/scripts/release-versioning.js');
137+
await script.determineLatestRelease({ core, github, context });

0 commit comments

Comments
 (0)