mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 16:02:35 +00:00
* ci: scope orcad smoke and parallelize Linux package formats * ci: validate packaging when its copy dependency changes --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
212 lines
7.1 KiB
JavaScript
212 lines
7.1 KiB
JavaScript
import {
|
|
chmodSync,
|
|
existsSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
readdirSync,
|
|
readlinkSync,
|
|
rmSync,
|
|
statSync,
|
|
symlinkSync,
|
|
writeFileSync
|
|
} from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import { linuxFormatArguments, packageLinuxFormats } from './package-linux-formats.mjs'
|
|
|
|
let root
|
|
let preparedDirectory
|
|
let outputDirectory
|
|
const targets = ['AppImage', 'deb', 'rpm']
|
|
const valueAfter = (args, flag) => args[args.indexOf(flag) + 1]
|
|
|
|
beforeEach(() => {
|
|
root = mkdtempSync(join(tmpdir(), 'orca formats with spaces-'))
|
|
preparedDirectory = join(root, 'prepared app')
|
|
outputDirectory = join(root, 'output packages')
|
|
mkdirSync(join(preparedDirectory, 'resources'), { recursive: true })
|
|
mkdirSync(outputDirectory)
|
|
writeFileSync(join(preparedDirectory, 'resources/package-type'), 'AppImage')
|
|
writeFileSync(join(preparedDirectory, 'resources/app.asar'), 'verified application')
|
|
writeFileSync(join(preparedDirectory, 'orca-ide'), 'executable')
|
|
chmodSync(join(preparedDirectory, 'orca-ide'), 0o755)
|
|
})
|
|
|
|
afterEach(() => rmSync(root, { recursive: true, force: true }))
|
|
|
|
function emitPackage(args) {
|
|
const format = valueAfter(args, '--linux')
|
|
const app = valueAfter(args, '--prepackaged')
|
|
const output = valueAfter(args, '--config.directories.output')
|
|
writeFileSync(join(app, 'resources/package-type'), format)
|
|
mkdirSync(output, { recursive: true })
|
|
writeFileSync(join(output, `orca.${format}`), format)
|
|
writeFileSync(join(output, 'latest-linux.yml'), format)
|
|
return { format, app, output }
|
|
}
|
|
|
|
it('preserves configured hooks, architecture, names, and PR compression with exact argv', () => {
|
|
const args = linuxFormatArguments({
|
|
format: 'deb',
|
|
appDirectory: '/app with spaces',
|
|
outputDirectory: '/out with spaces'
|
|
})
|
|
expect(args).toEqual([
|
|
'--config',
|
|
'config/electron-builder.config.cjs',
|
|
'--linux',
|
|
'deb',
|
|
'--x64',
|
|
'--publish',
|
|
'never',
|
|
'--prepackaged',
|
|
'/app with spaces',
|
|
'--config.directories.output',
|
|
'/out with spaces',
|
|
'--config.deb.compression=gz',
|
|
'--config.rpm.compression=gzip'
|
|
])
|
|
expect(() => linuxFormatArguments({ format: 'zip' })).toThrow('Unsupported Linux package format')
|
|
})
|
|
|
|
describe('independent Linux package formats', () => {
|
|
it('starts all formats before awaiting completion and isolates metadata writes', async () => {
|
|
if (process.platform !== 'win32') {
|
|
symlinkSync('resources/package-type', join(preparedDirectory, 'marker-link'))
|
|
}
|
|
let release
|
|
const gate = new Promise((resolve) => {
|
|
release = resolve
|
|
})
|
|
const copies = []
|
|
await packageLinuxFormats({
|
|
preparedDirectory,
|
|
outputDirectory,
|
|
runBuilder: async (args) => {
|
|
const app = valueAfter(args, '--prepackaged')
|
|
const format = valueAfter(args, '--linux')
|
|
expect(readFileSync(join(app, 'resources/package-type'), 'utf8')).toBe('AppImage')
|
|
expect(readFileSync(join(app, 'resources/app.asar'), 'utf8')).toBe('verified application')
|
|
if (process.platform !== 'win32') {
|
|
expect(readlinkSync(join(app, 'marker-link'))).toBe('resources/package-type')
|
|
expect(statSync(join(app, 'orca-ide')).mode & 0o777).toBe(0o755)
|
|
expect(statSync(join(app, 'resources/package-type')).ino).not.toBe(
|
|
statSync(join(preparedDirectory, 'resources/package-type')).ino
|
|
)
|
|
}
|
|
copies.push(app)
|
|
if (copies.length === 3) {
|
|
release()
|
|
}
|
|
await gate
|
|
expect(readdirSync(outputDirectory)).toHaveLength(1)
|
|
emitPackage(args)
|
|
await Promise.resolve()
|
|
expect(readFileSync(join(app, 'resources/package-type'), 'utf8')).toBe(format)
|
|
}
|
|
})
|
|
expect(new Set(copies).size).toBe(3)
|
|
expect(readFileSync(join(preparedDirectory, 'resources/package-type'), 'utf8')).toBe('AppImage')
|
|
expect(readFileSync(join(preparedDirectory, 'resources/app.asar'), 'utf8')).toBe(
|
|
'verified application'
|
|
)
|
|
for (const format of targets) {
|
|
expect(readFileSync(join(outputDirectory, `orca.${format}`), 'utf8')).toBe(format)
|
|
expect(
|
|
readFileSync(
|
|
join(outputDirectory, 'linux-package-formats', format, 'latest-linux.yml'),
|
|
'utf8'
|
|
)
|
|
).toBe(format)
|
|
}
|
|
expect(
|
|
readdirSync(outputDirectory).some((name) => name.startsWith('.linux-package-formats-'))
|
|
).toBe(false)
|
|
})
|
|
|
|
it('settles every worker before cleanup and exposes no partial artifact on failure', async () => {
|
|
const finished = []
|
|
let release
|
|
const gate = new Promise((resolve) => {
|
|
release = resolve
|
|
})
|
|
await expect(
|
|
packageLinuxFormats({
|
|
preparedDirectory,
|
|
outputDirectory,
|
|
runBuilder: async (args) => {
|
|
const format = valueAfter(args, '--linux')
|
|
if (format === 'AppImage') {
|
|
throw new Error('compression failed')
|
|
}
|
|
if (format === 'rpm') {
|
|
release()
|
|
}
|
|
await gate
|
|
const { app } = emitPackage(args)
|
|
expect(existsSync(app)).toBe(true)
|
|
finished.push(format)
|
|
}
|
|
})
|
|
).rejects.toMatchObject({
|
|
message: 'Linux package formats failed',
|
|
errors: [
|
|
expect.objectContaining({
|
|
message: 'AppImage packaging failed',
|
|
cause: expect.objectContaining({ message: 'compression failed' })
|
|
})
|
|
]
|
|
})
|
|
expect(finished.sort()).toEqual(['deb', 'rpm'])
|
|
expect(readdirSync(outputDirectory)).toEqual([])
|
|
expect(readFileSync(join(preparedDirectory, 'resources/package-type'), 'utf8')).toBe('AppImage')
|
|
})
|
|
|
|
it('rejects missing artifacts even when the builder reports success', async () => {
|
|
await expect(
|
|
packageLinuxFormats({
|
|
preparedDirectory,
|
|
outputDirectory,
|
|
runBuilder: async (args) => {
|
|
const result = emitPackage(args)
|
|
if (result.format === 'rpm') {
|
|
writeFileSync(join(result.output, 'orca.rpm'), '')
|
|
}
|
|
}
|
|
})
|
|
).rejects.toThrow('Linux package formats failed')
|
|
expect(readdirSync(outputDirectory)).toEqual([])
|
|
})
|
|
|
|
it('does not replace existing output packages', async () => {
|
|
writeFileSync(join(outputDirectory, 'orca.deb'), 'previous package')
|
|
await expect(
|
|
packageLinuxFormats({
|
|
preparedDirectory,
|
|
outputDirectory,
|
|
runBuilder: async (args) => {
|
|
emitPackage(args)
|
|
}
|
|
})
|
|
).rejects.toThrow('Refusing to replace existing package output')
|
|
expect(readdirSync(outputDirectory)).toEqual(['orca.deb'])
|
|
expect(readFileSync(join(outputDirectory, 'orca.deb'), 'utf8')).toBe('previous package')
|
|
})
|
|
|
|
it('rejects a tree already mutated by a previous root-package build', async () => {
|
|
writeFileSync(join(preparedDirectory, 'resources/package-type'), 'rpm')
|
|
await expect(
|
|
packageLinuxFormats({
|
|
preparedDirectory,
|
|
outputDirectory,
|
|
runBuilder: async () => {
|
|
throw new Error('must not run')
|
|
}
|
|
})
|
|
).rejects.toThrow('Expected a fresh Linux directory build')
|
|
expect(readdirSync(outputDirectory)).toEqual([])
|
|
})
|
|
})
|