Skip to content

Commit eb5301a

Browse files
Upgrade to action-gh-release@v3
This changes the mocks a bit, but most importantly requires upgrading to a newer Act (0.2.81+) to support node24 actions. This in turn changes the way test results are reported, requiring adaptation to test validation. Issue: ZENKO-5225
1 parent 3727d6c commit eb5301a

4 files changed

Lines changed: 65 additions & 29 deletions

File tree

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
core.exportVariable('RELEASE_NOTES', releaseNotes.body);
122122
123123
- name: Create Release
124-
uses: softprops/action-gh-release@v2.5.0
124+
uses: softprops/action-gh-release@v3
125125
env:
126126
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
127127
with:

tests/workflows/create-component-deployments.spec.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ async function getCommitHash(repo: string = "zenko") {
4646
return stdout.trim();
4747
}
4848

49+
// act >=0.2.81 appends a timing suffix to success/failure lines ("Main foo [40ms]"), which
50+
// act-js's OutputParser splits into a named "Run" entry followed by an unnamed status entry.
51+
// So the real status of result[i] lives on result[i + 1]; unnamed entries have status set.
52+
function findStep(result: { name: string; status: number }[], nameFragment: string) {
53+
const idx = result.findIndex(r => r.name?.includes(nameFragment));
54+
if (idx < 0) {
55+
return undefined;
56+
}
57+
return { name: result[idx].name, status: result[idx + 1]?.status };
58+
}
59+
4960
// Common deployment parameters expected for all components
5061
const commonDeploymentParams = {
5162
environment: "zenko/development/2.11",
@@ -173,11 +184,11 @@ describe("create-component-deployments action", () => {
173184
bind: true,
174185
});
175186

176-
const parseStep = result.find(r => r.name?.includes("Parse component repos"));
187+
const parseStep = findStep(result, "Parse component repos");
177188
expect(parseStep).toBeDefined();
178189
expect(parseStep?.status).toBe(0);
179190

180-
const deployStep = result.find(r => r.name?.includes("Create or update deployments"));
191+
const deployStep = findStep(result, "Create or update deployments");
181192
expect(deployStep).toBeDefined();
182193
expect(deployStep?.status).toBe(0);
183194
});
@@ -222,15 +233,15 @@ describe("create-component-deployments action", () => {
222233
bind: true,
223234
});
224235

225-
const filterStep = result.find(r => r.name?.includes("Filter to changed dependencies"));
236+
const filterStep = findStep(result, "Filter to changed dependencies");
226237
expect(filterStep).toBeDefined();
227238
expect(filterStep?.status).toBe(0);
228239

229-
const parseStep = result.find(r => r.name?.includes("Parse component repos"));
240+
const parseStep = findStep(result, "Parse component repos");
230241
expect(parseStep).toBeDefined();
231242
expect(parseStep?.status).toBe(0);
232243

233-
const deployStep = result.find(r => r.name?.includes("Create or update deployments"));
244+
const deployStep = findStep(result, "Create or update deployments");
234245
expect(deployStep).toBeDefined();
235246
expect(deployStep?.status).toBe(0);
236247
});
@@ -301,19 +312,19 @@ describe("create-component-deployments action", () => {
301312
bind: true,
302313
});
303314

304-
const filterStep = result.find(r => r.name?.includes("Filter to changed dependencies"));
315+
const filterStep = findStep(result, "Filter to changed dependencies");
305316
expect(filterStep).toBeDefined();
306317
expect(filterStep?.status).toBe(0);
307318

308-
const parseStep = result.find(r => r.name?.includes("Parse component repos"));
319+
const parseStep = findStep(result, "Parse component repos");
309320
expect(parseStep).toBeDefined();
310321
expect(parseStep?.status).toBe(0);
311322

312323
// No deployments should be created — token and deploy steps should be skipped
313-
const tokenStep = result.find(r => r.name?.includes("Generate scoped token"));
324+
const tokenStep = findStep(result, "Generate scoped token");
314325
expect(tokenStep).toBeUndefined();
315326

316-
const deployStep = result.find(r => r.name?.includes("Create or update deployments"));
327+
const deployStep = findStep(result, "Create or update deployments");
317328
expect(deployStep).toBeUndefined();
318329
});
319330
});

tests/workflows/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.0.0",
44
"description": "Tests for GHA workflows",
55
"scripts": {
6-
"postinstall": "yarn --cwd node_modules/@kie/act-js node scripts/postinstall.js 0.2.75",
6+
"postinstall": "yarn --cwd node_modules/@kie/act-js node scripts/postinstall.js 0.2.84",
77
"test": "jest"
88
},
99
"license": "Apache-2.0",

tests/workflows/release.spec.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -248,19 +248,43 @@ test.each([
248248
}
249249
}),
250250

