Skip to content

Commit 576b898

Browse files
authored
Merge pull request #1175 from cipherstash/fix/go-release-guest-mode
fix(ci): commit the Go release's guests 0644 in the build job too
2 parents 92be6c0 + 8311bcd commit 576b898

2 files changed

Lines changed: 93 additions & 2 deletions

File tree

‎.github/workflows/release-golang.yml‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,11 @@ jobs:
205205
git checkout --detach
206206
# The guests are gitignored on main; -f adds them to this commit only.
207207
read -ra guests <<< "$GUESTS"
208-
git add -f "${guests[@]}"
208+
# --chmod=-x: the committed mode is set in git, not read from the
209+
# file. Cargo writes the guests executable and `publish` writes them
210+
# 0644, so a mode taken from disk made this job's tree differ from
211+
# the one `publish` builds, and it refused the release.
212+
git add -f --chmod=-x "${guests[@]}"
209213
git commit -q -m "release(golang): v$VERSION with its WASI guests"
210214
tree=$(git rev-parse 'HEAD^{tree}')
211215
@@ -298,7 +302,9 @@ jobs:
298302
git config user.name "github-actions[bot]"
299303
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
300304
git checkout --detach
301-
git add -f "${guests[@]}"
305+
# The mode is set in git, as the build job sets it, so the two trees
306+
# agree whatever mode either job left the files in.
307+
git add -f --chmod=-x "${guests[@]}"
302308
git commit -m "release(golang): v$VERSION with its WASI guests" \
303309
-m "Built from $GITHUB_SHA by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
304310
tree=$(git rev-parse 'HEAD^{tree}')

‎scripts/__tests__/go-release-artifact.test.mjs‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { execFileSync, spawnSync } from 'node:child_process'
22
import { createHash } from 'node:crypto'
33
import {
4+
chmodSync,
45
existsSync,
56
mkdirSync,
67
mkdtempSync,
@@ -238,3 +239,87 @@ describe.skipIf(!BASH_HAS_MAPFILE)(
238239
})
239240
},
240241
)
242+
243+
/**
244+
* `build` reports the tree it tested and `publish` refuses any other, so the
245+
* two must stage the guests identically. Cargo writes them executable and
246+
* `publish` writes them 0644 from the artifact, which made the trees differ
247+
* in mode alone: the first release refused itself. Both now set the mode in
248+
* git (`--chmod=-x`), so it no longer depends on the files.
249+
*/
250+
const STAGE_STEP = WORKFLOW.jobs.build.steps.find((step) => step.id === 'stage')
251+
const STAGE_UNTIL = 'fetched=$(mktemp -d)'
252+
253+
/**
254+
* Run the build job's staging, up to the tree it reports, on guests left
255+
* executable as cargo leaves them, then return to `main`. Returns the tree.
256+
*/
257+
function runBuildStaging() {
258+
const script = String(STAGE_STEP.run)
259+
const cut = script.indexOf(STAGE_UNTIL)
260+
expect(
261+
cut,
262+
`the stage step no longer contains ${STAGE_UNTIL}`,
263+
).toBeGreaterThan(0)
264+
for (const guest of GUESTS) {
265+
const built = join(repo, guest)
266+
mkdirSync(dirname(built), { recursive: true })
267+
writeFileSync(built, `guest ${guest}\n`, { mode: 0o755 })
268+
}
269+
const result = spawnSync(
270+
'bash',
271+
['-c', `${script.slice(0, cut)}\necho "$tree"`],
272+
{
273+
cwd: repo,
274+
encoding: 'utf8',
275+
env: {
276+
PATH: process.env.PATH,
277+
HOME: repo,
278+
GIT_CONFIG_NOSYSTEM: '1',
279+
GUESTS: WORKFLOW.env.GUESTS,
280+
VERSION: '0.1.0-alpha.1',
281+
},
282+
},
283+
)
284+
expect(result.status, result.stderr).toBe(0)
285+
// Forced: the guests on disk are still 0755 while the commit holds them
286+
// 0644, which git reads as a local change.
287+
git(repo, 'checkout', '-q', '-f', 'main')
288+
return result.stdout.trim().split('\n').at(-1)
289+
}
290+
291+
describe('release-golang.yml build: Stage the release commit', () => {
292+
it('commits the guests non-executable, whatever mode cargo left them in', () => {
293+
expect(STAGE_STEP).toBeDefined()
294+
const tree = runBuildStaging()
295+
for (const guest of GUESTS) {
296+
expect(git(repo, 'ls-tree', tree, guest).split(/\s+/)[0], guest).toBe(
297+
'100644',
298+
)
299+
}
300+
})
301+
})
302+
303+
describe.skipIf(!BASH_HAS_MAPFILE)(
304+
'release-golang.yml build and publish build the same tree',
305+
() => {
306+
it.skipIf(!GNU_COREUTILS)(
307+
'publish accepts the tree build reports, from an executable artifact',
308+
() => {
309+
const tree = runBuildStaging()
310+
// The artifact executable too: the two jobs agree through git, not
311+
// through what either one wrote to disk.
312+
writeManifest(
313+
GUESTS.map((guest) => {
314+
const line = stage(guest, `guest ${guest}\n`)
315+
chmodSync(join(repo, 'release-out', guest), 0o755)
316+
return line
317+
}),
318+
)
319+
const result = runCommitStep(tree)
320+
expect(result.stderr + result.stdout).not.toContain('::error::')
321+
expect(result.status).toBe(0)
322+
},
323+
)
324+
},
325+
)

0 commit comments

Comments
 (0)