breakaway 1.4.0-main.35 → 1.4.0-main.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pipeline/SKILL.md +127 -0
- package/package.json +2 -1
- package/src/init.js +4 -3
- package/src/prompt.js +2 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pipeline
|
|
3
|
+
description: Use when a task asks to move a repository's CI/CD to breakaway's deploy flow or release flow (Move to breakaway's deploy flow, Deploy with breakaway, Release with breakaway), to write or change .github/breakaway-pipeline.json, to run npx breakaway pipeline init or pipeline check, or to map an old deploy or npm publish workflow onto Deploy, Promote, Roll back, and Release.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Moving a repository to the deploy flow
|
|
7
|
+
|
|
8
|
+
A move is one task and one pull request: you read how the repository checks, deploys, and publishes today, write `.github/breakaway-pipeline.json`, render the workflows from it with `npx breakaway pipeline init`, and account for every step of the old setup, so nothing is lost and nothing runs twice. The design is the [move's spec](../../../docs/specs/IDEA-27-move-ci-cd-to-the-deploy-flow.md) (sections 2, 2b, and 3), and the [docs](../../../docs/tasks.md#moving-a-repository-to-the-deploy-flow) say what the flows do.
|
|
9
|
+
|
|
10
|
+
It is an ordinary build: claim, check in on the peloton, and hand over the way the repository's prompt and the `tasks` skill say. This skill is how to do the move itself.
|
|
11
|
+
|
|
12
|
+
**You never** deploy, publish, run or re-run a workflow, touch Cloudflare, npm, or the repository's GitHub settings (environments, secrets, variables, the App's permissions), or set the repository's pipeline on the board (`repos modify --pipeline` and **Turn on deploys** are the owner's). You write files in a pull request; the owner does the rest.
|
|
13
|
+
|
|
14
|
+
## 1. Read what's there
|
|
15
|
+
|
|
16
|
+
Read all of it before you decide anything:
|
|
17
|
+
|
|
18
|
+
- **Workflows**, `.github/workflows/*.yml`: each one's `name:` line, its triggers (`push`, `pull_request`, `workflow_run`, `release`, `workflow_dispatch`, `schedule`), and every job and step. Note which steps check (lint, test, build, typecheck), which deploy (`wrangler deploy`, `wrangler versions upload`, `cloudflare/wrangler-action`), which publish (`npm publish`, or pnpm's or yarn's, `JS-DevTools/npm-publish`, a `release` event), and which do something else (a migration, a cache purge, a notification, a manual approval through an `environment` with reviewers).
|
|
19
|
+
- **Wrangler config**, `wrangler.jsonc`, `wrangler.json`, or `wrangler.toml`: the Worker's `name`, its `env` blocks (each one's name, and the Worker name it deploys as), and its bindings, above all D1 databases and their `migrations_dir`.
|
|
20
|
+
- **Migrations**: the folder and how they're applied today (`wrangler d1 migrations apply`, a script).
|
|
21
|
+
- **`package.json`**: `name`, `version`, `private`, `scripts` (`build`, `test`, `deploy`, `release`, `prepublishOnly`), `publishConfig`, and `workspaces`; and the lockfile, which says the install command (`npm ci` for `package-lock.json`, or pnpm's or yarn's frozen install for theirs).
|
|
22
|
+
- **Versioning tools**: `.changeset/`, `release-please-config.json`, `.releaserc*` or a `release` key in `package.json` (semantic-release), and `lerna.json`.
|
|
23
|
+
- **Anything already at the flow's paths**: `.github/breakaway-pipeline.json`, `.github/deploy-paths.json`, and `deploy.yml`, `promote.yml`, `rollback.yml`, or `release.yml` in `.github/workflows/`.
|
|
24
|
+
- **Scripts the workflows run**, in `scripts/` or `package.json`: what each does, so you know whether `beforeDeploy` can run it.
|
|
25
|
+
|
|
26
|
+
## 2. Decide what moves
|
|
27
|
+
|
|
28
|
+
| The repository today | What you do |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| Deploys a Worker with `wrangler`, from Actions, Workers Builds, or by hand | Move it: `workers` in the config. |
|
|
31
|
+
| Has checks but deploys by hand | Move it: the deploy is new, and the checks stay as they are. |
|
|
32
|
+
| Publishes a package to npm, from Actions or by hand | Move it: `package` in the config. |
|
|
33
|
+
| Deploys a Worker and publishes a package | Both, in one config and one pull request. |
|
|
34
|
+
| Publishes to another registry (GitHub Packages, JSR, PyPI) | That publishing stays as it is. Say so in the pull request; move a Worker deploy if there is one. |
|
|
35
|
+
| Deploys somewhere else (Pages, Vercel, Fly, a server, containers) | Stop (below). breakaway's deploy flow runs Cloudflare Workers only; a deploy command of the repository's own is planned, not built. |
|
|
36
|
+
| `package.json` says `"private": true` and nothing deploys | Stop: there is nothing the flows can take. |
|
|
37
|
+
|
|
38
|
+
The first version takes **one staging and one production Worker** and **one package** per repository. With more Workers, move the pair the task names (or that the old deploy targets on the default branch), and leave the others' deploys as they are, saying so. With several publishable packages (a monorepo), move the one the task names (or ask, below), and leave the others' publishing as it is.
|
|
39
|
+
|
|
40
|
+
**Stopping** means changing nothing and opening no pull request: `comment` on the task what you found and why it doesn't move (the row above, in a sentence the owner can act on), then `release` it. When only the owner can choose (which package, which Worker pair, or what to do with a versioning tool), ask with a decision instead (the core's "Asking for a decision") and release.
|
|
41
|
+
|
|
42
|
+
## 3. Write the config
|
|
43
|
+
|
|
44
|
+
`.github/breakaway-pipeline.json` is the repository's own. Every value comes from what you read, never invented:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"workers": { "staging": "widgets-staging", "production": "widgets" },
|
|
49
|
+
"wranglerEnv": { "staging": "staging", "production": "production" },
|
|
50
|
+
"branch": "main",
|
|
51
|
+
"checks": ["CI"],
|
|
52
|
+
"install": "npm ci",
|
|
53
|
+
"build": "npm run build",
|
|
54
|
+
"beforeDeploy": ["npx wrangler d1 migrations apply DB --remote --env $BREAKAWAY_ENV"],
|
|
55
|
+
"deployPaths": { "widgets": "^(src|public|migrations)/|^wrangler\\.jsonc$|^package(-lock)?\\.json$" },
|
|
56
|
+
"healthCheck": { "staging": "https://widgets-staging.example.workers.dev/", "production": "https://widgets.example.com/" },
|
|
57
|
+
"package": { "name": "widgets", "directory": ".", "access": "public" }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **`workers`**: the staging and production Worker names, as the wrangler config deploys them (with its `env` blocks, the name each environment deploys as). Two different Workers. A repository with only production today needs a staging Worker: name it `<production>-staging`, and the owner creates it (the checklist).
|
|
62
|
+
- **`wranglerEnv`**: only when the wrangler config has `env` blocks; the environment name for each Worker, passed as `--env`.
|
|
63
|
+
- **`branch`**: the default branch, when it isn't `main`.
|
|
64
|
+
- **`checks`**: the `name:` line of each workflow that must pass before a deploy or a release, exactly as written. Every check the old deploy waited for, and the ones that run on push to the default branch. Never a workflow you're about to remove.
|
|
65
|
+
- **`install`, `build`**: the repository's own one-line commands, from its old workflow or lockfile. Leave `build` out when nothing builds.
|
|
66
|
+
- **`beforeDeploy`**: the old deploy's steps before `wrangler deploy` (migrations first), one command each. They read `$BREAKAWAY_ENV` (`staging` or `production`) and `$WORKER`. A step that needs more than one line goes in a script the command runs; a step that can't run there stays where it is (step 5).
|
|
67
|
+
- **`deployPaths`**: per Worker, a regular expression of the paths whose change needs a deploy: its source, static assets, migrations, the wrangler config, and the package and lockfile. The board reads the same patterns to say "No deploy needed", so a path left out never ships and a path put in too many deploys for nothing. You can't prove them from reading, so the pull request lists each part and what it covers (step 6).
|
|
68
|
+
- **`healthCheck`**: an address the old workflow or the README checks after a deploy, as a string (staging only) or `{ "staging", "production" }`. Leave it out when there is none; never guess one.
|
|
69
|
+
- **`package`**: `name` exactly as its `package.json` says, `directory` its folder (`.` at the root), and `access` (`restricted` only when `publishConfig.access` or the old publish says so; a scoped package published `public` today stays `public`). Its `package.json` needs a plain `X.Y.Z` version: the flow counts from it. A repository that only publishes has no `workers`, `deployPaths`, `healthCheck`, `beforeDeploy`, or `wranglerEnv`.
|
|
70
|
+
|
|
71
|
+
**A versioning tool with its own scheme** (changesets, semantic-release, release-please) decides versions from commits or changeset files; the release flow takes `package.json`'s version and stages `X.Y.Z-main.N` on `next` for every merge. They can't both run, and one can't be mapped onto the other. Don't replace it: ask the owner with a decision (keep the tool and move only the Worker deploy; or drop the tool for the release flow, which a later task does), and stop the package part until it's answered. Move a Worker deploy in the meantime only if the decision says to.
|
|
72
|
+
|
|
73
|
+
## 4. Render the workflows
|
|
74
|
+
|
|
75
|
+
In the checkout, run `npx breakaway pipeline init`. It checks the config, naming the field that's wrong and what it should be, and the rendered workflows (YAML, pinned actions, expressions, secrets only in an environment), and writes nothing if either fails. It writes `.github/workflows/deploy.yml`, `promote.yml`, `rollback.yml`, and `.github/deploy-paths.json` for `workers`, and `.github/workflows/release.yml` for `package`. Never edit what it writes: change the config and run it again.
|
|
76
|
+
|
|
77
|
+
- **It refuses a file already there** that it didn't render. When that file is the old deploy or publish workflow you're replacing, delete it in this pull request (step 5) and run `pipeline init` again. When it's something else that only shares the name, stop and ask the owner with a decision: never rename or overwrite it silently.
|
|
78
|
+
- **It names helper scripts the repository lacks** (`scripts/record-deployment.mjs` and the others). `repos init <slug> --update` copies them, and that is the owner's: say so under **After merging** (the workflows fail without them), unless they're already there.
|
|
79
|
+
- **`npx breakaway pipeline check`** passes before you hand over: the config is sound, and the files are what it renders now.
|
|
80
|
+
|
|
81
|
+
## 5. Nothing lost, nothing twice
|
|
82
|
+
|
|
83
|
+
Every old job and step ends up in exactly one place: the config, a rendered workflow, kept where it was, or dropped with the reason. The rules:
|
|
84
|
+
|
|
85
|
+
- **Checks are the repository's own.** Never edit or remove a workflow that checks. Deploy and Release wait for them by name.
|
|
86
|
+
- **A workflow that only deploys or only publishes** comes out in the same pull request, deleted, or with its trigger on the default branch removed when it also runs for something else (a tag, by hand, a preview on pull requests, which stays). Otherwise the merge deploys or publishes twice.
|
|
87
|
+
- **A workflow that checks and deploys, or checks and publishes**, is split: its checks stay as they are, and the deploy or publish job or steps come out (with the `needs:`, `if:`, permissions, and `environment` only they used). If its deploy job is all that runs on push to the default branch, drop that trigger too, so the checks still run where they did.
|
|
88
|
+
- **Workers Builds** (Cloudflare's git integration) deploys from Cloudflare, not from a file: you can't turn it off. Say under **After merging** that the owner disconnects it before merging, or the merge deploys twice.
|
|
89
|
+
- **A step the flow can't do** (a manual approval, a custom deploy script `beforeDeploy` can't run, a notification, a cache purge, publishing to another registry) stays exactly where it is, and the pull request lists it under **After merging** for the owner to decide. Never invent an equivalent.
|
|
90
|
+
- **Publishing by hand** (a `release` or `publish` script someone runs from a laptop) stays in `package.json`; the pull request says the release flow replaces it and the owner stops running it.
|
|
91
|
+
- **Never** add or change a secret, an environment, or a variable, and never put a token in a file.
|
|
92
|
+
|
|
93
|
+
## 6. The pull request
|
|
94
|
+
|
|
95
|
+
Open it the way the repository's prompt's **Pull requests** says, closing the task. Its description holds, besides what that asks:
|
|
96
|
+
|
|
97
|
+
- **What moves**: the row from step 2, the Workers, and the package.
|
|
98
|
+
- **Step by step**: a table of every old workflow, job, and step, and where each went:
|
|
99
|
+
|
|
100
|
+
| Old | Was | Now |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `deploy.yml` · deploy | `wrangler deploy` on push to `main` | Deploy (`deploy.yml`, rendered) after CI passes; the old file is removed |
|
|
103
|
+
| `ci.yml` · test | `npm test` | Kept as it is; Deploy and Release wait for CI |
|
|
104
|
+
| `ci.yml` · publish | `npm publish` on a tag | Release (`release.yml`): `next` on every merge, `latest` on the owner's Release; the step is removed |
|
|
105
|
+
| `deploy.yml` · migrate | `wrangler d1 migrations apply` | `beforeDeploy` |
|
|
106
|
+
| `deploy.yml` · notify | posts to chat | Kept where it was (After merging) |
|
|
107
|
+
|
|
108
|
+
- **Deploy paths**: each pattern's parts and what they cover, so the owner can check them.
|
|
109
|
+
- **After merging**: the owner's checklist below, the steps kept where they were, and anything you stopped short of.
|
|
110
|
+
|
|
111
|
+
**The owner's part comes before the merge.** The merge itself runs Deploy and Release once its checks pass, so without the owner's part both fail. Add a `+owner` task for it, filled in like any task, with the checklist in its brief and only what this repository needs. It doesn't depend on the move task (it comes first), and the pull request's **After merging** repeats it under "Before you merge":
|
|
112
|
+
|
|
113
|
+
- the staging and production Workers (or confirm the existing ones), and a Cloudflare API token for each;
|
|
114
|
+
- the GitHub environments `staging` and `production`, each with its token as `CLOUDFLARE_API_TOKEN` and restricted to the default branch, and the repository variable `CLOUDFLARE_ACCOUNT_ID`;
|
|
115
|
+
- read and write on **Actions** for the board's GitHub App on the repository (Promote, Roll back, and Release start workflows);
|
|
116
|
+
- the helper scripts, with `repos init <slug> --update`, when `pipeline init` named any;
|
|
117
|
+
- Workers Builds disconnected, when the repository used it;
|
|
118
|
+
- for a package: the GitHub environment `npm`, restricted to the default branch; on npm, a trusted publisher for `release.yml` and the `npm` environment, or a granular `NPM_TOKEN` in that environment that can't bypass 2FA; and, after each run, approving the staged version on npm with 2FA (`npm stage approve <id>`, or Staged Packages on npmjs.com). The board shows what waits and never approves;
|
|
119
|
+
- optional: the repository variable `DEPLOYS_PAUSED` (`true` stops Promote and Release), and the health-check addresses;
|
|
120
|
+
- after the merge, **Turn on deploys** on the repository's GitHub page, which sets its pipeline from the files.
|
|
121
|
+
|
|
122
|
+
## Before handing over
|
|
123
|
+
|
|
124
|
+
- `npx breakaway pipeline check` passes.
|
|
125
|
+
- The repository's own checks pass, and every workflow you changed still parses and runs the jobs it ran before, minus what moved.
|
|
126
|
+
- Every old step is in the table, and no deploy or publish is left that the merge would run besides Deploy and Release.
|
|
127
|
+
- No secret, token, or address you didn't read in the repository is in a file, the task, or the pull request.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "breakaway",
|
|
3
|
-
"version": "1.4.0-main.
|
|
3
|
+
"version": "1.4.0-main.36",
|
|
4
4
|
"description": "The task board for you and your coding agents: a Cloudflare Worker, its web app, Taskwarrior sync, and the CLI (npx breakaway).",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"prompts/*.md",
|
|
36
36
|
"taskrc",
|
|
37
37
|
".agents/skills/tasks/SKILL.md",
|
|
38
|
+
".agents/skills/pipeline/SKILL.md",
|
|
38
39
|
"template"
|
|
39
40
|
],
|
|
40
41
|
"engines": {
|
package/src/init.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* scripts/tasks.mjs, and the board's first commit to an empty repository, through its GitHub App, is in
|
|
8
8
|
* src/store-init.js, which reads the board's files from src/board-files.json (BOARD_SOURCES).
|
|
9
9
|
*/
|
|
10
|
-
import { SKILL, areaList, routinePrompt } from './prompt.js';
|
|
10
|
+
import { PIPELINE_SKILL, SKILL, areaList, routinePrompt } from './prompt.js';
|
|
11
11
|
import { promptPathOf } from './repos.js';
|
|
12
12
|
|
|
13
13
|
/** This machine's folder for the board, as .taskrc names it, where a caller doesn't say (scripts/tasks/settings.js). */
|
|
@@ -50,7 +50,7 @@ export const RELEASE_ENTRIES = [
|
|
|
50
50
|
/** Copied unchanged: the board's shared core and stub, and the Taskwarrior settings the new .taskrc includes. */
|
|
51
51
|
const COPIED = ['prompts/core.md', 'prompts/stub.md'];
|
|
52
52
|
/** Copied with a change for the repository, or made from the board's: their source is versioned too. */
|
|
53
|
-
const ADAPTED = ['taskrc', 'scripts/task', SKILL];
|
|
53
|
+
const ADAPTED = ['taskrc', 'scripts/task', SKILL, PIPELINE_SKILL];
|
|
54
54
|
/**
|
|
55
55
|
* Where the board's files (the prompts, the shared taskrc) go in another repository. In the board's own checkout they
|
|
56
56
|
* sit at the root, so the paths `read` takes are the board's; what is written keeps this folder.
|
|
@@ -317,7 +317,7 @@ export function agentsMd(repo, board, dir = DEFAULT_DIR) {
|
|
|
317
317
|
|
|
318
318
|
- **Work lives on the task board.** This repository's tasks are in the areas ${areaList(repo)}. Use the \`tasks\` skill (\`${SKILL}\`) and the CLI, \`npx breakaway\` (the \`breakaway\` package on npm), to claim, comment, and hand over. It works in this checkout's repository, so \`list\` and \`next\` show only this repository's tasks. The skill is breakaway's, written for this repository's areas and prompt: where it names breakaway's own files or rules, the board's part applies and the rest doesn't.
|
|
319
319
|
- **Agents started by the board** follow [\`${promptPathOf(repo)}\`](${promptPathOf(repo)}), which starts with the board's core, \`tools/tasks/prompts/core.md\`.
|
|
320
|
-
- **Copied files.** \`tools/tasks/\`, the release helpers in \`scripts/\`, and \`${
|
|
320
|
+
- **Copied files.** \`tools/tasks/\`, the release helpers in \`scripts/\`, \`${SKILL}\`, and \`${PIPELINE_SKILL}\` come from [${board}](https://github.com/${board}). Don't edit them here: change them there. \`${MANIFEST}\` lists every file it copied, and \`repos init --update\` replaces only those: a file it doesn't list is this repository's own, even at a path breakaway copies to. \`.claude/settings.json\` holds the session hooks that show a cloud agent's output on its task, and they run through \`npx\`, so this repository carries no copy of the CLI.
|
|
321
321
|
- **Taskwarrior** (optional): \`scripts/task\`, or plain \`task\` with direnv after \`direnv allow\`, uses the board with this checkout's own \`.task/\` database, in the \`${repo.slug}\` context. \`npx breakaway setup\` connects the machine once.
|
|
322
322
|
- **Changes reach \`${repo.defaultBranch || 'main'}\` through pull requests**, which the owner merges. Never merge, force-push, or rewrite \`${repo.defaultBranch || 'main'}\`.
|
|
323
323
|
- **Never put a secret or token** in a file, task, comment, or pull request. The board's token lives in \`${dir}/tasks.env\` (or \`$BREAKAWAY_HOME/tasks.env\`) or the cloud environment's credentials, never in this repository.
|
|
@@ -472,6 +472,7 @@ export function initPlan({
|
|
|
472
472
|
}
|
|
473
473
|
if (readTarget('.claude/skills') === null) files.push({ path: '.claude/skills', link: '../.agents/skills' });
|
|
474
474
|
copy(SKILL, skillFor(read(SKILL), board, repo));
|
|
475
|
+
copy(PIPELINE_SKILL, skillFor(read(PIPELINE_SKILL), board, repo));
|
|
475
476
|
add('AGENTS.md', agentsMd(repo, board, configDir));
|
|
476
477
|
if (!skipped.includes('AGENTS.md')) todo.push('AGENTS.md: add how to build in this repository');
|
|
477
478
|
|
package/src/prompt.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
/** Where the `tasks` skill sits in a repository the board runs. */
|
|
8
8
|
export const SKILL = '.agents/skills/tasks/SKILL.md';
|
|
9
|
+
/** Where the `pipeline` skill sits: how an agent moves a repository's CI/CD to the deploy flow (BRK-92). */
|
|
10
|
+
export const PIPELINE_SKILL = '.agents/skills/pipeline/SKILL.md';
|
|
9
11
|
|
|
10
12
|
/** The repository's areas for its prompt and AGENTS.md: "product (`BWYP`), cloud (`BWYC`)". */
|
|
11
13
|
export const areaList = (repo) => repo.areas.map((a) => `${a.project} (\`${a.prefix}\`)`).join(', ');
|