Ctrl K
ComunidadGuías…

scripts/release-ci.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { appendFileSync, readFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { setTimeout as delay } from 'node:timers/promises';
import { validateAgentRelease } from '../app/src/lib/agent-release.mjs';

export function selectCiRun(runs, sha) {
  return runs.filter(run => run.head_sha === sha &&
    ['push', 'workflow_dispatch'].includes(run.event))
    .sort((a, b) => b.id - a.id)[0];
}

export function planAgentRelease({ applicationVersion, agentVersion, sourceChanged }) {
  if (applicationVersion === agentVersion) return { build: true };
  if (sourceChanged) throw new Error('Agent source changed: bump the app version and run pnpm agent:release:prepare before tagging.');
  return { build: false };
}

export async function waitForCi({ request, repository, sha, ref, pause = delay }) {
  let dispatched = false;
  for (let attempt = 0; attempt < 120; attempt++) {
    const result = await request(`/repos/${repository}/actions/workflows/ci.yml/runs?head_sha=${sha}&per_page=100`);
    const run = selectCiRun(result.workflow_runs, sha);
    if (!run && !dispatched) {
      await request(`/repos/${repository}/actions/workflows/ci.yml/dispatches`, { ref });
      dispatched = true;
    }
    if (run?.status === 'completed') {
      if (run.conclusion !== 'success') throw new Error(`CI did not pass for ${sha}: ${run.html_url}`);
      return run.id;
    }
    await pause(15000);
  }
  throw new Error('Timed out waiting for CI for the release commit.');
}

export async function githubRequest(path, body) {
  const response = await fetch(`https://api.github.com${path}`, {
    method: body ? 'POST' : 'GET',
    signal: AbortSignal.timeout(30000),
    headers: { Authorization: `Bearer ${process.env.GH_TOKEN}`, Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', ...(body ? { 'Content-Type': 'application/json' } : {}) },
    ...(body ? { body: JSON.stringify(body) } : {}),
  });
  if (!response.ok) throw new Error(`GitHub request failed (${response.status}): ${path}`);
  return response.status === 204 ? null : response.json();
}

async function main() {
  const root = resolve(import.meta.dirname, '..');
  const pkg = JSON.parse(readFileSync(resolve(root, 'app/package.json'), 'utf8'));
  const pin = validateAgentRelease(JSON.parse(readFileSync(resolve(root, 'app/agent-release.json'), 'utf8')));
  const ref = process.env.RELEASE_TAG ?? process.env.GITHUB_REF_NAME;
  if (ref !== `v${pkg.version}`) throw new Error('Release tag must match the application package version.');
  const sha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
  let sourceChanged = false;
  if (pkg.version !== pin.version) {
    const previous = execFileSync('git', ['rev-parse', `refs/tags/v${pin.version}^{commit}`], { cwd: root, encoding: 'utf8' }).trim();
    sourceChanged = Boolean(execFileSync('git', ['diff', '--name-only', previous, sha, '--', 'agent', 'go.work', 'go.work.sum', 'scripts/build-agent.mjs', 'scripts/agent-release.mjs'], { cwd: root, encoding: 'utf8' }).trim());
  }
  const plan = planAgentRelease({ applicationVersion: pkg.version, agentVersion: pin.version, sourceChanged });
  const run = await waitForCi({ request: githubRequest, repository: process.env.GITHUB_REPOSITORY, sha, ref });
  appendFileSync(process.env.GITHUB_OUTPUT, `ci_run_id=${run}\nbuild_agent=${plan.build}\n`);
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();