Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
259 lines
8.2 KiB
TypeScript
259 lines
8.2 KiB
TypeScript
import { readFileSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
|
|
const workflow = readFileSync(
|
|
join(process.cwd(), '.github', 'workflows', 'packages.yml'),
|
|
'utf8'
|
|
)
|
|
const parsed = parse(workflow) as {
|
|
jobs: Record<
|
|
string,
|
|
{
|
|
needs?: string | string[]
|
|
environment?: { name?: string }
|
|
strategy?: {
|
|
matrix?: {
|
|
include?: Array<Record<string, unknown>>
|
|
}
|
|
}
|
|
steps?: Array<{
|
|
name?: string
|
|
run?: string
|
|
uses?: string
|
|
with?: Record<string, unknown>
|
|
}>
|
|
}
|
|
>
|
|
}
|
|
|
|
function serializedJob(name: string): string {
|
|
return JSON.stringify(parsed.jobs[name])
|
|
}
|
|
|
|
function jobScripts(name: string): string {
|
|
return (
|
|
parsed.jobs[name]?.steps
|
|
?.map((step) => step.run ?? '')
|
|
.join('\n') ?? ''
|
|
)
|
|
}
|
|
|
|
function jobNeeds(name: string): string[] {
|
|
const needs = parsed.jobs[name]?.needs
|
|
return needs === undefined
|
|
? []
|
|
: Array.isArray(needs)
|
|
? needs
|
|
: [needs]
|
|
}
|
|
|
|
describe('desktop packages workflow', () => {
|
|
it('prepares a sandbox and virtual display before running Electron tests', () => {
|
|
const steps = parsed.jobs.validate?.steps ?? []
|
|
const installIndex = steps.findIndex(step => step.run === 'npm ci')
|
|
const prepareIndex = steps.findIndex(step => step.name === 'Prepare sandboxed Electron tests')
|
|
const validatorsIndex = steps.findIndex(step => step.name === 'Run validators')
|
|
expect(installIndex).toBeGreaterThanOrEqual(0)
|
|
expect(prepareIndex).toBeGreaterThan(installIndex)
|
|
expect(validatorsIndex).toBeGreaterThan(prepareIndex)
|
|
expect(steps[prepareIndex]?.run).toContain('sudo apt-get install --no-install-recommends --yes xvfb xauth')
|
|
expect(steps[prepareIndex]?.run).toContain('if [[ ! -f "$sandbox" ]]')
|
|
expect(steps[prepareIndex]?.run).toContain('force_no_cache=true node node_modules/electron/install.js')
|
|
expect(steps[prepareIndex]?.run).toContain('test -f "$sandbox"')
|
|
expect(steps[prepareIndex]?.run).toContain('sudo chown root:root "$sandbox"')
|
|
expect(steps[prepareIndex]?.run).toContain('sudo chmod 4755 "$sandbox"')
|
|
expect(steps[validatorsIndex]?.run).toContain('xvfb-run --auto-servernum npm test')
|
|
expect(steps[validatorsIndex]?.run).toContain('npm run typecheck')
|
|
expect(steps[validatorsIndex]?.run).toContain('npm run lint')
|
|
expect(jobScripts('validate')).not.toContain('--no-sandbox')
|
|
})
|
|
|
|
it('builds only DMG for both signed and unsigned macOS jobs', () => {
|
|
const steps = parsed.jobs.package?.steps?.filter(
|
|
(step) => step.name?.endsWith('macOS release packages') &&
|
|
!step.name.includes('non-macOS')
|
|
)
|
|
expect(steps).toHaveLength(2)
|
|
for (const step of steps ?? []) {
|
|
expect(step.run).toContain('--format dmg')
|
|
expect(step.run).not.toContain('--format dmg,zip')
|
|
}
|
|
})
|
|
|
|
it('uses valid YAML, pinned action majors, and only desktop release jobs', () => {
|
|
expect(() => parse(workflow)).not.toThrow()
|
|
expect(Object.keys(parsed.jobs)).toEqual([
|
|
'validate',
|
|
'authorize-release',
|
|
'package',
|
|
'release'
|
|
])
|
|
const actionReferences = [
|
|
...workflow.matchAll(
|
|
/uses:\s+((?:actions\/(?:checkout|setup-node|cache|upload-artifact|download-artifact)|aliyun\/configure-aliyun-credentials-action)@v\d+)/gu
|
|
)
|
|
].map((match) => match[1])
|
|
expect(new Set(actionReferences)).toEqual(
|
|
new Set([
|
|
'actions/checkout@v7',
|
|
'actions/setup-node@v7',
|
|
'actions/cache@v6',
|
|
'actions/upload-artifact@v7',
|
|
'actions/download-artifact@v8',
|
|
'aliyun/configure-aliyun-credentials-action@v1'
|
|
])
|
|
)
|
|
expect(workflow).not.toContain('agent-signing:')
|
|
expect(workflow).not.toContain('remote-runtime-signing:')
|
|
expect(workflow).not.toContain(
|
|
'GOODBUDDY_SIGNING_PRIVATE_KEY'
|
|
)
|
|
})
|
|
|
|
it('packages all six desktop targets without remote component dependencies', () => {
|
|
expect(
|
|
parsed.jobs.package?.strategy?.matrix?.include
|
|
).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
platform: 'windows',
|
|
arch: 'x64',
|
|
runner: 'windows-2025'
|
|
}),
|
|
expect.objectContaining({
|
|
platform: 'windows',
|
|
arch: 'arm64',
|
|
runner: 'windows-11-vs2026-arm'
|
|
}),
|
|
expect.objectContaining({
|
|
platform: 'macos',
|
|
arch: 'x64',
|
|
runner: 'macos-15-intel'
|
|
}),
|
|
expect.objectContaining({
|
|
platform: 'macos',
|
|
arch: 'arm64',
|
|
runner: 'macos-15'
|
|
}),
|
|
expect.objectContaining({
|
|
platform: 'linux',
|
|
arch: 'x64',
|
|
runner: 'ubuntu-24.04'
|
|
}),
|
|
expect.objectContaining({
|
|
platform: 'linux',
|
|
arch: 'arm64',
|
|
runner: 'ubuntu-24.04-arm'
|
|
})
|
|
])
|
|
)
|
|
expect(jobNeeds('package')).toEqual([
|
|
'validate',
|
|
'authorize-release'
|
|
])
|
|
const scripts = jobScripts('package')
|
|
expect(scripts).toContain('npm run release:package')
|
|
expect(scripts).toContain(
|
|
"process.arch !== '${{ matrix.arch }}'"
|
|
)
|
|
expect(serializedJob('package')).toContain(
|
|
'GOODBUDDY_RUN_LIVE_TOOL_ENVIRONMENT'
|
|
)
|
|
expect(scripts).toContain(
|
|
'tests/local-tool-environment-live.test.ts'
|
|
)
|
|
expect(scripts).not.toContain('agent-bundle.cjs import')
|
|
expect(scripts).not.toContain(
|
|
'remote-runtime-bundle.cjs import'
|
|
)
|
|
expect(serializedJob('package')).not.toContain(
|
|
'goodbuddy-agent-linux-'
|
|
)
|
|
expect(serializedJob('package')).not.toContain(
|
|
'goodbuddy-opencode-linux-'
|
|
)
|
|
})
|
|
|
|
it('keeps desktop authorization tied to protected main and v tags', () => {
|
|
const authorization = serializedJob('authorize-release')
|
|
const policy = jobScripts('authorize-release')
|
|
expect(authorization).toContain(
|
|
"github.ref == 'refs/heads/main'"
|
|
)
|
|
expect(policy).toContain('git merge-base --is-ancestor')
|
|
expect(policy).toContain(
|
|
"require('./package.json').version"
|
|
)
|
|
expect(policy).toContain(
|
|
'test "$GITHUB_REF" = "refs/tags/$expected"'
|
|
)
|
|
expect(policy).toContain(
|
|
'refs/tags/$GITHUB_REF_NAME^{commit}'
|
|
)
|
|
expect(policy).toContain(
|
|
'echo "authorized=true" >> "$GITHUB_OUTPUT"'
|
|
)
|
|
expect(serializedJob('package')).toContain(
|
|
"needs.authorize-release.outputs.authorized == 'true'"
|
|
)
|
|
})
|
|
|
|
it('installs RPM tooling and invokes the explicit native packager', () => {
|
|
const packageJob = serializedJob('package')
|
|
const scripts = jobScripts('package')
|
|
expect(packageJob).toContain('Install RPM packaging tools')
|
|
expect(packageJob).toContain(
|
|
"matrix.platform == 'linux'"
|
|
)
|
|
expect(scripts).toContain(
|
|
'sudo apt-get install --no-install-recommends --yes rpm'
|
|
)
|
|
expect(scripts).toContain('rpmbuild --version')
|
|
expect(scripts).toContain(
|
|
'--platform ${{ matrix.platform }} --arch ${{ matrix.arch }}'
|
|
)
|
|
})
|
|
|
|
it('publishes only aggregated desktop assets through the existing OSS contract', () => {
|
|
expect(jobNeeds('release')).toEqual([
|
|
'authorize-release',
|
|
'package'
|
|
])
|
|
expect(parsed.jobs.release?.environment?.name).toBe(
|
|
'aliyun-oss-release'
|
|
)
|
|
const release = serializedJob('release')
|
|
const scripts = jobScripts('release')
|
|
expect(release).toContain(
|
|
'aliyun/configure-aliyun-credentials-action@v1'
|
|
)
|
|
expect(scripts).toContain(
|
|
'node build/aggregate-release.cjs'
|
|
)
|
|
expect(scripts).toContain(
|
|
'node build/create-site-release.cjs'
|
|
)
|
|
expect(scripts).toContain(
|
|
'node build/verify-site-release.cjs'
|
|
)
|
|
expect(scripts).toContain(
|
|
'oss://${OSS_BUCKET}/releases/${GITHUB_REF_NAME}'
|
|
)
|
|
expect(scripts).toContain(
|
|
'oss://${OSS_BUCKET}/releases/latest.json'
|
|
)
|
|
})
|
|
|
|
it('retains macOS signed and explicit unsigned release modes', () => {
|
|
const scripts = jobScripts('package')
|
|
expect(scripts).toContain('--skip-build --unsigned')
|
|
expect(serializedJob('package')).toContain(
|
|
'"CSC_LINK":"${{ runner.temp }}/goodbuddy-developer-id.p12"'
|
|
)
|
|
expect(scripts).toContain('xcrun stapler validate')
|
|
expect(scripts).toContain('spctl --assess')
|
|
})
|
|
})
|