name: Checkout description: 'Checkout into trusted / untrusted / pinned folders consistently.' inputs: merged-as-untrusted-at: description: "Whether and which SHA to checkout for the merge commit in the ./nixpkgs/untrusted folder." target-as-trusted-at: description: "Whether and which SHA to checkout for the target commit in the ./nixpkgs/trusted folder." untrusted-pin-bump: description: "Commit that bumps ci/pinned.json; when set, ./nixpkgs/untrusted and ./nixpkgs/untrusted-pinned are derived from this commit." runs: using: composite steps: - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: MERGED_SHA: ${{ inputs.merged-as-untrusted-at }} TARGET_SHA: ${{ inputs.target-as-trusted-at }} PIN_BUMP_SHA: ${{ inputs.untrusted-pin-bump }} with: retries: 3 script: | const { rm, writeFile } = require('node:fs/promises') const { spawn } = require('node:child_process') const { join } = require('node:path') async function run(cmd, ...args) { return new Promise((resolve, reject) => { const proc = spawn(cmd, args, { stdio: 'inherit' }) proc.on('close', (code) => { if (code === 0) resolve() else reject(code) }) }) } // These are set automatically by the spare checkout for .github/actions. // Undo them, otherwise git fetch below will not do anything. await run('git', 'config', 'unset', 'remote.origin.promisor') await run('git', 'config', 'unset', 'remote.origin.partialclonefilter') // Getting the pinned SHA via API allows us to do one single fetch call for all commits. // Otherwise we would have to fetch merged/target first, read pinned, fetch again. // A single fetch call comes with a lot less overhead. The fetch takes essentially the // same time no matter whether its 1, 2 or 3 commits at once. async function getPinnedSha(ref) { if (!ref) return undefined const { content, encoding } = (await github.rest.repos.getContent({ ...context.repo, path: 'ci/pinned.json', ref, })).data const pinned = JSON.parse(Buffer.from(content, encoding).toString()) return pinned.pins.nixpkgs.revision } // Getting the pin-bump diff via the API avoids issues with `git fetch` // thin-packs not having enough base objects to be applied locally. // Returns a unified diff suitable for `git apply`. async function getPinBumpDiff(ref) { const { data } = await github.rest.repos.getCommit({ mediaType: { format: 'diff' }, ...context.repo, ref, }) return data } const pin_bump_sha = process.env.PIN_BUMP_SHA const commits = [ { sha: process.env.MERGED_SHA, path: 'untrusted', }, { sha: await getPinnedSha(pin_bump_sha || process.env.MERGED_SHA), path: 'untrusted-pinned' }, { sha: process.env.TARGET_SHA, path: 'trusted', }, { sha: await getPinnedSha(process.env.TARGET_SHA), path: 'trusted-pinned' } ].filter(({ sha }) => Boolean(sha)) console.log('Checking out the following commits:', commits) // Fetching all commits at once is much faster than doing multiple checkouts. // This would fail without --refetch, because the we had a partial clone before, but changed it above. await run('git', 'fetch', '--depth=1', '--refetch', 'origin', ...(commits.map(({ sha }) => sha))) // On Linux, checking out onto tmpfs takes 1s and is faster by at least 10x. // Currently, on Darwin we can only allocate 3.5GB, which isn't enough. // See https://github.com/NixOS/nixpkgs/pull/506437 await run('mkdir', 'nixpkgs') if (process.env.RUNNER_OS === 'Linux') { await run('sudo', 'mount', '-t', 'tmpfs', 'tmpfs', 'nixpkgs') } // Git worktree setup can race when multiple worktrees are created and // initialized at the same time against one repository. See #511286. // Keep the setup sequential so shared repo config updates cannot contend. for (const { sha, path } of commits) { await run('git', 'worktree', 'add', join('nixpkgs', path), sha, '--no-checkout') await run('git', '-C', join('nixpkgs', path), 'sparse-checkout', 'disable') await run('git', '-C', join('nixpkgs', path), 'checkout', '--progress') } // Apply pin bump to untrusted worktree if (pin_bump_sha) { console.log('Fetching ci/pinned.json bump commit:', pin_bump_sha) await writeFile('pin-bump.patch', await getPinBumpDiff(pin_bump_sha)) console.log('Applying untrusted ci/pinned.json bump to ./nixpkgs/untrusted') try { await run('git', '-C', join('nixpkgs', 'untrusted'), 'apply', '--3way', join('..', '..', 'pin-bump.patch')) } catch { core.setFailed([ `Failed to apply ci/pinned.json bump commit ${pin_bump_sha}.`, `This commit does not apply cleanly onto the untrusted base ${process.env.MERGED_SHA}.`, `Please rebase the PR or ensure the pin bump is standalone.` ].join(' ')) return } finally { await rm('pin-bump.patch') } } console.log('final disk usage:') await run('df', '-h')