techbro 0.0.0-stage → 0.5.0
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/README.md +453 -2
- package/assets/baseline-rules.yml +60 -0
- package/assets/caller-workflow.yml +26 -0
- package/assets/review-action.yml +27 -0
- package/assets/review-config.json +4 -0
- package/assets/review-workflow.yml +443 -0
- package/assets/review.mjs +721 -0
- package/bin/techbro.js +9 -0
- package/package.json +23 -4
- package/src/cli.js +690 -0
- package/src/cluster.js +134 -0
- package/src/config.js +35 -0
- package/src/doctor.js +125 -0
- package/src/github.js +152 -0
- package/src/init.js +167 -0
- package/src/kube-project.js +468 -0
- package/src/kube.js +317 -0
- package/src/managed.js +63 -0
- package/src/project.js +202 -0
- package/src/review.js +165 -0
- package/src/secret.js +80 -0
- package/src/setup.js +91 -0
- package/src/team-workflow.js +106 -0
- package/src/team.js +145 -0
- package/src/templates.js +44 -0
- package/src/upgrade.js +94 -0
- package/templates/_default/.agents/skills/README.md +36 -0
- package/templates/_default/.agents/skills/database-migrations/SKILL.md +72 -0
- package/templates/_default/.agents/skills/frontend-feature/SKILL.md +65 -0
- package/templates/_default/.agents/skills/release-safe-change/SKILL.md +75 -0
- package/templates/_default/.agents/skills/rust-backend-feature/SKILL.md +68 -0
- package/templates/_default/.editorconfig +15 -0
- package/templates/_default/.env.example +9 -0
- package/templates/_default/.github/workflows/ci.yml +60 -0
- package/templates/_default/.nvmrc +1 -0
- package/templates/_default/Cargo.lock +1834 -0
- package/templates/_default/Cargo.toml +3 -0
- package/templates/_default/README.md +257 -0
- package/templates/_default/TEMPLATE.md +29 -0
- package/templates/_default/_dockerignore +8 -0
- package/templates/_default/_gitignore +8 -0
- package/templates/_default/apps/backend/Cargo.toml +22 -0
- package/templates/_default/apps/backend/Dockerfile +34 -0
- package/templates/_default/apps/backend/README.md +13 -0
- package/templates/_default/apps/backend/src/config.rs +111 -0
- package/templates/_default/apps/backend/src/controllers/health.rs +36 -0
- package/templates/_default/apps/backend/src/controllers/items.rs +79 -0
- package/templates/_default/apps/backend/src/controllers/mod.rs +2 -0
- package/templates/_default/apps/backend/src/error.rs +115 -0
- package/templates/_default/apps/backend/src/lib.rs +7 -0
- package/templates/_default/apps/backend/src/main.rs +71 -0
- package/templates/_default/apps/backend/src/models/item.rs +94 -0
- package/templates/_default/apps/backend/src/models/mod.rs +1 -0
- package/templates/_default/apps/backend/src/routes.rs +94 -0
- package/templates/_default/apps/backend/src/state.rs +12 -0
- package/templates/_default/apps/backend/src/views/items.rs +18 -0
- package/templates/_default/apps/backend/src/views/mod.rs +1 -0
- package/templates/_default/apps/backend/tests/api.rs +234 -0
- package/templates/_default/apps/frontend/.env.example +2 -0
- package/templates/_default/apps/frontend/.prettierignore +3 -0
- package/templates/_default/apps/frontend/.prettierrc +1 -0
- package/templates/_default/apps/frontend/Dockerfile +15 -0
- package/templates/_default/apps/frontend/README.md +39 -0
- package/templates/_default/apps/frontend/_dockerignore +3 -0
- package/templates/_default/apps/frontend/_gitignore +24 -0
- package/templates/_default/apps/frontend/components.json +25 -0
- package/templates/_default/apps/frontend/eslint.config.js +37 -0
- package/templates/_default/apps/frontend/index.html +13 -0
- package/templates/_default/apps/frontend/jsconfig.json +7 -0
- package/templates/_default/apps/frontend/nginx.conf +30 -0
- package/templates/_default/apps/frontend/package-lock.json +4338 -0
- package/templates/_default/apps/frontend/package.json +49 -0
- package/templates/_default/apps/frontend/public/favicon.svg +1 -0
- package/templates/_default/apps/frontend/src/App.jsx +11 -0
- package/templates/_default/apps/frontend/src/App.test.jsx +40 -0
- package/templates/_default/apps/frontend/src/api/client.js +50 -0
- package/templates/_default/apps/frontend/src/api/client.test.js +70 -0
- package/templates/_default/apps/frontend/src/api/items.js +26 -0
- package/templates/_default/apps/frontend/src/api/system.js +11 -0
- package/templates/_default/apps/frontend/src/components/ItemList.jsx +20 -0
- package/templates/_default/apps/frontend/src/components/ItemList.test.jsx +18 -0
- package/templates/_default/apps/frontend/src/components/Layout.jsx +35 -0
- package/templates/_default/apps/frontend/src/components/StatusBadge.jsx +22 -0
- package/templates/_default/apps/frontend/src/components/StepList.jsx +19 -0
- package/templates/_default/apps/frontend/src/components/ui/button.jsx +52 -0
- package/templates/_default/apps/frontend/src/components/ui/card.jsx +83 -0
- package/templates/_default/apps/frontend/src/components/ui/input.jsx +19 -0
- package/templates/_default/apps/frontend/src/hooks/useBackendStatus.js +33 -0
- package/templates/_default/apps/frontend/src/hooks/useItems.js +47 -0
- package/templates/_default/apps/frontend/src/index.css +130 -0
- package/templates/_default/apps/frontend/src/lib/config.js +2 -0
- package/templates/_default/apps/frontend/src/lib/utils.js +6 -0
- package/templates/_default/apps/frontend/src/main.jsx +11 -0
- package/templates/_default/apps/frontend/src/models/items.js +24 -0
- package/templates/_default/apps/frontend/src/models/items.test.js +24 -0
- package/templates/_default/apps/frontend/src/pages/ItemsPage.jsx +41 -0
- package/templates/_default/apps/frontend/src/pages/WelcomePage.jsx +156 -0
- package/templates/_default/apps/frontend/src/pages/WelcomePage.test.jsx +55 -0
- package/templates/_default/apps/frontend/src/routes.jsx +17 -0
- package/templates/_default/apps/frontend/src/shadcn.css +655 -0
- package/templates/_default/apps/frontend/src/test/setup.js +1 -0
- package/templates/_default/apps/frontend/vite.config.js +25 -0
- package/templates/_default/database/Dockerfile +9 -0
- package/templates/_default/database/README.md +15 -0
- package/templates/_default/database/migrations/postgres/20261008120000_create_items.sql +5 -0
- package/templates/_default/database/migrations/sqlite/20261008120000_create_items.sql +5 -0
- package/templates/_default/docker-compose.prod.yml +53 -0
- package/templates/_default/docker-compose.yml +69 -0
- package/templates/_default/docs/api/README.md +41 -0
- package/templates/_default/docs/api/openapi.yaml +152 -0
- package/templates/_default/docs/decisions/0001-adopt-engineering-convention.md +13 -0
- package/templates/_default/docs/decisions/README.md +6 -0
- package/templates/_default/docs/deployment.md +46 -0
- package/templates/_default/docs/docker.md +63 -0
- package/templates/_default/docs/getting-started.md +214 -0
- package/templates/_default/package.json +15 -0
- package/templates/_default/rust-toolchain.toml +3 -0
- package/templates/_default/scripts/check +10 -0
- package/templates/_default/scripts/db-migrate +25 -0
- package/templates/_default/scripts/install-hooks +5 -0
- package/templates/_default/scripts/pre-push +3 -0
- package/templates/_default/scripts/setup +26 -0
package/README.md
CHANGED
|
@@ -1,3 +1,454 @@
|
|
|
1
|
-
#
|
|
1
|
+
# techbro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
techbro is a CLI companion for programmers who build with AI assistance ("vibe
|
|
4
|
+
coding"). It helps AI-assisted code meet the team's standards, much like a
|
|
5
|
+
programmer's review would, and keeps the team's conventions in one shared place.
|
|
6
|
+
Zero runtime dependencies; plain ESM JavaScript.
|
|
7
|
+
|
|
8
|
+
## Vision and current scope
|
|
9
|
+
|
|
10
|
+
The goal is broader than reviews: help a programmer start projects the team's
|
|
11
|
+
way, align coding style with the tech team, review work against team standards,
|
|
12
|
+
and eventually push and deploy code safely.
|
|
13
|
+
|
|
14
|
+
Implemented today:
|
|
15
|
+
|
|
16
|
+
- Scaffold projects from the company template library (`.project_templates/` in
|
|
17
|
+
this repository): `project init`, `project list`.
|
|
18
|
+
- Keep shared team state (members and linked projects) in a GitHub repository:
|
|
19
|
+
`team create|join|list|use|upgrade`, `project link`.
|
|
20
|
+
- Dispatch a team-hosted review workflow for a registered project:
|
|
21
|
+
`project review`.
|
|
22
|
+
|
|
23
|
+
- Generate manifests, build in-cluster, deploy, and operate Kubernetes apps with
|
|
24
|
+
explicit cluster/namespace targets: `kube cluster list|add|use`,
|
|
25
|
+
`kube check|status|logs|rollout-status|restart|rollback`,
|
|
26
|
+
`kube generate|build|deploy`. Build and deploy are separate, need `--yes`, and
|
|
27
|
+
were validated end to end on one internal cluster only; there is no built-in
|
|
28
|
+
cluster, so set one up first. See the limits in
|
|
29
|
+
[docs/kubernetes.md](docs/kubernetes.md).
|
|
30
|
+
- Share a cluster with the team: the owner runs `cluster add` once and the
|
|
31
|
+
credential is stored encrypted (with `TECHBRO_PASSPHRASE`) in the team repo;
|
|
32
|
+
members run `cluster use`. Validated live once.
|
|
33
|
+
- Check local setup and missing config or secrets: `doctor [--online]`.
|
|
34
|
+
|
|
35
|
+
Planned, not implemented (no commands exist yet): pushing code through techbro
|
|
36
|
+
and automatically aligning code style with team conventions.
|
|
37
|
+
Do not rely on these until they appear in the command reference below.
|
|
38
|
+
|
|
39
|
+
For a step-by-step walkthrough from install to first review, see
|
|
40
|
+
[docs/onboarding.md](docs/onboarding.md). Upgrading from an older version: see
|
|
41
|
+
[docs/upgrading.md](docs/upgrading.md).
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Node 20 or newer and npm.
|
|
46
|
+
- git (for `project init` repository setup and `project link` origin detection).
|
|
47
|
+
- Rust toolchain (`cargo`, https://rustup.rs) for the setup step of `project init`.
|
|
48
|
+
- `gh` (logged in) or a `GITHUB_TOKEN` for `team` and `project link` commands.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
The repository is private, so the installer must be fetched with a GitHub token.
|
|
53
|
+
Run `gh auth login` once, then:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
curl -fsSL -H "Authorization: Bearer $(gh auth token)" \
|
|
57
|
+
https://raw.githubusercontent.com/mtel-thailand/techbro/main/install.sh | bash
|
|
58
|
+
techbro --version
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Without `gh`, use `-H "Authorization: Bearer $GITHUB_TOKEN"` with a token that has
|
|
62
|
+
`repo` scope. An anonymous `curl` returns 404.
|
|
63
|
+
|
|
64
|
+
The installer downloads this repository from GitHub, bundles the templates, and runs
|
|
65
|
+
`npm install -g`. Running it again reinstalls the latest version and prints
|
|
66
|
+
`updated <old> -> <new>`.
|
|
67
|
+
|
|
68
|
+
| Variable | Default | Effect |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `TECHBRO_SOURCE` | `github` | `github` or `npm` (install `techbro@latest` from the registry) |
|
|
71
|
+
| `TECHBRO_REF` | `main` | git ref to download (github source) |
|
|
72
|
+
| `TECHBRO_REPO` | `mtel-thailand/techbro` | `owner/name` to download |
|
|
73
|
+
| `GITHUB_TOKEN` | `gh auth token` | token for the download |
|
|
74
|
+
|
|
75
|
+
Uninstall: `npm uninstall -g techbro`. Once published to npm,
|
|
76
|
+
`npx techbro@latest project init my-app` works without installing.
|
|
77
|
+
|
|
78
|
+
## Quick start
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# 1. create a team (once per team; needs push access to the GitHub repo)
|
|
82
|
+
techbro team create core --url https://github.com/<owner>/<team-repo>
|
|
83
|
+
|
|
84
|
+
# 2. scaffold a project
|
|
85
|
+
techbro project init my-app
|
|
86
|
+
cd my-app
|
|
87
|
+
|
|
88
|
+
# 3. push my-app to its own GitHub repo, then register it with the team
|
|
89
|
+
git remote add origin https://github.com/<owner>/my-app.git
|
|
90
|
+
techbro project link
|
|
91
|
+
|
|
92
|
+
# 4. run it (two terminals)
|
|
93
|
+
cargo watch -x 'run -p backend' # http://localhost:8080
|
|
94
|
+
npm run dev:frontend # http://localhost:5173
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Teammates run `techbro team join core --url https://github.com/<owner>/<team-repo>`.
|
|
98
|
+
|
|
99
|
+
## Command reference
|
|
100
|
+
|
|
101
|
+
Exit codes: `0` success; `2` no arguments, unknown command, or usage printed
|
|
102
|
+
without a subcommand; `1` any error (message on stderr as `techbro: <message>`).
|
|
103
|
+
|
|
104
|
+
### `techbro project init [dir]`
|
|
105
|
+
|
|
106
|
+
Scaffolds a template into `dir` (default: current directory) and makes it runnable.
|
|
107
|
+
|
|
108
|
+
| Flag | Default | Effect |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `-t, --template <name>` | `_default` | template to scaffold (see `project list`) |
|
|
111
|
+
| `-n, --name <app-name>` | slug of the directory name | app name for `package.json`, `.env.example`, `apps/frontend/.env.example` |
|
|
112
|
+
| `-f, --force` | off | allow a non-empty target directory (files overwritten) |
|
|
113
|
+
| `--no-git` | git on | skip `git init -b main` |
|
|
114
|
+
| `--skip-setup` | setup on | scaffold only; install nothing |
|
|
115
|
+
| `--skip-build` | build on | run setup but skip `cargo build -p backend` |
|
|
116
|
+
|
|
117
|
+
Reads: the template folder. Writes: the copied template, `.techbro.json`, and
|
|
118
|
+
`git init -b main` unless disabled. If `dir` is already inside a git repository,
|
|
119
|
+
git init is skipped and the repository is left as is. Never copied:
|
|
120
|
+
`node_modules`, `target`, `dist`, `.env`, `*.db`, `*.sqlite`, `coverage`,
|
|
121
|
+
`.DS_Store`. Errors: non-empty directory without `--force`, unknown template,
|
|
122
|
+
invalid existing `.techbro.json` (checked before copying).
|
|
123
|
+
|
|
124
|
+
What setup does (7 steps, 6 with `--skip-build`):
|
|
125
|
+
|
|
126
|
+
1. Checks `node`, `npm` and `cargo` are on PATH.
|
|
127
|
+
2. Creates `.env` and `apps/frontend/.env` from the `.env.example` files.
|
|
128
|
+
3. `npm ci` in `apps/frontend` (`npm install` if there is no lockfile).
|
|
129
|
+
4. `cargo fetch --locked`.
|
|
130
|
+
5. Installs `sqlx-cli` and `cargo-watch` with `cargo install` if missing.
|
|
131
|
+
6. `bash scripts/db-migrate sqlite` to create `local.db`.
|
|
132
|
+
7. `cargo build -p backend`.
|
|
133
|
+
|
|
134
|
+
The first run can take several minutes. If a step fails, the scaffold is kept and
|
|
135
|
+
the error shows the command to rerun.
|
|
136
|
+
|
|
137
|
+
### `techbro project link`
|
|
138
|
+
|
|
139
|
+
Registers the current project repo in the current team's `projects.json`.
|
|
140
|
+
|
|
141
|
+
| Flag | Default | Effect |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `-u, --url <repo>` | `git remote get-url origin` in the current directory | project repository |
|
|
144
|
+
| `-n, --name <name>` | `.techbro.json` `name`, else repo name | project name |
|
|
145
|
+
|
|
146
|
+
Checks, in order: a current team exists; the project URL parses; you have read
|
|
147
|
+
access to the project repo; you are listed in the team's `members.json`; the team
|
|
148
|
+
repo has `projects.json`. An entry with the same URL is left alone
|
|
149
|
+
(`already linked`). Otherwise the entry is added by direct write or by issue (see
|
|
150
|
+
Permissions). If `.techbro.json` exists in the current directory, `team` and
|
|
151
|
+
`repo` are set in it (other keys kept); it is never created.
|
|
152
|
+
|
|
153
|
+
Output: `already linked` | `linked <name> (<url>) to team <team>` |
|
|
154
|
+
`link pending: <issueUrl> (the team workflow will add it)`.
|
|
155
|
+
|
|
156
|
+
Review config seeding: when you have push access to the team repo, link also adds
|
|
157
|
+
a `projects.<name>` entry to `.github/techbro-review.json` if (and only if) that
|
|
158
|
+
project has none. Existing entries and all other keys are preserved, never
|
|
159
|
+
overwritten; the write uses the file's current sha, so a concurrent change is
|
|
160
|
+
reported as a warning instead of clobbering it. Recognized projects:
|
|
161
|
+
|
|
162
|
+
- the `_default` template (`.techbro.json` `template` is `_default`, plus
|
|
163
|
+
`package.json` and `Cargo.toml`): `test` = `{ runtime: "rust-node", install:
|
|
164
|
+
"npm --prefix apps/frontend ci", command: "npm run check", workdir: "." }`;
|
|
165
|
+
- otherwise, only if `package.json` defines a `test` script: `npm test` on the
|
|
166
|
+
`node` runtime (`npm ci` install when `package-lock.json` exists).
|
|
167
|
+
|
|
168
|
+
Both get `security: { failOn: "ERROR" }`, `sonar: { mode: "optional" }` and `duplication: { mode: "required", maxPercent: 10 }`. Existing entries are never rewritten; entries without `duplication` inherit the same default remotely and in `--dry-run`. Anything
|
|
169
|
+
else is not guessed: link warns and you must add the entry by hand. Without push
|
|
170
|
+
access (issue flow) link never changes the config; it prints the proposed entry
|
|
171
|
+
and a team admin must add it. Check readiness with `project review --dry-run`.
|
|
172
|
+
|
|
173
|
+
What a review runs: Opengrep security rules and jscpd duplication (required by
|
|
174
|
+
default, 10% max; set `duplication.mode: "off"` to opt out, noted in the report, or
|
|
175
|
+
raise `duplication.maxPercent` for legacy projects, which techbro never does
|
|
176
|
+
automatically); Sonar is optional, not required on the server. A scanner that
|
|
177
|
+
errors fails closed. For the `_default` template the configured test command
|
|
178
|
+
(`npm run check`) covers rustfmt, Clippy, ESLint, Prettier and tests. A non-template
|
|
179
|
+
project with only `npm test` runs just that test script; it is not linted.
|
|
180
|
+
|
|
181
|
+
Review caller: after the registry and config steps, link installs a tiny
|
|
182
|
+
`.github/workflows/techbro-review.yml` in the project repo that calls the team's
|
|
183
|
+
shared workflow (`<team-owner>/<team-repo>/.github/workflows/techbro-review.yml@main`;
|
|
184
|
+
`@main` is intentional so the team updates reviews centrally). It is always
|
|
185
|
+
delivered through a pull request from branch `techbro/review-caller-v3` on the
|
|
186
|
+
project's default branch, never a direct push. Missing or unmodified-old callers
|
|
187
|
+
are added/updated; a customized caller is kept with a warning; an existing
|
|
188
|
+
branch or PR is reused and manual edits on the branch are not overwritten. Without
|
|
189
|
+
push access to the project repo link reports that a project writer must run it.
|
|
190
|
+
Projects outside the team's organization are unsupported (no caller installed).
|
|
191
|
+
The read-only issue flow installs no caller; rerun link after registration.
|
|
192
|
+
|
|
193
|
+
### `techbro project review`
|
|
194
|
+
|
|
195
|
+
Dispatches the team's `techbro-review.yml` workflow for a registered project.
|
|
196
|
+
|
|
197
|
+
| Flag | Default | Effect |
|
|
198
|
+
| --- | --- | --- |
|
|
199
|
+
| `-u, --url <repo>` | origin of the current directory | registered project |
|
|
200
|
+
| `-r, --ref <ref>` | local `HEAD`, else remote default branch | ref resolved to an immutable SHA |
|
|
201
|
+
| `-t, --team <name>` | current team | team to use |
|
|
202
|
+
| `-p, --project <name>` | none | pick a registered project by name |
|
|
203
|
+
| `-w, --wait` | off | wait (30 min max); exit 1 unless the run succeeds |
|
|
204
|
+
| `--dry-run` | off | read-only preflight; dispatches nothing, writes nothing |
|
|
205
|
+
|
|
206
|
+
`--dry-run` verifies team membership, registration, project access and the
|
|
207
|
+
workflow file, then shows the effective checks from `.github/techbro-review.json`
|
|
208
|
+
(test command, `security.failOn` ERROR|WARNING|INFO|none, `sonar.mode`
|
|
209
|
+
optional|required|off) and lists missing setup; exit 1 if anything is missing.
|
|
210
|
+
Sonar token/host cannot be inspected, so they are not validated; the remote run is the authoritative gate. A normal review prints the
|
|
211
|
+
same configuration warnings but still dispatches. With `sonar.mode: optional` an
|
|
212
|
+
unavailable Sonar is skipped with a note; an attempted Sonar run that fails is a
|
|
213
|
+
failure. Example: `techbro project review --dry-run`.
|
|
214
|
+
|
|
215
|
+
Requires team membership, read access to the team repo, write access to the
|
|
216
|
+
project repo, a project registered exactly once in the same organization as the
|
|
217
|
+
team, and the merged caller on the project's default branch. Uncommitted files
|
|
218
|
+
are excluded (warning); the commit must exist on the remote. The project's
|
|
219
|
+
`techbro-review.yml` is dispatched on its default branch with only inputs `sha`
|
|
220
|
+
and `request_id`, matched by run title; the run and the report artifact live in
|
|
221
|
+
the project repo. If the run is not found in time the workflow URL is printed and
|
|
222
|
+
nothing is redispatched. `--dry-run` also checks the caller and the team's
|
|
223
|
+
reusable workflow/action, and warns if the team repo's Actions sharing cannot be
|
|
224
|
+
verified (a team admin must allow organization repositories to use its workflows
|
|
225
|
+
and actions).
|
|
226
|
+
|
|
227
|
+
Setup (no secrets are provisioned by the CLI; no GitHub App or browser login is
|
|
228
|
+
needed): the review token only reads the project's own repository. Optionally set
|
|
229
|
+
`SONAR_TOKEN` (secret) and `SONAR_HOST_URL` (variable) in the project repo; they
|
|
230
|
+
are passed through the caller. Never put secrets in team repo files. A private
|
|
231
|
+
team repo shares its workflow and action with organization repositories only
|
|
232
|
+
after an admin enables that in its Actions settings.
|
|
233
|
+
|
|
234
|
+
Coverage figures are project-controlled (the project's own test commands produce them).
|
|
235
|
+
`--wait` exits non-zero when the run fails or is incomplete, tests are not
|
|
236
|
+
configured, the scanner errors, or the quality gate fails; the report artifact is
|
|
237
|
+
still linked. If the 30 minute `--wait` limit passes, the remote run keeps going;
|
|
238
|
+
the run link is printed so you can follow it.
|
|
239
|
+
|
|
240
|
+
Be aware: the review runs the project's own test commands, i.e. arbitrary source
|
|
241
|
+
code, and anyone who can read the project repo's Actions runs can read the report artifacts. Persistent Sonar is planned; limitations are listed in each
|
|
242
|
+
report, and test commands come from the team review config.
|
|
243
|
+
|
|
244
|
+
### `techbro team upgrade`
|
|
245
|
+
|
|
246
|
+
`techbro team upgrade [--team <name>] [--dry-run]` compares the team repo's
|
|
247
|
+
managed review files with this CLI's versions. Missing files are added, unmodified
|
|
248
|
+
old ones updated, current ones skipped; customized files are kept and a
|
|
249
|
+
`<path>.techbro-new` candidate is added for manual merge. `members.json`,
|
|
250
|
+
`projects.json` and `techbro-team.yml` are never touched. Changes go through a PR
|
|
251
|
+
from branch `techbro/upgrade-v5` against the default branch; an open PR and
|
|
252
|
+
existing branch are reused (never reset). `--dry-run` writes nothing. Seed files (config) that exist are never flagged. If the review workflow, action or
|
|
253
|
+
helper is customized, all three are kept and both get `.techbro-new` candidates.
|
|
254
|
+
Pushing workflow files needs the `workflow` token scope. `team create` also adds missing
|
|
255
|
+
managed files.
|
|
256
|
+
|
|
257
|
+
### `techbro project list`
|
|
258
|
+
|
|
259
|
+
Prints each template name with a description taken from the first `# ` heading of
|
|
260
|
+
its `TEMPLATE.md` (or `README.md`).
|
|
261
|
+
|
|
262
|
+
### `techbro team create <name> --url <repo>`
|
|
263
|
+
|
|
264
|
+
Initializes shared state in a GitHub repo and makes the team current.
|
|
265
|
+
|
|
266
|
+
| Flag | Default | Effect |
|
|
267
|
+
| --- | --- | --- |
|
|
268
|
+
| `-u, --url <repo>` | required | team repository (`https://github.com/o/r`, with `.git` or trailing slash, or `git@github.com:o/r.git`) |
|
|
269
|
+
| `-f, --force` | off | overwrite an existing local team of the same name |
|
|
270
|
+
|
|
271
|
+
`<name>` must match `[a-z0-9-]+`. Requires push access to the repo; otherwise it
|
|
272
|
+
stops with `no write access to <o/r> as <login>` before writing anything. Writes
|
|
273
|
+
to `main`, skipping files that exist:
|
|
274
|
+
|
|
275
|
+
- `members.json` (commit `techbro: initialize team members`). If it already
|
|
276
|
+
exists, owner and members are kept and your login is added if missing
|
|
277
|
+
(`techbro: add <login> to team members`).
|
|
278
|
+
- `projects.json` (`techbro: initialize team projects`); never modified if present.
|
|
279
|
+
- `.github/workflows/techbro-team.yml` (`techbro: add team workflow`).
|
|
280
|
+
|
|
281
|
+
Output: `Repository`, `Owner`, `Members`, `Created`, `Updated`, `Current team`.
|
|
282
|
+
Then the team is saved in the global config as current.
|
|
283
|
+
|
|
284
|
+
### `techbro team join <name> --url <repo>`
|
|
285
|
+
|
|
286
|
+
Same flags and name rules as `team create`. Needs read access (`no read access to
|
|
287
|
+
<o/r> as <login>` otherwise) and an initialized team repo (`team repo is not
|
|
288
|
+
initialized; run team create`). Output: `Repository: <o/r>`, then one of
|
|
289
|
+
`already a member`, `added <login> to members.json`, or
|
|
290
|
+
`membership pending: <issueUrl> (the team workflow will add you)`, then
|
|
291
|
+
`Current team: <name>`.
|
|
292
|
+
|
|
293
|
+
### `techbro team list`
|
|
294
|
+
|
|
295
|
+
One line per saved team: `*` marks the current one, then name and URL.
|
|
296
|
+
|
|
297
|
+
### `techbro team use <name>`
|
|
298
|
+
|
|
299
|
+
Makes a saved team current and prints `Current team: <name>`. Errors on an unknown
|
|
300
|
+
name.
|
|
301
|
+
|
|
302
|
+
### `techbro cluster add|list|use|remove`
|
|
303
|
+
|
|
304
|
+
Team-shared clusters. The team owner runs `cluster add <name> --kubeconfig <file>
|
|
305
|
+
--context <ctx> --yes` once; techbro encrypts only that context with
|
|
306
|
+
`TECHBRO_PASSPHRASE` and commits it under `clusters/` in the team repo. Members
|
|
307
|
+
run `cluster use <name>` to decrypt it into a private techbro-owned kubeconfig.
|
|
308
|
+
See [docs/kubernetes.md](docs/kubernetes.md#team-shared-clusters-techbro-cluster).
|
|
309
|
+
|
|
310
|
+
### `techbro doctor [--online] [--json]`
|
|
311
|
+
|
|
312
|
+
Read-only check of tools, GitHub token, `TECHBRO_PASSPHRASE`, config, team,
|
|
313
|
+
shared cluster kubeconfigs and secret-like tracked files, with a fix per problem.
|
|
314
|
+
Never prints secret values; exits 1 if any check fails.
|
|
315
|
+
|
|
316
|
+
### `techbro --help`, `--version`
|
|
317
|
+
|
|
318
|
+
`techbro --help` (also `-h`, `help`) prints the usage; `techbro project --help` and
|
|
319
|
+
`techbro team --help` print per-group usage. `--version` (`-v`) prints the package
|
|
320
|
+
version. With no arguments the usage is printed and the exit code is `2`.
|
|
321
|
+
|
|
322
|
+
## Files and formats
|
|
323
|
+
|
|
324
|
+
### `.techbro.json` (project root)
|
|
325
|
+
|
|
326
|
+
Written by `project init` unless one already exists (an existing JSON object is
|
|
327
|
+
preserved as is):
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"schemaVersion": 1,
|
|
332
|
+
"name": "my-app",
|
|
333
|
+
"template": "_default",
|
|
334
|
+
"techbroVersion": "0.3.0",
|
|
335
|
+
"config": {}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
`project link` adds `"team": "<team>"` and `"repo": "https://github.com/<o>/<r>"`.
|
|
340
|
+
`config` is reserved and does not affect commands.
|
|
341
|
+
|
|
342
|
+
### `~/.config/techbro/config.json` (global)
|
|
343
|
+
|
|
344
|
+
Set `TECHBRO_CONFIG_DIR` to use `$TECHBRO_CONFIG_DIR/config.json` instead. Missing
|
|
345
|
+
file means an empty config; invalid JSON or a wrong shape is an error naming the
|
|
346
|
+
file. Written atomically (temp file plus rename).
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"schemaVersion": 1,
|
|
351
|
+
"currentTeam": "core",
|
|
352
|
+
"teams": {
|
|
353
|
+
"core": { "url": "https://github.com/<o>/<r>", "owner": "<login>", "addedAt": "<ISO time>" }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### `members.json` (team repo, `main`)
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
{ "schemaVersion": 1, "team": "core", "owner": "<login>", "members": ["<login>"] }
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### `projects.json` (team repo, `main`)
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"schemaVersion": 1,
|
|
369
|
+
"projects": [
|
|
370
|
+
{
|
|
371
|
+
"name": "my-app",
|
|
372
|
+
"url": "https://github.com/<o>/my-app",
|
|
373
|
+
"template": "_default",
|
|
374
|
+
"linkedBy": "<login>",
|
|
375
|
+
"linkedAt": "<ISO time>"
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
`template` is `null` when the project has no `.techbro.json`.
|
|
382
|
+
|
|
383
|
+
### `.github/workflows/techbro-team.yml` (team repo)
|
|
384
|
+
|
|
385
|
+
Created by `team create`. Triggers on issues `opened` and `reopened`; permissions
|
|
386
|
+
`contents: write` and `issues: write`; each job has a 5 minute timeout. Issue
|
|
387
|
+
fields reach the scripts only through environment variables.
|
|
388
|
+
|
|
389
|
+
- Job `join` (label `techbro-join`, body `{"schemaVersion":1,"action":"join","login":"<you>"}`).
|
|
390
|
+
- Job `link` (label `techbro-link`, body `{"schemaVersion":1,"action":"link","login":"<you>","project":{...}}`).
|
|
391
|
+
|
|
392
|
+
Each job: verifies the issue author is a collaborator (read, triage, write,
|
|
393
|
+
maintain or admin; otherwise comments and closes), checks the body `login` equals
|
|
394
|
+
the issue author, updates the JSON file, commits as `github-actions[bot]`, pushes
|
|
395
|
+
to `main`, comments, and closes the issue. If the push fails (protected `main`) it
|
|
396
|
+
comments that the change must be made manually and the job fails. The result is
|
|
397
|
+
asynchronous: the CLI returns `pending` and the change lands when the workflow
|
|
398
|
+
runs. Issues must be enabled on the team repo for this path.
|
|
399
|
+
|
|
400
|
+
## Permissions model
|
|
401
|
+
|
|
402
|
+
Token: `GITHUB_TOKEN` if set, else `gh auth token`; otherwise the command fails
|
|
403
|
+
with `set GITHUB_TOKEN or run gh auth login`. The token is never printed.
|
|
404
|
+
|
|
405
|
+
| Command | GitHub permission needed | Path |
|
|
406
|
+
| --- | --- | --- |
|
|
407
|
+
| `team create` | push on team repo | direct write to `main` only (shared workflow written last) |
|
|
408
|
+
| `team join` | read on team repo | push: direct write to `members.json`; read only: issue `techbro-join` + workflow |
|
|
409
|
+
| `project link` | read on project repo, membership in team, read on team repo | push on team repo: direct write to `projects.json`; read only: issue `techbro-link` + workflow |
|
|
410
|
+
| `project link` (caller) | push on project repo | PR on the project repo, never a direct push |
|
|
411
|
+
| `project review` | membership, read on team repo, write on project repo | dispatch on the project repo |
|
|
412
|
+
| `team list`, `team use` | none (local config only) | none |
|
|
413
|
+
|
|
414
|
+
An open request issue created by you is reused instead of opening a duplicate.
|
|
415
|
+
|
|
416
|
+
## Templates
|
|
417
|
+
|
|
418
|
+
Templates are tracked in `.project_templates/` at the repository root. On `npm pack` /
|
|
419
|
+
`npm publish`, `prepack` (`npm run sync`) copies them into `templates/`
|
|
420
|
+
(gitignored). `.gitignore` and `.dockerignore` are stored as `_gitignore` /
|
|
421
|
+
`_dockerignore` because npm renames `.gitignore` in packages; `init` restores the
|
|
422
|
+
real names. In a repository checkout (no `templates/` folder) the CLI reads
|
|
423
|
+
`./.project_templates` directly, so no sync is needed.
|
|
424
|
+
|
|
425
|
+
To add a template, create `.project_templates/<name>/` with a `TEMPLATE.md` whose
|
|
426
|
+
first line is a `# ` heading. It shows up in `project list` and works with
|
|
427
|
+
`project init --template <name>`.
|
|
428
|
+
|
|
429
|
+
## Troubleshooting
|
|
430
|
+
|
|
431
|
+
- `curl` returns 404 on install: the repo is private; send a token (see Install).
|
|
432
|
+
- `missing on PATH: cargo`: install Rust from https://rustup.rs, or use
|
|
433
|
+
`--skip-setup`.
|
|
434
|
+
- `main may be protected`: the push to `main` was rejected. Allow the token to
|
|
435
|
+
bypass protection, or make the change by hand in a pull request.
|
|
436
|
+
- `issues are disabled on <o/r>`: enable Issues on the team repo, or ask the owner
|
|
437
|
+
to add you.
|
|
438
|
+
- `<login> is not a member of team <name>; run techbro team join`: join first.
|
|
439
|
+
- `no origin remote; pass --url`: add a remote (`git remote add origin <url>`) or
|
|
440
|
+
pass `--url`.
|
|
441
|
+
- `no current team`: run `team create`, `team join` or `team use`.
|
|
442
|
+
- `project init` inside an existing git repository prints "existing repository,
|
|
443
|
+
left as is" and does not run `git init`.
|
|
444
|
+
|
|
445
|
+
## Development
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npm test # node --test, no network
|
|
449
|
+
npm pack --dry-run # check what gets published
|
|
450
|
+
npm publish
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Bump `version` in `package.json` for every release; `techbroVersion` in new
|
|
454
|
+
`.techbro.json` files and `techbro --version` come from it.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: techbro.js.eval
|
|
3
|
+
languages: [javascript, typescript]
|
|
4
|
+
severity: ERROR
|
|
5
|
+
message: eval of dynamic code
|
|
6
|
+
pattern: eval(...)
|
|
7
|
+
- id: techbro.js.new-function
|
|
8
|
+
languages: [javascript, typescript]
|
|
9
|
+
severity: WARNING
|
|
10
|
+
message: dynamic Function constructor
|
|
11
|
+
pattern: new Function(...)
|
|
12
|
+
- id: techbro.py.eval
|
|
13
|
+
languages: [python]
|
|
14
|
+
severity: ERROR
|
|
15
|
+
message: eval of dynamic code
|
|
16
|
+
pattern: eval(...)
|
|
17
|
+
- id: techbro.py.subprocess-shell
|
|
18
|
+
languages: [python]
|
|
19
|
+
severity: WARNING
|
|
20
|
+
message: subprocess with shell=True
|
|
21
|
+
pattern: subprocess.$F(..., shell=True, ...)
|
|
22
|
+
- id: techbro.any.private-key
|
|
23
|
+
languages: [regex]
|
|
24
|
+
severity: ERROR
|
|
25
|
+
message: private key material in source
|
|
26
|
+
pattern-regex: '-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----'
|
|
27
|
+
- id: techbro.any.aws-access-key
|
|
28
|
+
languages: [regex]
|
|
29
|
+
severity: ERROR
|
|
30
|
+
message: AWS access key id in source
|
|
31
|
+
pattern-regex: '\b(AKIA|ASIA)[0-9A-Z]{16}\b'
|
|
32
|
+
- id: techbro.any.github-token
|
|
33
|
+
languages: [regex]
|
|
34
|
+
severity: ERROR
|
|
35
|
+
message: GitHub token in source
|
|
36
|
+
pattern-regex: '\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\b|\bgithub_pat_[A-Za-z0-9_]{50,255}\b'
|
|
37
|
+
- id: techbro.any.slack-token
|
|
38
|
+
languages: [regex]
|
|
39
|
+
severity: ERROR
|
|
40
|
+
message: Slack token in source
|
|
41
|
+
pattern-regex: '\bxox[abprs]-[A-Za-z0-9-]{10,100}\b'
|
|
42
|
+
- id: techbro.rust.shell-command
|
|
43
|
+
languages: [rust]
|
|
44
|
+
severity: ERROR
|
|
45
|
+
message: shell invocation via sh -c
|
|
46
|
+
pattern-either:
|
|
47
|
+
- pattern: Command::new("sh").arg("-c")
|
|
48
|
+
- pattern: Command::new("bash").arg("-c")
|
|
49
|
+
- pattern: Command::new("sh").args(["-c", ...])
|
|
50
|
+
- pattern: Command::new("bash").args(["-c", ...])
|
|
51
|
+
- id: techbro.rust.unsafe-block
|
|
52
|
+
languages: [rust]
|
|
53
|
+
severity: WARNING
|
|
54
|
+
message: unsafe block
|
|
55
|
+
pattern: unsafe { ... }
|
|
56
|
+
- id: techbro.rust.unwrap
|
|
57
|
+
languages: [rust]
|
|
58
|
+
severity: INFO
|
|
59
|
+
message: unwrap on Option/Result
|
|
60
|
+
pattern: $X.unwrap()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: techbro review
|
|
2
|
+
run-name: review ${{ inputs.request_id }}
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
inputs:
|
|
7
|
+
sha:
|
|
8
|
+
description: Commit sha to review (40 hex)
|
|
9
|
+
required: true
|
|
10
|
+
type: string
|
|
11
|
+
request_id:
|
|
12
|
+
description: Request UUID
|
|
13
|
+
required: true
|
|
14
|
+
type: string
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
review:
|
|
21
|
+
uses: __TEAM__/.github/workflows/techbro-review.yml@main
|
|
22
|
+
with:
|
|
23
|
+
sha: ${{ inputs.sha }}
|
|
24
|
+
request_id: ${{ inputs.request_id }}
|
|
25
|
+
secrets:
|
|
26
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: techbro review helper
|
|
2
|
+
description: Stage the trusted techbro helper, rules and team data from this team repository.
|
|
3
|
+
inputs:
|
|
4
|
+
expect-version:
|
|
5
|
+
description: Managed version the calling workflow was rendered for
|
|
6
|
+
required: true
|
|
7
|
+
runs:
|
|
8
|
+
using: composite
|
|
9
|
+
steps:
|
|
10
|
+
- name: Stage trusted files
|
|
11
|
+
shell: bash
|
|
12
|
+
env:
|
|
13
|
+
EXPECT: ${{ inputs.expect-version }}
|
|
14
|
+
run: |
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
a="$GITHUB_ACTION_PATH"
|
|
17
|
+
t="$a/../.."
|
|
18
|
+
v() { head -n1 "$1" | sed -n 's/^[#/]* techbro-managed v\([0-9][0-9]*\) .*/\1/p'; }
|
|
19
|
+
for f in "$a/action.yml" "$t/techbro/review.mjs" "$t/techbro/rules/baseline.yml" "$t/workflows/techbro-review.yml"; do
|
|
20
|
+
[ -f "$f" ] && [ ! -L "$f" ] || { echo "missing or unsafe trusted file" >&2; exit 1; }
|
|
21
|
+
[ "$(v "$f")" = "$EXPECT" ] || { echo "managed version mismatch (expected v$EXPECT); run techbro team upgrade" >&2; exit 1; }
|
|
22
|
+
done
|
|
23
|
+
mkdir -p "$GITHUB_WORKSPACE/ctl" "$GITHUB_WORKSPACE/team"
|
|
24
|
+
cp "$t/techbro/review.mjs" "$GITHUB_WORKSPACE/ctl/review.mjs"
|
|
25
|
+
cp -R "$t/techbro/rules" "$GITHUB_WORKSPACE/ctl/rules"
|
|
26
|
+
cp "$t/../projects.json" "$t/../members.json" "$GITHUB_WORKSPACE/team/"
|
|
27
|
+
if [ -f "$t/techbro-review.json" ]; then cp "$t/techbro-review.json" "$GITHUB_WORKSPACE/team/"; fi
|