Skip to content

Automation example

This example ties together the build, diff, and audit runtimes to validate a release candidate before publishing artefacts.

ts
// scripts/release-check.ts
import { createDefaultBuildEnvironment, executeBuild, loadConfig } from '@dtifx/build';
import {
  createRunContext,
  createSessionTokenSourcePort,
  renderReport,
  runDiffSession,
} from '@dtifx/diff';
import {
  createAuditReporter,
  createAuditRuntime,
  createAuditTokenResolutionEnvironment,
  loadAuditConfiguration,
  resolveAuditConfigPath,
  type AuditTelemetryRuntime,
} from '@dtifx/audit';
import { JsonLineLogger } from '@dtifx/core/logging';
import { createTelemetryRuntime } from '@dtifx/core/telemetry';

async function main(): Promise<void> {
  // 1. Run the build pipeline to ensure transforms and formatters succeed.
  const loadedBuild = await loadConfig('./dtifx.config.mjs');
  const buildEnvironment = createDefaultBuildEnvironment(
    {
      config: loadedBuild.config,
      configDirectory: loadedBuild.directory,
      configPath: loadedBuild.path,
    },
    { defaultOutDir: 'dist' },
  );
  const telemetry = createTelemetryRuntime('stdout');
  try {
    const buildResult = await executeBuild(
      buildEnvironment.services,
      loadedBuild.config,
      telemetry.tracer,
      { parentSpan: telemetry.tracer.startSpan('release.build') },
    );
    console.log(`Generated ${buildResult.formatters.length} formatter batches.`);

    // 2. Diff the proposed release against the last published snapshot.
    const diffSession = await runDiffSession(
      {
        tokenSource: createSessionTokenSourcePort({
          previous: { kind: 'file', target: 'snapshots/published.json' },
          next: { kind: 'file', target: 'snapshots/candidate.json' },
        }),
      },
      {
        failure: { failOnBreaking: true },
      },
    );
    const diffReport = await renderReport(diffSession.filteredDiff, {
      format: 'markdown',
      mode: 'summary',
      runContext: createRunContext({
        sources: {
          previous: { kind: 'file', target: 'snapshots/published.json' },
          next: { kind: 'file', target: 'snapshots/candidate.json' },
        },
        startedAt: new Date(),
        durationMs: 0,
      }),
    });
    console.log(diffReport);
    if (diffSession.failure.shouldFail) {
      throw new Error('Blocking change detected by diff policy.');
    }

    // 3. Run governance policies using the same configuration.
    const auditConfigPath = await resolveAuditConfigPath();
    const loadedAudit = await loadAuditConfiguration({ path: auditConfigPath });
    const logger = new JsonLineLogger(process.stderr);
    const auditEnvironment = await createAuditTokenResolutionEnvironment({
      configuration: loadedAudit,
      telemetry: telemetry as AuditTelemetryRuntime,
      logger,
    });
    const reporter = createAuditReporter({
      format: 'human',
      logger,
      includeTimings: true,
    });
    const auditRuntime = createAuditRuntime({
      configuration: auditEnvironment.policyConfiguration,
      reporter,
      telemetry,
      tokens: auditEnvironment.tokens,
      dispose: () => auditEnvironment.dispose(),
    });
    const auditResult = await auditRuntime.run();
    if (auditResult.summary.severity.error > 0) {
      throw new Error('Audit reported blocking policy violations.');
    }
  } finally {
    await telemetry.exportSpans();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Run the script with tsx or ts-node to exercise all runtimes before publishing a release. Extend it with notification hooks, artifact uploads, or change management automation as required.