mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
245 lines
8.9 KiB
JavaScript
245 lines
8.9 KiB
JavaScript
import { execFileSync } from 'node:child_process'
|
|
import {
|
|
chmodSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
renameSync,
|
|
rmSync,
|
|
writeFileSync
|
|
} from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { delimiter, dirname, join } from 'node:path'
|
|
import { runInNewContext } from 'node:vm'
|
|
import { expect, it } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
|
|
const workflow = parse(readFileSync('.github/workflows/zcode-credential-contract.yml', 'utf8'))
|
|
const accountWorkflow = parse(readFileSync('.github/workflows/zcode-account-contract.yml', 'utf8'))
|
|
const job = workflow.jobs['credential-files']
|
|
const scope = job.steps.find((step) => step.id === 'scope')
|
|
const account = job.steps.find(
|
|
(step) => step.name === 'Verify read-only credentials and account changes'
|
|
)
|
|
const credentials = job.steps.find(
|
|
(step) => step.name === 'Verify rollback and native Windows permissions'
|
|
)
|
|
const accountFiles = [
|
|
'src/main/zcode/zcode-v2-plan-credentials.test.ts',
|
|
'src/main/zcode/zcode-v2-personal-overlay-raw-json.test.ts',
|
|
'src/main/rate-limits/service-zcode-usage.test.ts',
|
|
'src/shared/node-bounded-file-reader-sync.test.ts'
|
|
]
|
|
const credentialFiles = [
|
|
'src/main/zcode/zcode-plan-api-key-store.test.ts',
|
|
'src/main/zcode/zcode-plan-api-key-store-plaintext-rollback.test.ts',
|
|
'src/main/zcode/zcode-plan-api-key-store-publication-race.test.ts',
|
|
'src/main/zcode/zcode-plan-api-key-store-native-windows.test.ts',
|
|
'src/shared/secure-file.test.ts'
|
|
]
|
|
|
|
it('shares one bootstrap across the original modules and operating systems', () => {
|
|
expect(job.strategy).toEqual({
|
|
'fail-fast': false,
|
|
matrix: { os: ['ubuntu-22.04', 'macos-15', 'windows-2022'] }
|
|
})
|
|
expect(job.name).toBe('ZCode credentials (${{ matrix.os }})')
|
|
expect(account.run.split(/\s+/).slice(2)).toEqual(accountFiles)
|
|
expect(credentials.run.split(/\s+/).slice(2)).toEqual(credentialFiles)
|
|
expect(
|
|
job.steps.filter((step) => step.uses === './.github/actions/install-node-dependencies')
|
|
).toHaveLength(1)
|
|
expect(job.steps.find((step) => step.id === 'dependencies').with['native-runtime']).toBe('node')
|
|
expect(job.env.ORCA_BACKGROUND_LAUNCH).toBe('1')
|
|
expect(job['timeout-minutes']).toBe(20)
|
|
expect(job.steps[0].with['fetch-depth']).toBe(2)
|
|
expect(workflow.on.pull_request.paths).toEqual([
|
|
'src/main/zcode/**',
|
|
'src/shared/node-bounded-file-reader*',
|
|
'src/main/rate-limits/**',
|
|
'src/shared/secure-file*',
|
|
'src/shared/secure-path-windows-acl*',
|
|
'src/shared/windows-security-descriptor*',
|
|
'.github/workflows/zcode-account-contract.yml',
|
|
'.github/workflows/zcode-credential-contract.yml',
|
|
'config/scripts/zcode-credential-workflow.test.mjs'
|
|
])
|
|
expect(accountWorkflow.on).toEqual({ workflow_dispatch: null })
|
|
expect(accountWorkflow.jobs['account-contract']).toEqual({
|
|
uses: './.github/workflows/zcode-credential-contract.yml',
|
|
with: { contract: 'account' }
|
|
})
|
|
expect(workflow.on.workflow_dispatch).toBeNull()
|
|
expect(workflow.on.workflow_call.inputs.contract.default).toBe('credentials')
|
|
})
|
|
|
|
it('runs the other selected contract after a sibling fails and retains prerequisite failures', () => {
|
|
for (const step of [account, credentials]) {
|
|
expect(step['continue-on-error']).toBeUndefined()
|
|
for (const [cancelled, installed, bun, selected, expected] of [
|
|
[false, 'success', 'success', 'true', true],
|
|
[false, 'failure', 'success', 'true', false],
|
|
[false, 'success', 'failure', 'true', false],
|
|
[false, 'success', 'success', 'false', false],
|
|
[true, 'success', 'success', 'true', false]
|
|
]) {
|
|
expect(
|
|
runInNewContext(step.if.slice(3, -2), {
|
|
cancelled: () => cancelled,
|
|
steps: {
|
|
scope: { outputs: { account: selected, credentials: selected } },
|
|
dependencies: { outcome: installed },
|
|
bun: { outcome: bun }
|
|
}
|
|
})
|
|
).toBe(expected)
|
|
}
|
|
}
|
|
})
|
|
|
|
function executeScope(directory, overrides = {}) {
|
|
const output = join(directory, 'github-output')
|
|
writeFileSync(output, '')
|
|
execFileSync('bash', ['-e', '-o', 'pipefail', '-c', scope.run], {
|
|
cwd: directory,
|
|
env: {
|
|
...process.env,
|
|
ORCA_BACKGROUND_LAUNCH: '1',
|
|
EVENT_NAME: 'pull_request',
|
|
MANUAL_ACCOUNT: 'false',
|
|
MANUAL_CREDENTIALS: 'true',
|
|
GITHUB_OUTPUT: output,
|
|
...overrides
|
|
}
|
|
})
|
|
return Object.fromEntries(
|
|
readFileSync(output, 'utf8')
|
|
.trim()
|
|
.split('\n')
|
|
.map((line) => line.split('='))
|
|
)
|
|
}
|
|
|
|
// The workflow owns Bash execution on Windows; this contract executes the same source on Unix.
|
|
it.skipIf(process.platform === 'win32')(
|
|
'selects the original path groups, including deletions and renames',
|
|
() => {
|
|
const directory = mkdtempSync(join(tmpdir(), 'orca-zcode-workflow-'))
|
|
const git = (args) => execFileSync('git', args, { cwd: directory, stdio: 'pipe' })
|
|
const write = (file, value) => {
|
|
mkdirSync(dirname(join(directory, file)), { recursive: true })
|
|
writeFileSync(join(directory, file), value)
|
|
}
|
|
const commit = () => {
|
|
git(['add', '-A'])
|
|
git([
|
|
'-c',
|
|
'user.name=Fixture',
|
|
'-c',
|
|
'user.email=fixture@example.test',
|
|
'commit',
|
|
'--allow-empty',
|
|
'-qm',
|
|
'fixture'
|
|
])
|
|
}
|
|
const cases = [
|
|
['src/main/zcode/credentials.ts', true, true],
|
|
['src/main/zcode/nested/credentials.ts', true, true],
|
|
['src/shared/node-bounded-file-reader-sync.test.ts', true, false],
|
|
['src/main/rate-limits/service-zcode-usage.ts', true, false],
|
|
['src/shared/secure-file.ts', false, true],
|
|
['src/shared/secure-path-windows-acl.ts', false, true],
|
|
['src/shared/windows-security-descriptor.ts', false, true],
|
|
['.github/workflows/zcode-account-contract.yml', true, false],
|
|
['.github/workflows/zcode-credential-contract.yml', true, true],
|
|
['config/scripts/zcode-credential-workflow.test.mjs', true, true],
|
|
['docs/unrelated.md', false, false]
|
|
]
|
|
try {
|
|
git(['init', '-q'])
|
|
write('README.md', 'fixture')
|
|
commit()
|
|
expect(executeScope(directory)).toEqual({ account: 'true', credentials: 'true' })
|
|
for (const [file, runsAccount, runsCredentials] of cases) {
|
|
write(file, 'first')
|
|
commit()
|
|
expect(executeScope(directory), file).toEqual({
|
|
account: String(runsAccount),
|
|
credentials: String(runsCredentials)
|
|
})
|
|
rmSync(join(directory, file))
|
|
commit()
|
|
expect(executeScope(directory), `deleted ${file}`).toEqual({
|
|
account: String(runsAccount),
|
|
credentials: String(runsCredentials)
|
|
})
|
|
}
|
|
write('src/main/zcode/credentials.ts', 'renamed')
|
|
commit()
|
|
renameSync(join(directory, 'src/main/zcode/credentials.ts'), join(directory, 'README.md'))
|
|
commit()
|
|
expect(executeScope(directory)).toEqual({ account: 'true', credentials: 'true' })
|
|
write('src/main/rate-limits/service.ts', 'mixed')
|
|
write('src/shared/secure-file.ts', 'mixed')
|
|
commit()
|
|
expect(executeScope(directory)).toEqual({ account: 'true', credentials: 'true' })
|
|
commit()
|
|
expect(executeScope(directory)).toEqual({ account: 'true', credentials: 'true' })
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
}
|
|
)
|
|
|
|
it.skipIf(process.platform === 'win32')(
|
|
'keeps both manual entry points and missing diff evidence',
|
|
() => {
|
|
const directory = mkdtempSync(join(tmpdir(), 'orca-zcode-manual-'))
|
|
try {
|
|
expect(executeScope(directory)).toEqual({ account: 'true', credentials: 'true' })
|
|
expect(executeScope(directory, { EVENT_NAME: 'workflow_dispatch' })).toEqual({
|
|
account: 'false',
|
|
credentials: 'true'
|
|
})
|
|
expect(
|
|
executeScope(directory, {
|
|
EVENT_NAME: 'workflow_dispatch',
|
|
MANUAL_ACCOUNT: 'true',
|
|
MANUAL_CREDENTIALS: 'false'
|
|
})
|
|
).toEqual({ account: 'true', credentials: 'false' })
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
}
|
|
)
|
|
|
|
it.skipIf(process.platform === 'win32')(
|
|
'runs both groups when the full diff fails even if scoped diffs would report no changes',
|
|
() => {
|
|
const directory = mkdtempSync(join(tmpdir(), 'orca-zcode-diff-failure-'))
|
|
try {
|
|
const gitShim = join(directory, 'git')
|
|
writeFileSync(
|
|
gitShim,
|
|
'#!/bin/sh\nif [ "$1" = rev-parse ]; then exit 0; fi\nif [ "$#" = 5 ]; then exit "$FULL_DIFF_STATUS"; fi\nexit 0\n'
|
|
)
|
|
chmodSync(gitShim, 0o755)
|
|
const env = { PATH: `${directory}${delimiter}${process.env.PATH}` }
|
|
expect(executeScope(directory, { ...env, FULL_DIFF_STATUS: '1' })).toEqual({
|
|
account: 'false',
|
|
credentials: 'false'
|
|
})
|
|
for (const status of ['2', '128']) {
|
|
expect(executeScope(directory, { ...env, FULL_DIFF_STATUS: status })).toEqual({
|
|
account: 'true',
|
|
credentials: 'true'
|
|
})
|
|
}
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
}
|
|
)
|