@async/pipeline

Sync: Choose What GitHub And npm See

pipeline.ts owns the workflow. GitHub Actions and package.json are read surfaces other tools depend on — so instead of taking them over, @async/pipeline generates exactly the pieces you opt into, records ownership, and fails loudly on drift or collision.

sync: {
  github: true,   // one bootloader workflow + lock
  tasks: true     // job scripts in package.json
}

Both are independent. Omit either and that surface is never touched.

GitHub Actions Gets A Bootloader, Not Your Logic

GitHub decides whether to start a workflow from committed YAML before any of your code runs. That YAML is the only thing sync puts there:

async-pipeline sync github generate

writes two files:

.github/workflows/async-pipeline.yml   # pinned actions, contents: read, calls the CLI
.github/async-pipeline.lock.json       # hash of the trigger/job metadata it was built from

The workflow checks out, sets up Node, restores the task cache, runs async-pipeline github check (so a stale workflow fails its own run), and delegates job selection back to the CLI. Task commands, dependency order, caching, and retries never appear in YAML — they stay in pipeline.ts, which means changing a task does not require touching CI.

What this deliberately does not do:

npm Scripts Stay Yours

sync.tasks writes package-manager aliases for the jobs you pick — and nothing else:

sync: {
  tasks: {
    prefix: "pipeline",
    jobs: ["verify"],
    scripts: { "sync:check": "sync check" }
  }
}
{
  "scripts": {
    "pipeline:verify": "async-pipeline run verify",
    "pipeline:sync:check": "async-pipeline sync check"
  }
}

Every generated script is namespaced under your chosen prefix and recorded in .async-pipeline/tasks.lock.json. That lock is the ownership boundary: sync only ever rewrites scripts the lock claims. If a generated name collides with a script it does not own, generation fails with ASYNC_PIPELINE_SYNC_CONFLICT instead of overwriting your work. Your hand-written scripts are never rewritten, reordered, or removed.

Drift Is An Error, Not A Surprise

The generated files are committed, so they are reviewable in every PR — and checkable:

async-pipeline sync check     # all synced surfaces match pipeline.ts, or non-zero
async-pipeline sync generate  # regenerate everything that is stale

Run sync check in the pipeline itself (this repo does) and a hand-edited workflow or script fails CI with a diffable reason. The boundary stays honest in both directions: pipeline.ts cannot silently diverge from what GitHub runs, and nobody can quietly move logic into YAML.

Leaving Is Cheap

The exit test for any tool that generates files: what happens when you remove it?

  1. Delete the sync block from pipeline.ts.
  2. Delete .github/workflows/async-pipeline.yml, the lock files, and the pipeline:* scripts.

That is the whole list. The generated workflow and scripts are plain artifacts with no runtime hook back into the package — until that day, they are simply the parts of your pipeline you chose to publish to GitHub and npm.

Where To Go Next