mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 00:02:37 +00:00
ci: cache dependency downloads and shallow development checkouts
This commit is contained in:
@@ -10,6 +10,10 @@ inputs:
|
||||
description: Node.js version override; defaults to the version declared in package.json.
|
||||
required: false
|
||||
default: ''
|
||||
cache-dependency-path:
|
||||
description: Lockfiles for the pnpm download store; include mobile/pnpm-lock.yaml only when the job installs mobile dependencies.
|
||||
required: false
|
||||
default: pnpm-lock.yaml
|
||||
persist-native-cache:
|
||||
description: Save restored native modules at job end. Set false when a later step overwrites the same path with a different ABI.
|
||||
required: false
|
||||
@@ -39,9 +43,7 @@ runs:
|
||||
with:
|
||||
install: false
|
||||
|
||||
# Why both lockfiles: setup-node keys the pnpm store on the root lockfile alone, so
|
||||
# jobs that also install mobile restored a store with none of the React Native tree
|
||||
# in it and re-downloaded the lot on every run.
|
||||
# Desktop-only jobs should not miss their download cache when mobile dependencies change.
|
||||
- name: Setup Node.js
|
||||
id: default-node
|
||||
if: inputs.node-version == ''
|
||||
@@ -49,9 +51,7 @@ runs:
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: |
|
||||
pnpm-lock.yaml
|
||||
mobile/pnpm-lock.yaml
|
||||
cache-dependency-path: ${{ inputs.cache-dependency-path }}
|
||||
|
||||
- name: Setup requested Node.js
|
||||
id: requested-node
|
||||
@@ -60,9 +60,7 @@ runs:
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: pnpm
|
||||
cache-dependency-path: |
|
||||
pnpm-lock.yaml
|
||||
mobile/pnpm-lock.yaml
|
||||
cache-dependency-path: ${{ inputs.cache-dependency-path }}
|
||||
|
||||
- name: Validate native runtime
|
||||
shell: bash
|
||||
|
||||
@@ -160,16 +160,14 @@ jobs:
|
||||
|
||||
- name: Checkout the requested ref
|
||||
uses: actions/checkout@v6
|
||||
env:
|
||||
# Full-history checkout must also preserve case-twin branch and tag names.
|
||||
GIT_DEFAULT_REF_FORMAT: reftable
|
||||
with:
|
||||
# Why an input at all rather than just github.ref: the whole point is to
|
||||
# build code that has not landed, and the workflow definition itself
|
||||
# always comes from the dispatch ref — naming the branch here instead
|
||||
# applies main's current copy of this file to an arbitrary branch.
|
||||
ref: ${{ steps.vetted.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
# Version helpers only read HEAD; published versions come from the release API.
|
||||
fetch-depth: 1
|
||||
# This job only reads stablyai/orca and never pushes; every write goes
|
||||
# to the adhoc repo through a minted App token passed by env. Not
|
||||
# persisting the checkout credential shrinks the blast radius if a build
|
||||
|
||||
@@ -90,7 +90,8 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
# Version helpers only read HEAD; published versions come from the release API.
|
||||
fetch-depth: 1
|
||||
# Why: this job only reads stablyai/orca and never pushes; every write
|
||||
# goes to the daily repo through a minted App token passed by env.
|
||||
# Not persisting the checkout credential shrinks the blast radius if a
|
||||
|
||||
@@ -137,7 +137,8 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.head_sha }}
|
||||
fetch-depth: 0
|
||||
# Version helpers only read HEAD; published versions come from the release API.
|
||||
fetch-depth: 1
|
||||
# Why: this job only reads stablyai/orca and never pushes; every write
|
||||
# goes to the hourly repo through a minted App token passed by env.
|
||||
# Not persisting the checkout credential shrinks the blast radius if a
|
||||
|
||||
@@ -38,16 +38,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/setup@v2
|
||||
with:
|
||||
install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: mobile/pnpm-lock.yaml
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -67,6 +69,22 @@ jobs:
|
||||
- name: Expo prebuild
|
||||
run: npx expo prebuild --platform android --no-install
|
||||
|
||||
# setup-gradle also extracts compiler caches independently of its include paths.
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
cache-disabled: true
|
||||
|
||||
- name: Cache Gradle dependency downloads
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/modules-2
|
||||
~/.gradle/wrapper/dists
|
||||
key: gradle-downloads-v1-${{ runner.os }}-${{ runner.arch }}-jdk17-${{ hashFiles('mobile/android/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('mobile/pnpm-lock.yaml', 'mobile/pnpm-workspace.yaml', 'mobile/patches/**', 'mobile/android/**/*.gradle', 'mobile/android/**/*.gradle.kts', 'mobile/android/**/gradle.properties', 'mobile/android/gradle/wrapper/gradle-wrapper.properties', 'mobile/android/gradle/libs.versions.toml') }}
|
||||
restore-keys: |
|
||||
gradle-downloads-v1-${{ runner.os }}-${{ runner.arch }}-jdk17-${{ hashFiles('mobile/android/gradle/wrapper/gradle-wrapper.properties') }}-
|
||||
|
||||
- name: Build Android release APK
|
||||
run: cd android && ./gradlew assembleRelease
|
||||
|
||||
|
||||
@@ -58,16 +58,18 @@ jobs:
|
||||
# Xcode 26.x ships the Swift 6.x toolchain Expo SDK 55 requires.
|
||||
xcode-version: '26.5'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/setup@v2
|
||||
with:
|
||||
install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: mobile/pnpm-lock.yaml
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -98,6 +100,15 @@ jobs:
|
||||
ORCA_IOS_APS_ENVIRONMENT: production
|
||||
run: npx expo prebuild --platform ios --no-install
|
||||
|
||||
# Cache downloaded pod sources only; prebuild and pod install still run on every build.
|
||||
- name: Cache CocoaPods downloads
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/Library/Caches/CocoaPods
|
||||
key: cocoapods-downloads-v1-${{ runner.os }}-${{ runner.arch }}-xcode26.5-${{ hashFiles('mobile/Gemfile.lock') }}-${{ hashFiles('mobile/pnpm-lock.yaml', 'mobile/pnpm-workspace.yaml', 'mobile/patches/**', 'mobile/Gemfile.lock', 'mobile/ios/Podfile', 'mobile/ios/Podfile.properties.json', 'mobile/ios/Podfile.lock') }}
|
||||
restore-keys: |
|
||||
cocoapods-downloads-v1-${{ runner.os }}-${{ runner.arch }}-xcode26.5-${{ hashFiles('mobile/Gemfile.lock') }}-
|
||||
|
||||
- name: Install CocoaPods
|
||||
run: npx pod-install ios
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- uses: ./.github/actions/install-node-dependencies
|
||||
with:
|
||||
cache-dependency-path: |
|
||||
pnpm-lock.yaml
|
||||
mobile/pnpm-lock.yaml
|
||||
|
||||
# bundler-cache installs mobile/Gemfile.lock, so this job is also what
|
||||
# proves the pinned fastlane the release workflow depends on still
|
||||
|
||||
@@ -126,6 +126,9 @@ jobs:
|
||||
- uses: ./.github/actions/install-node-dependencies
|
||||
with:
|
||||
native-runtime: node
|
||||
cache-dependency-path: |
|
||||
pnpm-lock.yaml
|
||||
mobile/pnpm-lock.yaml
|
||||
|
||||
- name: Lint
|
||||
run: pnpm exec oxlint --format github
|
||||
|
||||
@@ -1251,12 +1251,13 @@ jobs:
|
||||
# Cache the Electron binary + electron-builder tool downloads
|
||||
# (winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job.
|
||||
- name: Cache electron-builder downloads
|
||||
uses: actions/cache@v5
|
||||
id: electron-builder-downloads
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ${{ matrix.eb_cache_path }}
|
||||
key: electron-builder-${{ matrix.platform }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
key: electron-builder-downloads-v2-${{ runner.os }}-${{ runner.arch }}-${{ matrix.platform }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-builder-${{ matrix.platform }}-
|
||||
electron-builder-downloads-v2-${{ runner.os }}-${{ runner.arch }}-${{ matrix.platform }}-
|
||||
|
||||
# Why: pnpm install triggers electron's postinstall, which downloads the
|
||||
# Electron binary from GitHub release assets. GitHub's download CDN
|
||||
@@ -1454,6 +1455,14 @@ jobs:
|
||||
if: matrix.platform == 'win' && github.run_attempt == 1
|
||||
uses: ./.github/actions/install-signpath-module
|
||||
|
||||
# Save before SignPath replaces cached elevate.exe with this run's signed bytes.
|
||||
- name: Save electron-builder downloads before signing
|
||||
if: steps.electron-builder-downloads.outputs.cache-hit != 'true' && (matrix.platform != 'win' || github.run_attempt == 1)
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ${{ matrix.eb_cache_path }}
|
||||
key: ${{ steps.electron-builder-downloads.outputs.cache-primary-key }}
|
||||
|
||||
# ── Windows inner-binary signing (issue #7785) ─────────────────────
|
||||
# Why: SignPath cannot deep-sign inside NSIS installers, so inner PE
|
||||
# files (Orca.exe, node-pty *.node, DLLs) are signed via a separate zip
|
||||
@@ -1713,10 +1722,7 @@ jobs:
|
||||
# clobbered the SignPath signature in v1.4.129-rc.4. There is no supported
|
||||
# way to disable just the copy, so we overwrite the cache's copy with our
|
||||
# signed one (identical bytes plus signature) so the clobber becomes a
|
||||
# no-op. Known quirk: the cache persists across releases via actions/cache,
|
||||
# so later runs may see elevate.exe as already signed and skip staging it —
|
||||
# that is fine (the signature is timestamped) and the evidence gate checks
|
||||
# elevate.exe in the shipped installer unconditionally.
|
||||
# no-op. The download cache is saved before signing, so this swap stays local.
|
||||
#
|
||||
# The cache lookup lives in a script because the inline path this step used
|
||||
# (`<cache>\nsis`) matches no app-builder-lib layout, and `SilentlyContinue`
|
||||
@@ -1733,9 +1739,7 @@ jobs:
|
||||
Write-Host '::warning::No elevate.exe in win-unpacked resources; nothing to protect from the rebuild clobber.'
|
||||
exit 0
|
||||
}
|
||||
# Why this guard stays: windows-signing-rehearsal.yml shares the
|
||||
# electron-builder-win-<lockfile hash> cache key with this workflow, so a
|
||||
# test-certificate elevate.exe must never be staged into a release cache.
|
||||
# Only this run's production SignPath signature may enter the installer rebuild.
|
||||
$signature = Get-AuthenticodeSignature -FilePath $signed
|
||||
$subject = if ($null -eq $signature.SignerCertificate) { '<none>' } else { $signature.SignerCertificate.Subject }
|
||||
if ($signature.Status -ne 'Valid' -or $subject -notlike '*CN=SignPath Foundation*') {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { parse } from 'yaml'
|
||||
|
||||
const read = (path) => parse(readFileSync(path, 'utf8'))
|
||||
const workflow = (name) => read(`.github/workflows/${name}.yml`)
|
||||
const action = read('.github/actions/install-node-dependencies/action.yml')
|
||||
|
||||
describe('CI dependency download caches', () => {
|
||||
it('scopes desktop stores to the root lockfile and lets mixed installs opt in', () => {
|
||||
expect(action.inputs['cache-dependency-path'].default).toBe('pnpm-lock.yaml')
|
||||
for (const step of action.runs.steps.filter((step) => step.uses === 'actions/setup-node@v6')) {
|
||||
expect(step.with.cache).toBe('pnpm')
|
||||
expect(step.with['cache-dependency-path']).toBe('${{ inputs.cache-dependency-path }}')
|
||||
}
|
||||
const install = action.runs.steps.find((step) => step.name === 'Install dependencies')
|
||||
expect(install.if).toBeUndefined()
|
||||
expect(install.run).toContain('pnpm install --frozen-lockfile --ignore-scripts')
|
||||
expect(install.run).toContain(
|
||||
'diff --exit-code -- package.json pnpm-lock.yaml pnpm-workspace.yaml'
|
||||
)
|
||||
const mobile = workflow('mobile').jobs.verify.steps.find((step) =>
|
||||
step.uses?.includes('install-node-dependencies')
|
||||
)
|
||||
expect(mobile.with['cache-dependency-path'].trim().split('\n')).toEqual([
|
||||
'pnpm-lock.yaml',
|
||||
'mobile/pnpm-lock.yaml'
|
||||
])
|
||||
})
|
||||
|
||||
it.each(['mobile-android-release', 'mobile-ios-release'])(
|
||||
'%s caches its own lockfile and always performs a frozen install and native generation',
|
||||
(name) => {
|
||||
const steps = Object.values(workflow(name).jobs)[0].steps
|
||||
const node = steps.findIndex((step) => step.uses === 'actions/setup-node@v6')
|
||||
expect(steps.findIndex((step) => step.uses === 'pnpm/setup@v2')).toBeLessThan(node)
|
||||
expect(steps[node].with.cache).toBe('pnpm')
|
||||
expect(steps[node].with['cache-dependency-path']).toBe('mobile/pnpm-lock.yaml')
|
||||
const install = steps.find((step) => step.name === 'Install dependencies')
|
||||
expect(install.if).toBeUndefined()
|
||||
expect(install.run).toBe('pnpm install --frozen-lockfile')
|
||||
const prebuild = steps.findIndex((step) => step.name === 'Expo prebuild')
|
||||
expect(steps[prebuild].if).toBeUndefined()
|
||||
expect(prebuild).toBeLessThan(steps.findIndex((step) => step.uses === 'actions/cache@v5'))
|
||||
}
|
||||
)
|
||||
|
||||
it('caches only Gradle downloads, with generated native and toolchain inputs', () => {
|
||||
const steps = workflow('mobile-android-release').jobs['android-build'].steps
|
||||
const setup = steps.find((step) => step.uses === 'gradle/actions/setup-gradle@v4')
|
||||
expect(setup.with['cache-disabled']).toBe(true)
|
||||
const cache = steps.find((step) => step.name === 'Cache Gradle dependency downloads')
|
||||
expect(cache.with.path.trim().split('\n')).toEqual([
|
||||
'~/.gradle/caches/modules-2',
|
||||
'~/.gradle/wrapper/dists'
|
||||
])
|
||||
for (const input of [
|
||||
'runner.os',
|
||||
'runner.arch',
|
||||
'jdk17',
|
||||
'mobile/pnpm-lock.yaml',
|
||||
'mobile/patches/**',
|
||||
'mobile/android/**/*.gradle',
|
||||
'gradle-wrapper.properties'
|
||||
]) {
|
||||
expect(cache.with.key).toContain(input)
|
||||
}
|
||||
expect(cache.with['restore-keys'].trim()).toBe(
|
||||
"gradle-downloads-v1-${{ runner.os }}-${{ runner.arch }}-jdk17-${{ hashFiles('mobile/android/gradle/wrapper/gradle-wrapper.properties') }}-"
|
||||
)
|
||||
const build = steps.find((step) => step.name === 'Build Android release APK')
|
||||
expect(build.if).toBeUndefined()
|
||||
expect(build.run).toBe('cd android && ./gradlew assembleRelease')
|
||||
})
|
||||
|
||||
it('caches CocoaPods downloads without Pods, signing state, or build products', () => {
|
||||
const steps = workflow('mobile-ios-release').jobs['ios-build'].steps
|
||||
const cache = steps.find((step) => step.name === 'Cache CocoaPods downloads')
|
||||
expect(cache.with.path).toBe('~/Library/Caches/CocoaPods')
|
||||
for (const input of [
|
||||
'runner.os',
|
||||
'runner.arch',
|
||||
'xcode26.5',
|
||||
'mobile/Gemfile.lock',
|
||||
'mobile/pnpm-lock.yaml',
|
||||
'mobile/patches/**',
|
||||
'mobile/ios/Podfile',
|
||||
'Podfile.properties.json'
|
||||
]) {
|
||||
expect(cache.with.key).toContain(input)
|
||||
}
|
||||
expect(cache.with['restore-keys'].trim()).toBe(
|
||||
"cocoapods-downloads-v1-${{ runner.os }}-${{ runner.arch }}-xcode26.5-${{ hashFiles('mobile/Gemfile.lock') }}-"
|
||||
)
|
||||
const install = steps.find((step) => step.name === 'Install CocoaPods')
|
||||
expect(install.if).toBeUndefined()
|
||||
expect(install.run).toBe('npx pod-install ios')
|
||||
expect(steps.indexOf(cache)).toBeLessThan(steps.indexOf(install))
|
||||
})
|
||||
|
||||
it('saves release tool downloads before signing can mutate them', () => {
|
||||
const steps = Object.values(workflow('release-cut').jobs).find((job) =>
|
||||
job.steps?.some((step) => step.id === 'electron-builder-downloads')
|
||||
).steps
|
||||
const restore = steps.find((step) => step.id === 'electron-builder-downloads')
|
||||
const save = steps.find(
|
||||
(step) => step.name === 'Save electron-builder downloads before signing'
|
||||
)
|
||||
expect(restore.uses).toBe('actions/cache/restore@v5')
|
||||
expect(restore.with.key).toContain(
|
||||
'electron-builder-downloads-v2-${{ runner.os }}-${{ runner.arch }}'
|
||||
)
|
||||
expect(restore.with['restore-keys']).toContain('electron-builder-downloads-v2-')
|
||||
expect(save.uses).toBe('actions/cache/save@v5')
|
||||
expect(save.with.path).toBe(restore.with.path)
|
||||
expect(save.with.key).toBe('${{ steps.electron-builder-downloads.outputs.cache-primary-key }}')
|
||||
expect(steps.indexOf(save)).toBeGreaterThan(
|
||||
steps.findIndex((step) => step.name === 'Build Windows release artifacts')
|
||||
)
|
||||
expect(steps.indexOf(save)).toBeLessThan(
|
||||
steps.findIndex((step) => step.id === 'sign-elevate-cache')
|
||||
)
|
||||
expect(save.if).toContain("matrix.platform != 'win' || github.run_attempt == 1")
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,10 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { parse } from 'yaml'
|
||||
import { runProcessSync } from '../../src/shared/child-process/run-process'
|
||||
|
||||
const projectDir = resolve(import.meta.dirname, '../..')
|
||||
|
||||
@@ -15,13 +18,74 @@ const REF_MIRRORS = [
|
||||
]
|
||||
|
||||
describe('ref-mirroring vet steps', () => {
|
||||
it('keeps the full-history adhoc checkout on the same case-safe backend', () => {
|
||||
it.each(['daily', 'hourly', 'adhoc'])('%s builds only need the current commit', (channel) => {
|
||||
const job = readWorkflow(`.github/workflows/${channel}-mac-build.yml`).jobs[
|
||||
`build-${channel}-mac`
|
||||
]
|
||||
const checkout = job.steps.find((step) => step.uses === 'actions/checkout@v6')
|
||||
expect(checkout.with['fetch-depth']).toBe(1)
|
||||
expect(job.steps.some((step) => step.run?.includes('gh release list'))).toBe(true)
|
||||
expect(
|
||||
job.steps.some((step) => step.run?.includes('ORCA_PUBLISHED_VERSIONS="$published"'))
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('retains release-cut history for version reservation and retry ancestry', () => {
|
||||
const checkout = readWorkflow('.github/workflows/release-cut.yml').jobs.cut.steps.find(
|
||||
(step) => step.uses === 'actions/checkout@v6'
|
||||
)
|
||||
expect(checkout.with['fetch-depth']).toBe(0)
|
||||
})
|
||||
|
||||
it('resolves identical dev identities in full and depth-one checkouts without local tags', () => {
|
||||
const directory = mkdtempSync(join(tmpdir(), 'orca-checkout-identity-'))
|
||||
const source = join(directory, 'source')
|
||||
const shallow = join(directory, 'shallow')
|
||||
const run = (program, args, cwd) => {
|
||||
const result = runProcessSync({ program, args, cwd })
|
||||
expect(result.code, result.stderr).toBe(0)
|
||||
return result.stdout.trim()
|
||||
}
|
||||
const git = (args, cwd = directory) => run('git', args, cwd)
|
||||
try {
|
||||
git(['init', source])
|
||||
git(['config', 'user.name', 'CI test'], source)
|
||||
git(['config', 'user.email', 'ci@example.invalid'], source)
|
||||
writeFileSync(join(source, 'package.json'), JSON.stringify({ version: '1.4.165-rc.0' }))
|
||||
git(['add', 'package.json'], source)
|
||||
git(['-c', 'commit.gpgsign=false', 'commit', '-m', 'initial'], source)
|
||||
git(['tag', 'v1.4.167'], source)
|
||||
git(['-c', 'commit.gpgsign=false', 'commit', '--allow-empty', '-m', 'head'], source)
|
||||
git(['clone', '--depth=1', '--no-tags', pathToFileURL(source).href, shallow])
|
||||
expect(git(['rev-list', '--count', 'HEAD'], shallow)).toBe('1')
|
||||
expect(git(['tag', '--list'], shallow)).toBe('')
|
||||
const script = `
|
||||
const result = [];
|
||||
for (const [channel, exported] of [['daily', 'Daily'], ['hourly', 'Hourly'], ['adhoc', 'Adhoc']]) {
|
||||
const module = await import(${JSON.stringify(pathToFileURL(join(projectDir, 'config/scripts/')).href)} + channel + '-build-version.mjs');
|
||||
const date = new Date('2026-09-12T00:00:00Z');
|
||||
result.push(channel === 'adhoc'
|
||||
? module.getAdhocBuildIdentity(date, 'branch', ['v1.4.167'])
|
||||
: module['get' + exported + 'BuildIdentity'](date, { publishedVersions: ['v1.4.167'], releaseNames: [] }));
|
||||
}
|
||||
process.stdout.write(JSON.stringify(result));
|
||||
`
|
||||
const identities = (cwd) => run(process.execPath, ['--input-type=module', '-e', script], cwd)
|
||||
expect(identities(shallow)).toBe(identities(source))
|
||||
expect(
|
||||
JSON.parse(identities(shallow)).every((identity) => identity.version.startsWith('1.4.168-'))
|
||||
).toBe(true)
|
||||
} finally {
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('checks out only the vetted commit without remirroring refs', () => {
|
||||
const steps = readWorkflow('.github/workflows/adhoc-mac-build.yml').jobs['build-adhoc-mac']
|
||||
.steps
|
||||
const checkout = steps.find((step) => step.name === 'Checkout the requested ref')
|
||||
expect(checkout.env.GIT_DEFAULT_REF_FORMAT).toBe('reftable')
|
||||
expect(checkout.with.ref).toBe('${{ steps.vetted.outputs.sha }}')
|
||||
expect(checkout.with['fetch-depth']).toBe(0)
|
||||
expect(checkout.with['fetch-depth']).toBe(1)
|
||||
expect(checkout.with['persist-credentials']).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('release ref trust with case-twin names', () => {
|
||||
expect(result.stdout).toContain('Refusing to build PR ref')
|
||||
})
|
||||
|
||||
it('preserves both case variants in the subsequent full-history checkout', async () => {
|
||||
it('checks out the vetted SHA shallowly without mirroring case-twin refs again', async () => {
|
||||
const checkout = join(directory, 'checkout')
|
||||
const env = { ...identity, ...macCheckout.env }
|
||||
await git(['init', checkout], env)
|
||||
@@ -105,21 +105,15 @@ describe('release ref trust with case-twin names', () => {
|
||||
checkout,
|
||||
'fetch',
|
||||
'--no-tags',
|
||||
`--depth=${macCheckout.with['fetch-depth']}`,
|
||||
repository,
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
'+refs/tags/*:refs/tags/*'
|
||||
upper
|
||||
],
|
||||
env
|
||||
)
|
||||
await git(['-C', checkout, 'checkout', '--detach', upper], env)
|
||||
for (const [ref, sha] of [
|
||||
['refs/remotes/origin/Fix', upper],
|
||||
['refs/remotes/origin/fix', lower],
|
||||
['refs/tags/Release', upper],
|
||||
['refs/tags/release', lower]
|
||||
]) {
|
||||
expect(await git(['-C', checkout, 'rev-parse', `${ref}^{commit}`], env)).toBe(sha)
|
||||
}
|
||||
expect(await git(['-C', checkout, 'rev-parse', 'HEAD'], env)).toBe(upper)
|
||||
expect(await git(['-C', checkout, 'rev-list', '--count', 'HEAD'], env)).toBe('1')
|
||||
expect(await git(['-C', checkout, 'for-each-ref', '--format=%(refname)'], env)).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user