Skip to content

fix(cli): make init scaffolds build, add repo to team before push, fix register/secret/status - #9

Merged
Alexanderamiri merged 1 commit into
mainfrom
fix/cli-onboarding-fixes
Oct 31, 2026
Merged

Alexanderamiri merged 1 commit into
mainfrom
fix/cli-onboarding-fixes

Conversation

@Alexanderamiri

Copy link
Copy Markdown
Member

Problem

Findings 2, 5, 13 and 14 from the developer-experience audit:

  • init (2): the scaffolded Dockerfiles ran ./mvnw / ./gradlew / pnpm install --frozen-lockfile / pip install -r requirements.txt, but init never wrote those files, so no scaffold could build. init also pushed to main before telling the user to add the repo to the team, so the broker rejected the first pipeline run. It also prompted for a fixed routing.priority.
  • register-team (5): it never wrote role:, so no member became a GitHub team maintainer able to add repos. It printed a team-repo endpoint that doesn't exist (gh api orgs/javaBin/teams/TEAM/repos -f owner=... -f repo=...). And it wrote description: unquoted, so a : or # broke the YAML.
  • secret (13): the help text sent users to javabin-developer. That role can't reach team parameters: its policy keys on aws:PrincipalTag/team, and no Identity Center user has enterprise.team set (checked in live AWS). Also, a missing name: was a flat error. Dotted repo names are common (2023–2027.javazone.no, java.no), and the pipeline defaults name to the repo name and then rejects it.
  • status (14): it used LoadConfig, not LoadConfigForTeam, so it failed after javabin aws setup unless AWS_PROFILE was set. It also printed costs as $ while team budgets are NOK.

Change

  • cmd/scaffold.go (new): each runtime writes a minimal app that needs no wrapper scripts or lockfiles:

    • java: Maven image + pom.xml + JDK HttpServer
    • kotlin: Gradle image + build.gradle.kts
    • typescript: npm install + tsc
    • python: stdlib http.server + requirements.txt
    • go: net/http + go.mod

    Each app serves GET /health on the chosen port (ENV PORT/EXPOSE set to it) and runs as USER 1000. Nothing writes outside /tmp (python sets PYTHONDONTWRITEBYTECODE). Each also gets a .dockerignore.

  • cmd/init.go: commits locally, then adds the repo to the team with PUT orgs/javaBin/teams/<team>/repos/javaBin/<repo> (gh.AddRepoToTeam), and only then pushes. On 403/404 it stops before pushing and explains why: the user isn't a team maintainer or org owner, or the team doesn't exist yet. It prints the gh api -X PUT ... command for a maintainer and cd <repo> && git push. The priority prompt is gone and app.yaml no longer sets routing.priority.

  • cmd/register.go: teams/<name>.yaml is now written with yaml.v3. The first member gets role: maintainer, and the budget is checked to be a whole number before confirming. The post-PR hint uses the PUT form.

  • cmd/secret.go: the help text points at javabin-team-<team>. When name: is missing, the CLI uses the repo name (from the origin remote, or the directory name), the same as the pipeline. If the name isn't a valid app name (the platform's NAME_RE), it fails with a suggestion, for example name: javazone-no-2027 for 2027.javazone.no.

  • cmd/status.go: uses LoadConfigForTeam(team). Costs print as $12.34 (~130 NOK), using the same 10.5 rate as the platform's shared/constants.py USD_TO_NOK (which the cost reports use too).

  • README and CLAUDE.md are updated to match.

Verification

  • go build ./..., go vet ./... and go test ./... all pass.
  • New tests:
    • scaffold: every build-context COPY source is written by the scaffold, there's no mvnw/gradlew, USER 1000/PORT/EXPOSE are set, and no placeholders are left
    • team-YAML round trip with : and # in the description, plus the maintainer role
    • AddRepoToTeam against httptest (PUT path and body; 403/404 return ErrNoTeamAccess)
    • the recovery message, sanitizeAppName, the defaulted and dotted repo-name cases, and formatCost
  • JAVABIN_SCAFFOLD_DOCKER=1 go test ./cmd -run TestScaffoldImagesServeHealth -count=1 built all 5 images locally and ran each one the way ECS does (--user 1000 --read-only --tmpfs /tmp). GET /health returned 200 for all five. This test is opt-in because it needs Docker and network.
  • Live AWS (read-only): javabin-team-cupcake allows ce:GetCostAndUsage and ecs:ListServices, so status works with the team profile. The javabin-developer policy is ABAC on PrincipalTag/team, and no identity-store user has enterprise.team.

Notes for reviewers

  • Merge order: don't merge before the platform change that derives routing.priority. generate-modules.py on platform main still rejects routing without priority, so until that change merges, apps scaffolded by this CLI fail expand.
  • javaBin/registry README line 24 still has the old team-repo command. It's out of scope for this PR.

🤖 Generated with Claude Code

…x register/secret/status