251+
// Mock release lookup by tag used by action-gh-release@v3.
252+
// IMPORTANT: register these BEFORE listReleases — moctokit translates the
253+
// unparameterized `listReleases()` path to the regex `/repos/.+/.+/releases`
254+
// (unanchored), which also matches getReleaseByTag URLs and would hijack them.
255+
// v3 calls getReleaseByTag twice: once upfront (findTagFromReleases → 404)
256+
// and once after creation (canonicalizeCreatedRelease).
257+
moctokit.rest.repos
258+
.getReleaseByTag({ tag, owner: 'scality', repo: 'Zenko' })
259+
.reply({ status: 404, data: {} })
260+
.reply({
261+
status: 200, data: {
262+
id: 123,
263+
draft: true,
264+
assets: [],
265+
name: `Release ${tag}`,
266+
prerelease: tag === '2.3.7-rc.1',
267+
tag_name: tag,
268+
target_commitish: await getCommitHash(),
269+
upload_url: 'http://uploads.github.com/repos/scality/Zenko/releases/456/assets{?name,label}',
270+
html_url: 'http://github.com/repos/scality/Zenko/releases/456',
271+
},
272+
}),
273+
251274
// Mock release notes generation
252275
moctokit.rest.repos
253-
.listReleases()
276+
.listReleases({ owner: 'scality', repo: 'Zenko' } as any)
254277
// First call from release notes generation, to get the previous release
255278
.reply({ status: 200, data: [{ tag_name: '2.3.6', id: 122 }] })
256279
// Second call from release notes generation, to get the previous release
257280
.reply({ status: 200, data: [{ tag_name: '2.3.6', id: 122 }] })
258-
// Third call made by action-gh-release@v2.5.2+ (after release creation) to handle
259-
// race condition when release is created by multiple jobs in parallel...
281+
// Third call made by action-gh-release@v3's canonicalizeCreatedRelease
282+
// (recentReleasesByTag scans allReleases to reconcile duplicate drafts).
260283
.reply({
261284
status: 200, data: [{ tag_name: '2.3.6', id: 122 }, {
262285
id: 123,
263286
draft: true,
287+
assets: [],
264288
name: `Release ${tag}`,
265289
prerelease: tag === '2.3.7-rc.1',
266290
tag_name: tag,
@@ -278,11 +302,6 @@ test.each([
278302
target_commitish: await getCommitHash(),
279303
})
280304
.reply({ status: 200, data: { body: 'something changed' } }),
281-
282-
// Mock release creation: check existing release and create a new one
283-
moctokit.rest.repos
284-
.getReleaseByTag()
285-
.reply({ status: 404, data: {} }),
286305
moctokit.rest.repos
287306
.createRelease({
288307
owner: 'scality',
@@ -293,7 +312,7 @@ test.each([
293312
name: `Release ${tag}`,
294313
body: 'something changed',
295314
prerelease: tag === '2.3.7-rc.1',
296-
draft: true,
315+
draft: tag !== '2.3.7-rc.1', // v3 only set drafts for non-prereleases
297316
})
298317
.reply({ status: 201, data: {
299318
id: 123,
@@ -365,7 +384,7 @@ test.each([
365384
}],
366385
'release': [{
367386
// Need to explicitely pass token, the GITHUB_TOKEN does not seem to be set
368-
uses: 'softprops/action-gh-release@v2.5.0',
387+
uses: 'softprops/action-gh-release@v3',
369388
mockWith: {
370389
with: {
371390
token: "my-token",
@@ -400,19 +419,25 @@ test.each([
400419
}
401420
});
402421

403-
var lastResult = result[result.length - 1];
404-
var postSteps = [];
422+
// act >=0.2.81 appends a timing suffix to success/failure lines ("Main foo [40ms]"), which
423+
// act-js's OutputParser splits into a named "Run" entry followed by an unnamed status entry.
424+
// So the real status of result[i] lives on result[i + 1]; unnamed entries have status set.
425+
var lastResult = result.length - 2;
426+
var postSteps: number[] = [];
405427

406428
// action-artifacts keep executing Post step, need to skip it...
407429
for (let i = result.length - 1; i >= 0; i--) {
408-
if (result[i].name.startsWith('Main ')) {
409-
lastResult = result[i];
430+
if (!result[i].name) {
431+
postSteps.push(result[i].status);
432+
} else if (result[i].name.startsWith('Main ')) {
433+
lastResult = i;
410434
break;
411435
}
412-
postSteps.push(result[i]);
413436
}
414437

415-
expect(lastResult.name).toStrictEqual('Main ' + stepName);
416-
expect(lastResult.status).toStrictEqual(status.value());
417-
postSteps.forEach(r => expect(r.status).toStrictEqual(Pass.value()));
438+
postSteps.pop(); // last pushed entry is the matched step's own status, not a post-step
439+
440+
expect(result[lastResult].name.startsWith('Main ' + stepName)).toBe(true);
441+
expect(result[lastResult + 1].status).toStrictEqual(status.value());
442+
postSteps.forEach(s => expect(s).toStrictEqual(Pass.value()));
418443
})

0 commit comments

Comments
 (0)