OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / github-release-setup.test.js

174 lines · javascript ref:
import test from 'brittle'
import { execFile } from 'node:child_process'
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'

const REQUIRED_SECRETS = [
  'FLEET_SSH_PRIVATE_KEY',
  'UMBREL_STORE_TOKEN',
  'UMBREL_OFFICIAL_PR_TOKEN',
  'ECOSYSTEM_CONSUMER_TOKEN',
  'UMBREL_OFFICIAL_FORK',
  'NPM_TOKEN',
  'STARTOS_DEVELOPER_KEY_PEM',
  'STARTOS_REGISTRY_URL'
]

function runSetupCheck (argv, env = {}) {
  return new Promise((resolve) => {
    execFile(process.execPath, ['scripts/check-github-release-setup.mjs', ...argv], {
      cwd: process.cwd(),
      env: {
        PATH: process.env.PATH || '',
        ...env
      },
      timeout: 10000
    }, (err, stdout, stderr) => {
      resolve({
        status: err && typeof err.code === 'number' ? err.code : 0,
        stdout,
        stderr
      })
    })
  })
}

async function fakeGh (t) {
  const dir = await mkdtemp(path.join(tmpdir(), 'hiverelay-fake-gh-'))
  t.teardown(async () => {
    await rm(dir, { recursive: true, force: true })
  })
  const file = path.join(dir, 'gh')
  await writeFile(file, `#!/usr/bin/env node
const args = process.argv.slice(2)
const payload = JSON.parse(process.env.HIVERELAY_FAKE_GH || '{}')
if (payload.fail) {
  process.stderr.write(payload.failMessage || 'fake gh failure')
  process.exit(payload.failCode || 1)
}
if (payload.invalidJson) {
  process.stdout.write('{not-json')
  process.exit(0)
}
if (args[0] === 'secret' && args[1] === 'list') {
  process.stdout.write(JSON.stringify((payload.secrets || []).map(name => ({ name }))))
  process.exit(0)
}
if (args[0] === 'variable' && args[1] === 'list') {
  process.stdout.write(JSON.stringify((payload.variables || []).map(row => ({ name: row.name, value: row.value }))))
  process.exit(0)
}
process.stderr.write('unexpected fake gh args: ' + args.join(' '))
process.exit(2)
`)
  await chmod(file, 0o755)
  return file
}

function fakeEnv (payload) {
  return {
    HIVERELAY_FAKE_GH: JSON.stringify(payload)
  }
}

test('GitHub release setup check passes with all required secrets', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bigdestiny2/P2P-Hiverelay',
    '--gh', gh
  ], fakeEnv({
    secrets: REQUIRED_SECRETS,
    variables: [{ name: 'FLEET_ROLLOUT_TIMEOUT_MS', value: '1800000' }]
  }))

  t.is(res.status, 0)
  t.ok(res.stdout.includes('GitHub release setup presence check passed'))
  t.ok(res.stdout.includes('Required masked release values stored as GitHub Secrets: 8/8'))
  t.ok(res.stdout.includes('FLEET_ROLLOUT_TIMEOUT_MS'))
  t.ok(res.stdout.includes('Secret values are not readable through gh'))
  t.ok(res.stdout.includes('Release distribution preflight'))
})

test('GitHub release setup check reports missing release secrets', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bigdestiny2/P2P-Hiverelay',
    '--gh', gh
  ], fakeEnv({
    secrets: ['FLEET_SSH_PRIVATE_KEY'],
    variables: []
  }))

  t.is(res.status, 1)
  t.ok(res.stderr.includes('GitHub release setup check failed'))
  t.ok(res.stderr.includes('Missing repository secret UMBREL_STORE_TOKEN'))
  t.ok(res.stderr.includes('Missing repository secret ECOSYSTEM_CONSUMER_TOKEN'))
  t.ok(res.stderr.includes('Missing repository secret NPM_TOKEN'))
  t.ok(res.stderr.includes('Missing repository secret STARTOS_REGISTRY_URL'))
  t.ok(res.stderr.includes('Repair path:'))
  t.ok(res.stderr.includes('npm run release:write-secret-template -- --out /private/tmp/hiverelay-release-secrets.env'))
  t.ok(res.stderr.includes('npm run release:check-distribution-env -- --env-file /private/tmp/hiverelay-release-secrets.env --channel both --prerelease false'))
  t.ok(res.stderr.includes('npm run release:apply-github-secrets -- --repo bigdestiny2/P2P-Hiverelay --env-file /private/tmp/hiverelay-release-secrets.env'))
  t.ok(res.stderr.includes('npm run release:check-github-setup -- --repo bigdestiny2/P2P-Hiverelay'))
  t.ok(res.stderr.includes('gh workflow run release-distribution-preflight.yml --repo bigdestiny2/P2P-Hiverelay --ref main -f channel=both -f prerelease=false'))
  t.absent(res.stderr.includes('Missing repository secret FLEET_SSH_PRIVATE_KEY'))
})

test('GitHub release setup check rejects required secrets configured as variables', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bigdestiny2/P2P-Hiverelay',
    '--gh', gh
  ], fakeEnv({
    secrets: REQUIRED_SECRETS.filter(name => name !== 'UMBREL_STORE_TOKEN'),
    variables: [{ name: 'UMBREL_STORE_TOKEN', value: 'ghp_example' }]
  }))

  t.is(res.status, 1)
  t.ok(res.stderr.includes('Missing repository secret UMBREL_STORE_TOKEN'))
  t.ok(res.stderr.includes('Required secret UMBREL_STORE_TOKEN is configured as a repository variable'))
})

test('GitHub release setup check rejects invalid optional fleet timeout variable', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bigdestiny2/P2P-Hiverelay',
    '--gh', gh
  ], fakeEnv({
    secrets: REQUIRED_SECRETS,
    variables: [{ name: 'FLEET_ROLLOUT_TIMEOUT_MS', value: '1800000\nHIVERELAY_ATTACKER_VALUE=owned' }]
  }))

  t.is(res.status, 1)
  t.ok(res.stderr.includes('FLEET_ROLLOUT_TIMEOUT_MS must be an integer'))
  t.absent(res.stderr.includes('HIVERELAY_ATTACKER_VALUE=owned'))
})

test('GitHub release setup check reports gh JSON failures', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bigdestiny2/P2P-Hiverelay',
    '--gh', gh
  ], fakeEnv({
    invalidJson: true
  }))

  t.is(res.status, 1)
  t.ok(res.stderr.includes('returned invalid JSON'))
})

test('GitHub release setup check rejects malformed repo names before gh calls', async (t) => {
  const gh = await fakeGh(t)
  const res = await runSetupCheck([
    '--repo', 'bad/repo\nHIVERELAY_ATTACKER_VALUE=owned',
    '--gh', gh
  ], fakeEnv({
    secrets: REQUIRED_SECRETS
  }))

  t.is(res.status, 1)
  t.ok(res.stderr.includes('Invalid --repo'))
  t.absent(res.stderr.includes('HIVERELAY_ATTACKER_VALUE=owned'))
})