- init: each runtime writes a minimal app (pom.xml/Gradle/package.json/
  requirements.txt/go.mod + source) that serves /health on the chosen port;
  Dockerfiles need no mvnw/gradlew/lockfile and run as uid 1000 writing only
  /tmp. Adds the repo to the app.yaml team before pushing; on 403/404 stops
  with the maintainer command and git push. No routing.priority.
- register-team: serialise teams/<name>.yaml with yaml.v3, first member is
  role: maintainer, print the PUT form of the team-repo command.
- secret: point at javabin-team-<team>, default a missing name to the repo
  name and explain invalid (dotted) names with a suggestion.
- status: use LoadConfigForTeam and show costs as USD with ~NOK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Alexanderamiri added a commit to javaBin/platform that referenced this pull request Oct 31, 2026
…me, validate compute (#181)

## Problem

- **ALB priority collisions (finding 1).** app-template ships
`routing.priority: 100`, which cupcake (frosting) already holds.
jz-watch run 40079243698 failed at apply with `CreateRule PriorityInUse:
Priority '100' is currently in use`. `routing.login` rules take the
numbers directly below `routing.priority`, so nearby picks collide too.
The docs said to "coordinate via the registry", but the registry has
nothing for priorities, and nothing checked them before apply.
- **`name` default was inconsistent (part of finding 13).** Only the
generator fell back to the repo name. detect.yml emitted `app_name=""`,
the CLI rejects a missing name, and a repo name like `2027.javazone.no`
fails `NAME_RE` anyway.
- **Compute values were not validated (part of finding 14).** An invalid
Fargate cpu/memory pair, port or desired_count only failed at apply.

## Change

- `scripts/generate-modules.py`
- `routing.priority` is optional. When it is left out,
`derived_priority(team, name)` takes sha256 of `team/name` and maps it
to one of 4454 slots of 11 in 1000-50000. The host rule sits at the top
of its slot and up to 10 login rules sit below it, inside the same slot.
The value is printed as a `::notice`. `domain:` no longer needs a
priority.
- An explicit `routing.priority` must be 1-999, so it can never land in
the derived range. Explicit values are passed through unchanged.
- `name` is required, with a clear message. The message for a dotted
name gives an example fix (`2027.javazone.no` becomes e.g.
`javazone-2027`). The `APP_SERVICE` repo-name fallback is removed.
- `compute.cpu` and `compute.memory` must be whole numbers and a valid
Fargate task size; the error lists the memory values allowed for that
cpu. `port` must be 1-65535 and `desired_count` 0 or more.
- New `scripts/check-alb-priorities.py`, run in tf-plan after the plan
when it has changes, using the team's read-only plan credentials
(`ReadOnlyAccess` covers `elbv2:DescribeRules`). It takes every
`aws_lb_listener_rule` the plan creates or moves and compares its
priority with the live rules on that listener.
- Priorities the plan frees in the same run (delete-then-create, or the
app's own rule moving) count as free.
- On a conflict it fails with `::error`, naming the host that holds the
priority and suggesting the lowest free number that leaves 10 free below
it.
- If the plan or the live rules cannot be read, it only warns, because
apply still catches a conflict.
- `detect.yml` fails when `app.yaml` has no `name`.
`expand-terraform.yml` no longer passes `APP_SERVICE`.
- Docs: `docs/app-yaml-reference.md` (name, compute table,
routing.priority, login priorities, domain),
`docs/reusable-workflows.md` and `CLAUDE.md`.

## No rule churn

These are the live rules on `javabin-platform-alb`:443:

| Priority | Host |
|---|---|
| 10 | password |
| 100 | cupcake |
| 101 | cupcake-backend |
| 150 | watch |
| 200 | 2027 |
| 900 | test |

Every app.yaml in the org (cupcake, frosting, jz-watch,
2027.javazone.no, platform-test-app, app-template) sets `name` and an
explicit priority below 1000. Their generated priorities are unchanged;
I checked by generating each app.yaml from GitHub with this branch.

## Verification

- `python3 -m unittest discover -s scripts/tests`: 153 tests OK,
including new tests for name, priority range, derived priority
(stability, range, room for 10 login rules, `domain:`), compute
validation, and `test_check_alb_priorities.py`.
- `python3 -m unittest discover -s terraform/lambda-src/tests`: 219
tests OK.
- Ran the check against the live listener rules (read-only
`describe-rules`):
- priority 100 is reported as held by `cupcake.javazone.no`, with
suggestion 21;
  - the derived priority for cupcake/cupcake-backend (2297) passes.
- `actionlint` on the changed workflows: the only finding is the
existing SC2129 style note in detect.yml.

## Follow-ups (other repos)

- javaBin/app-template#4 drops `priority: 100` from the template
`app.yaml` (merge after this PR).
- javaBin/javabin-cli#9 removes the priority prompt from `javabin init`
(no `priority` written, so new apps take the derived one).
- `docs/getting-started.md` in javaBin/docs still shows `priority: 120`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@Alexanderamiri
Alexanderamiri merged commit 2409ff1 into main Oct 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant