IW-399 — Move installation and releases to code.iterative.works #408

Merged
mph merged 34 commits from IW-399 into main 2026-07-26 06:37:54 +00:00
Owner

Integration PR for IW-399 — Move installation and releases from GitHub to code.iterative.works.

All three phases are complete. Ready for final review.

Status

  • Phase 1 — Consumption side: docs + bootstrap repoint (merged via #403)
  • Phase 2 — CI image cutover: fj added to the image, workflows repointed off ghcr.io to code.iterative.works/iterative-works/iw-cli-ci:latest, runner auto-token pull credentials
  • Phase 3 — Release publishing + verify: publishing moved from gh/GitHub Releases to Forgejo REST

Phase 3 summary

  • scripts/forgejo-release.sh (new): shared create-or-clobber Forgejo release publisher over REST (idempotent, fails loudly, never echoes the token).
  • release.yml: versioned + vlatest publish via the REST helper using secrets.FORGEJO_RELEASE_TOKEN; auto-token still does checkout + the vlatest tag force-move.
  • release-prepare.sh (fj pr instead of gh), release-publish.sh (break-glass; SHA-pinning preserved), RELEASE.md (tag-triggered workflow is now primary).

Decision: the runner auto-token cannot publish releases on Forgejo 11.0.8 (403 — no releases scope; the forgejo-actions id -2 user lacks release-unit write on the private org). Publishing uses the FORGEJO_RELEASE_TOKEN Actions secret (maintainer token, write:repository) via REST.

Verification (Tier-2 authenticated E2E)

A throwaway v0.0.0-test tag published a versioned release with the correct asset via the token — green in CI. The E2E surfaced three real release-blockers, all fixed here (each would have killed the first real v0.6.3 release):

  1. rsync missing from the CI image → build exit 127.
  2. scala-cli 1.12.5 couldn't start Bloop cold (its deps purged from the maven-nightlies repo it resolves from) → bumped to 1.15.0.
  3. package-release.sh tarball validation tar | grep -q SIGPIPE-under-pipefail false failure.

Code review (3 reviewers): all acceptance criteria met; four findings addressed (delete all same-named assets on clobber; trigger scoped to v[0-9]*; checkout fetch-depth: 0; RELEASE.md checklist fix).

Gated / follow-up (not blocking this PR)

  • Tier-3 anonymous install (acceptance criterion #2): blocked until a human opens anonymous release-asset read access on the private iterative-works org.
  • The vlatest step was isolated out of the Tier-2 run (to not touch the production rolling release); it is the same validated helper call + a git force-push and gets its first real run on the first genuine release.

See .github/ci-image.md for how the image is built and pushed.

Integration PR for **IW-399 — Move installation and releases from GitHub to code.iterative.works**. All three phases are complete. Ready for final review. ## Status - [x] **Phase 1 — Consumption side**: docs + bootstrap repoint (merged via #403) - [x] **Phase 2 — CI image cutover**: `fj` added to the image, workflows repointed off `ghcr.io` to `code.iterative.works/iterative-works/iw-cli-ci:latest`, runner auto-token pull credentials - [x] **Phase 3 — Release publishing + verify**: publishing moved from `gh`/GitHub Releases to Forgejo REST ## Phase 3 summary - `scripts/forgejo-release.sh` (new): shared create-or-clobber Forgejo release publisher over REST (idempotent, fails loudly, never echoes the token). - `release.yml`: versioned + `vlatest` publish via the REST helper using `secrets.FORGEJO_RELEASE_TOKEN`; auto-token still does checkout + the `vlatest` tag force-move. - `release-prepare.sh` (`fj pr` instead of `gh`), `release-publish.sh` (break-glass; SHA-pinning preserved), `RELEASE.md` (tag-triggered workflow is now primary). **Decision:** the runner auto-token cannot publish releases on Forgejo 11.0.8 (403 — no `releases` scope; the `forgejo-actions` id -2 user lacks release-unit write on the private org). Publishing uses the `FORGEJO_RELEASE_TOKEN` Actions secret (maintainer token, `write:repository`) via REST. ## Verification (Tier-2 authenticated E2E) A throwaway `v0.0.0-test` tag published a versioned release with the correct asset via the token — green in CI. The E2E surfaced **three real release-blockers**, all fixed here (each would have killed the first real `v0.6.3` release): 1. `rsync` missing from the CI image → build exit 127. 2. scala-cli 1.12.5 couldn't start Bloop cold (its deps purged from the `maven-nightlies` repo it resolves from) → bumped to 1.15.0. 3. `package-release.sh` tarball validation `tar | grep -q` SIGPIPE-under-`pipefail` false failure. **Code review** (3 reviewers): all acceptance criteria met; four findings addressed (delete all same-named assets on clobber; trigger scoped to `v[0-9]*`; `checkout fetch-depth: 0`; RELEASE.md checklist fix). ## Gated / follow-up (not blocking this PR) - **Tier-3 anonymous install** (acceptance criterion #2): blocked until a human opens anonymous release-asset read access on the private `iterative-works` org. - The `vlatest` step was isolated out of the Tier-2 run (to not touch the production rolling release); it is the same validated helper call + a git force-push and gets its first real run on the first genuine release. See `.github/ci-image.md` for how the image is built and pushed.
mph added 20 commits 2026-07-14 15:58:11 +00:00
Move installation and releases from GitHub to code.iterative.works

Artifacts:
- analysis.md

🤖 Generated with Claude Code
Probed the live Forgejo instance, registry, and Forgejo/Gitea docs to
resolve research-answerable CLARIFYs and surface one new blocker:

- URL scheme: explicit-tag path is GitHub-identical (verified); the
  /releases/latest/download/ magic path is NOT implemented in Forgejo
- fj release subcommand present locally (no --clobber; view-then-update)
- vlatest tag + rolling "latest" release already exist on Forgejo
- iw-cli-ci is NOT yet on the Forgejo registry (Group E confirmed blocked)
- NEW BLOCKER: iterative-works org is private -> anonymous install
  one-liner 404s; private web download route needs basic auth, not tokens

🤖 Generated with Claude Code
Resolved:
- analysis.md: Public vs credentialed install → make iw-cli public (credential-free)
- analysis.md: Runner release-publishing auth → use Forgejo auto-token, no PAT
- analysis.md: Container-registry cutover scope → build+publish iw-cli-ci to Forgejo, in scope (adds fj to CI image)
- analysis.md: Workflow location → keep under .github/workflows/
- analysis.md: One vs two release paths → tag-triggered workflow canonical, local scripts as break-glass fallback

🤖 Generated with Claude Code
Dashboard review state written during triage (analysis_ready).

🤖 Generated with Claude Code
Group E (CI image cutover) is now in scope and gates Group C, so it
becomes its own phase between consumption and publishing:
- Phase 1: docs + bootstrap (A+B)
- Phase 2: CI image cutover (E) — add fj, build+push to Forgejo
- Phase 3: release publishing + verify (C+D+F)

Total re-estimated 9.5-17h; confidence raised to Medium-High now that
the load-bearing unknowns are resolved.

🤖 Generated with Claude Code
Move installation and releases from GitHub to code.iterative.works

Artifacts:
- tasks.md (3 phases)
- review-state.json (tasks_ready)

🤖 Generated with Claude Code
Issue: IW-399
Phase: 1 - Consumption side — docs + bootstrap repoint

This commit marks the checkpoint for phase 1 context generation.
Use wf-implement IW-399 to return to this point.

🤖 Generated with Claude Code
Consumption side — docs + bootstrap repoint

Artifacts:
- phase-01-tasks.md

🤖 Generated with Claude Code
Co-authored-by: Michal Prihoda <michal@prihoda.net>
Co-committed-by: Michal Prihoda <michal@prihoda.net>
Issue: IW-399
Phase: 2 - CI image cutover

This commit marks the checkpoint for phase 2 context generation.
Use wf-implement IW-399 to return to this point.

🤖 Generated with Claude Code
Issue: IW-399
Phase: 2 - CI image cutover

Add fj (forgejo-cli) to .github/Dockerfile.ci (deps/ COPY convention),
repoint the image source LABEL off github.com, and add fj to the tool
verification RUN. Swap all 7 container.image + credentials blocks
(release.yml x1, ci.yml x6) from ghcr.io + github.actor/GITHUB_TOKEN to
the Forgejo registry image + FORGEJO_REGISTRY_USER/_TOKEN pull secrets.

Code side only. Image build/push to the Forgejo registry and the green
CI-run verification need registry access and are a documented human/infra
handoff (see phase-02-tasks.md + implementation-log.md). The gh release
publishing steps in release.yml are untouched (Phase 3).

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Issue: IW-399
Phase: 2 - CI image cutover

Pull the Forgejo-registry CI image with the runner auto-token
(github.actor / secrets.GITHUB_TOKEN, which resolves to the Forgejo
auto-token) instead of dedicated registry secrets — lowest-setup path,
tried first. Add packages: read to release.yml permissions so the
auto-token can pull the private image (ci.yml already had it).

Docs updated: iw-cli-ci is a container package under the org (no git
repo needed); auto-token decision + scheduled-run caveat + dedicated-secret
fallback recorded in phase-02-tasks.md and implementation-log.md.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(IW-399): document CI image build/push and deps staging
All checks were successful
CI / format (pull_request) Successful in 6s
CI / lint (pull_request) Successful in 5s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m8s
CI / dashboard-build (pull_request) Successful in 54s
CI / test (pull_request) Successful in 8m46s
50374b65a6
Captures how to assemble .github/deps/, build iw-cli-ci from
Dockerfile.ci (context .github), and push to the Forgejo registry
(write:package required). Includes the portable-fj caveat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iw-cli-ci pushed to code.iterative.works; PR #408 green on all six
jobs via the runner auto-token. Handoff checkboxes closed, log updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(IW-399): update review-state to phase_merged
All checks were successful
CI / format (pull_request) Successful in 5s
CI / lint (pull_request) Successful in 5s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m5s
CI / dashboard-build (pull_request) Successful in 50s
CI / test (pull_request) Successful in 8m33s
d107dad189
Issue: IW-399
Phase: 3 - Release publishing + verify

Checkpoint for phase 3 context generation. Groups C+D+F:
release.yml + local scripts gh->fj, RELEASE.md rewrite, E2E verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release publishing + verify

Artifacts:
- phase-03-tasks.md

🤖 Generated with Claude Code
feat(IW-399): move release publishing from gh/GitHub to Forgejo (Phase 3, groups C+D)
All checks were successful
CI / format (pull_request) Successful in 6s
CI / lint (pull_request) Successful in 8s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m22s
CI / dashboard-build (pull_request) Successful in 53s
CI / test (pull_request) Successful in 9m34s
f4581fa594
Rewrite the release production side off GitHub. New shared REST helper
scripts/forgejo-release.sh does create-or-clobber Forgejo release publishing
(idempotent, loud on non-2xx), reused by both the workflow and the local script.

- release.yml: versioned + vlatest publish steps gh -> forgejo-release.sh (REST),
  using the FORGEJO_RELEASE_TOKEN secret. vlatest git tag force-move keeps the
  auto-token. Phase-2 container/credentials block untouched.
- release-prepare.sh: gh pr -> fj pr; drop gh-auth gate; reword PR body.
- release-publish.sh: break-glass fallback via REST; SHA-pin via explicit
  tag-at-MAIN_SHA; github.com URLs -> code.iterative.works.
- RELEASE.md: tag-triggered workflow is the primary path; local publish demoted
  to break-glass; prereq gh -> fj + FORGEJO_RELEASE_TOKEN.

Probe-driven pivot: the runner auto-token 403s on the Forgejo 11.0.8 releases API
(no releases scope; forgejo-actions id -2 lacks release-unit write on the private
org), so publishing requires a maintainer/bot token secret. The no-PAT plan is
superseded; recorded in analysis DECISION + phase-03 context/tasks. Resolved
CLARIFY C1/C2/C3/D1.

🤖 Generated with Claude Code
chore(IW-399): update review-state to implementing
All checks were successful
CI / format (pull_request) Successful in 6s
CI / lint (pull_request) Successful in 7s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m13s
CI / dashboard-build (pull_request) Successful in 52s
CI / test (pull_request) Successful in 10m0s
7f8c403021
package-release.sh:65 uses rsync to stage command *.scala files into the
release tarball. The iw-cli-ci image lacked rsync, so release.yml's build
step failed with exit 127 (rsync: command not found) before publish —
blocking every tag-triggered release. GitHub-hosted ubuntu-latest ships
rsync; this restores that parity. Also asserted in the image verify step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scala-cli 1.12.5's Bloop launcher resolves Bloop's own dependencies from
repo.scala-lang.org/.../maven-nightlies, which has purged the versions it
wants (gigahorse-core_2.12 0.9.3, util-interface 1.11.7, zinc/compiler-bridge
2.12 1.12.0). A cold-cache container can no longer start Bloop, so any fresh
image build fails every scala-cli compile. 1.15.0's Bloop deps are still
present there, so it compiles cold. Verified: real core/ compile (-Werror)
succeeds cold through the maven mirror in the rebuilt image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(IW-399): release packaging false 'missing core/project.scala' error
All checks were successful
CI / format (pull_request) Successful in 6s
CI / lint (pull_request) Successful in 7s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m32s
CI / dashboard-build (pull_request) Successful in 1m0s
CI / test (pull_request) Successful in 9m57s
a0733b83b2
The tarball validation piped tar into 'grep -q'. grep -q exits on first
match, SIGPIPE-killing tar; under 'set -o pipefail' the pipeline then
returns non-zero and the guard fires even though core/project.scala is
present (it sorts early in the listing, so grep matches and exits before
tar finishes). Run tar to completion first, then grep a here-string.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
forgejo-release.sh: clobber now deletes ALL existing assets of a given
name, not just the first — Forgejo does not enforce unique attachment
names, so a duplicate from an interrupted run or a legacy asset would
otherwise never converge to one.

release.yml:
- Scope the tag trigger to v[0-9]* (real version tags) so the rolling
  'vlatest' tag no longer matches. A break-glass 'git push vlatest'
  (maintainer credentials, not the recursion-suppressed auto-token) would
  otherwise retrigger the workflow with VERSION=latest and publish junk.
- checkout fetch-depth: 0 so the workflow_dispatch path's 'git describe
  --tags' can resolve the latest tag (a shallow checkout fetches no tags).

RELEASE.md: correct the checklist — the versioned release carries only
iw-cli-<version>.tar.gz; iw-bootstrap ships on the vlatest release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record Phase 3 (release publishing → Forgejo, groups C+D+F): the rewrite,
the Tier-2 authenticated E2E, the three release-blockers it surfaced
(rsync / scala-cli 1.15.0 / package-release SIGPIPE), and the code-review
findings addressed. Mark Phase 3 done in the tasks index; correct the
'package-release.sh unchanged' acceptance line (an orthogonal SIGPIPE fix
landed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(IW-399): update review-state to all_complete
All checks were successful
CI / format (pull_request) Successful in 6s
CI / lint (pull_request) Successful in 7s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m21s
CI / dashboard-build (pull_request) Successful in 54s
CI / test (pull_request) Successful in 8m15s
ba067be0da
mph changed title from WIP: IW-399 — Move installation and releases to code.iterative.works to IW-399 — Move installation and releases to code.iterative.works 2026-07-26 06:37:35 +00:00
mph merged commit ccef805ed6 into main 2026-07-26 06:37:54 +00:00
Sign in to join this conversation.
No description provided.