cimas 0.1.4 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 914081d7dffac8d44caecd31568adc36ca7b87d1493cdd90840d3f53e44d5f9d
4
- data.tar.gz: 78d8cf640851b093868598702abd538c9b239f7614eba62525de249b572d4704
3
+ metadata.gz: 11ddc7c90fe0593643ea085df50c9e05133d501aa2e158dc3a125b2dbcb8fdca
4
+ data.tar.gz: dad6f26540e9640d86737f89e3e00c9df3bf372f8521c86cf6e0e0f04d6a7e30
5
5
  SHA512:
6
- metadata.gz: fb099628d653c820358e6e6ad4c4458e96e187dbe7e04613842e434dd12ad322ab92d7352713dbfdb4c2deb1fb036c96ac44714b87c88cf30ca01affdc57b8c4
7
- data.tar.gz: 4fa7ebbe5c44d21a33134571637da703aaca216545ecd6ab441e640c184c231a9e884f74966088d0029457b3896b0c1d602765e028f91867ad896db6cada3626
6
+ metadata.gz: a3bc8620cb6566187a87816f7ccc66ebec1eb8692099cba77d0ee78c70c4376b8652e0d8c837c2af322cf977931af0ba9a64fdb4138a935e3c90a56adfca34dc
7
+ data.tar.gz: 0f5e66b3d7440f699d2d58378be0467deb4b60d4dfb88986b270e506b2a8bde59f23ff24f1744865e0aec942829c19209a772ff8b5784d0c981c36b7a19f207e
@@ -0,0 +1,18 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ name: rake
4
+
5
+ on:
6
+ push:
7
+ branches: [ master, main ]
8
+ tags: [ v* ]
9
+ pull_request:
10
+
11
+ permissions:
12
+ contents: write
13
+
14
+ jobs:
15
+ rake:
16
+ uses: metanorma/ci/.github/workflows/generic-rake.yml@main
17
+ secrets:
18
+ pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
@@ -0,0 +1,31 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ name: release
4
+
5
+ on:
6
+ workflow_dispatch:
7
+ inputs:
8
+ next_version:
9
+ description: |
10
+ Next release version. Possible values: x.y.z, major, minor, patch (or pre|rc|etc).
11
+ Also, you can pass 'skip' to skip 'git tag' and do 'gem push' for the current version
12
+ required: true
13
+ default: 'skip'
14
+ repository_dispatch:
15
+ types: [ do-release ]
16
+
17
+ # Least-privilege ceiling for the called rubygems-release.yml: its release job
18
+ # needs contents:write (git tag push) and id-token:write (OIDC Trusted Publishing).
19
+ permissions:
20
+ contents: write
21
+ id-token: write
22
+
23
+ jobs:
24
+ release:
25
+ uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
26
+ with:
27
+ next_version: ${{ github.event.inputs.next_version }}
28
+ secrets:
29
+ rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
30
+ pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
31
+
data/.gitignore CHANGED
@@ -9,3 +9,6 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # Local working notes (never commit)
14
+ TODO*
data/.rubocop.yml ADDED
@@ -0,0 +1,29 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ inherit_from:
4
+ - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
5
+ # A repo-local `.rubocop_todo.yml`, when one exists, MUST be the last
6
+ # entry: inherit_from is last-wins, so listing it before oss-guides lets
7
+ # the shared config's stricter Metrics/ rules override the todo's
8
+ # per-file grandfathering. Emit the reference only when the todo file
9
+ # actually exists in the repo — this repo has none, and a dangling
10
+ # reference makes every local rubocop run exit 2 with
11
+ # "Configuration file not found".
12
+
13
+ # Rubocop plugins enabled centrally so every metanorma-org gem picks them up
14
+ # on cimas sync — best practice belongs at the shared-template layer, not
15
+ # per-repo. Per ronaldtse feedback on metanorma/ci#332.
16
+ plugins:
17
+ - rubocop-rspec
18
+ - rubocop-performance
19
+ - rubocop-rake
20
+
21
+ # local repo-specific modifications
22
+ # ...
23
+
24
+ AllCops:
25
+ # 3.3 matches the org-wide minimum being pushed via #274 (Raise minimum
26
+ # Ruby version to 3.3 due to EOL of 3.2 on 2026-03-31). Was 3.4 before
27
+ # this commit — 3.4 was above the org's stated minimum and would have
28
+ # applied Rubocop rules that fail-close on gems still targeting 3.3.
29
+ TargetRubyVersion: 3.3
data/CLAUDE.md ADDED
@@ -0,0 +1,56 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What cimas is
6
+
7
+ A CLI gem that fans out CI-configuration synchronization ("waves") across a fleet of GitHub repos: clone all repos listed in a `cimas.yml`, render template files into each repo's working copy, commit drift to a per-wave branch, push, open PRs, then clean up branches/files after waves land or die. Metanorma-org specific; the live fleet configs live in `metanorma/metanorma-build-scripts/cimas-config/`.
8
+
9
+ Development happens on `main`; all changes land via PRs, one clean (squashed) commit each, rebase-merged.
10
+
11
+ ## Commands
12
+
13
+ ```sh
14
+ bundle install # Gemfile is gemspec-driven; Gemfile.lock is untracked
15
+ bundle exec rspec # full suite (rake default == spec)
16
+ bundle exec rspec spec/cimas/repository_spec.rb # one file
17
+ bundle exec rspec spec/cimas/cli/command_spec.rb -e "refuses push" # one example
18
+ ```
19
+
20
+ - Rubocop: **not in the bundle** (not a gemspec dev-dependency) — run the globally installed `rubocop` directly. `.rubocop.yml` inherits a remote riboseinc config and lists plugin gems that may not be installed; it parses fine, but the run reports hundreds of pre-existing style offenses (single quotes vs the org's double-quote policy, 80-col lines) — do not treat it as a merge gate. CI (`.github/workflows/rake.yml` → `metanorma/ci` reusable workflow) runs specs only.
21
+ - `bin/console` works (`bundle exec ruby bin/console`).
22
+ - GitHub-touching code paths need `GITHUB_TOKEN` in the environment; specs do not.
23
+
24
+ ## Architecture
25
+
26
+ Everything routes through two files:
27
+
28
+ - **`exe/cimas`** — entry point only: `require_relative '../lib/cimas'`, `Cimas::Cli::Runner.start(ARGV)`, and a two-tier rescue (`Cimas::Cli::Error`/`Thor::Error` → one clean line, exit 1; anything else → backtrace). All parsing/help lives in `Cimas::Cli::Runner` (Thor).
29
+ - **`lib/cimas/cli/runner.rb`** — `Runner < Thor` is the CLI surface: one definition per flag (`SHARED_OPTIONS` + `STRING_OPTION_KEYS` data; per-command `method_option`), `cimas help [COMMAND]` is the help interface (no `--help` remap), no `default_task` (bare `cimas` prints help; a leading global option would otherwise dispatch the default with the subcommand as an argument), `exit_on_failure?` true (Thor 1.5 exits 0 otherwise). Every task is one line: `run_command("name")` → `Command.new(command_options).execute(name)`. Adding a subcommand = desc + options + one-line action; adding a flag = one entry in the tables.
30
+ - **`lib/cimas/cli/command.rb`** — `Command` is the orchestrator holding the domain: config loading, every subcommand's behavior, ERB rendering, the patch engine. GitHub access goes through the `Cimas::GitHub` seam. Config precedence: `DEFAULT_CONFIG` < cimas.yml `settings:` < Runner-translated options. User-facing errors raise `Cimas::Cli::Error` (lib/cimas/cli/error.rb).
31
+
32
+ Domain models and seams, each loaded via autoload with `__dir__`-absolute paths (see lib/cimas.rb / lib/cimas/cli.rb): `Cimas::Repository` (`repositories:`), `Cimas::Patch` (`patches:` — compiled regex, `matches?`/`apply`), `Cimas::OrphanFiles` (pure orphan-detection logic), `Cimas::WorkingCopy` (the git seam — every git-gem call, rescue, and porcelain parse lives behind ~10 domain verbs like `drift?`, `reset_clean`, `provision via reset_onto/switch_branch`, `push` returning `:pushed/:behind_remote/[:rejected, e]`; subcommands must not call the git gem directly), `Cimas::GitHub` (Octokit boundary: client, remote→slug, visibility fallback), `Cimas::ReleasePreflight` (extracted check-runner). `Cimas::GENERATED_HEADER` / `GENERATED_HEADER_MARKER` are the SSOT for the generated-file header. `repo_by_name` returns a real `nil` for unknown names.
33
+
34
+ ### Dispatch-time scope guard (the key safety mechanism)
35
+
36
+ `Command.execute(command_name)` is the single dispatch entrypoint (the Thor Runner calls it). One `COMMANDS` registry entry per subcommand drives everything: remote-mutating classification (`remote_mutating` / `remote_mutating_if`) applies the scope guard **and** the `Scope for <command>: N repo(s): ...` announcement from the same data; `requires` / `requires_if` fail fast on missing flags (`push` without `-b`/`-m`, `for-each` without `-c`, `release-preflight` without `--repo` exit 1 before any repo iteration). Remote-mutating commands refuse a missing, empty, or zero-resolving `-g`; local-only commands (`sync`, `pull`, `diff`) still default to every repo. **Adding a subcommand = adding one registry entry — nothing else.** Calling a subcommand method directly bypasses guard/validation by design (it protects CLI operators, not library callers).
37
+
38
+ ### The wave lifecycle (order matters)
39
+
40
+ `setup` → `pull` → `sync` → `diff` → `push` → `open-prs` → (`cleanup-merged-prs` | `cleanup-closed-prs` | `cleanup-orphan-files`), plus `for-each` (arbitrary shell per repo) and `release-preflight` (single repo).
41
+
42
+ - **`sync`** copies each `files:` mapping from the config-master directory (`-d`) into the repo working area under `repos_path` (`-r`). `.erb` sources render with an `OpenStruct` binding: legacy per-repo `template: binding:` keys become dot-notation methods; the top-level `with:` hash is exposed as `with_values[...]` (for keys that aren't valid Ruby identifiers). After copying, `apply_patches` runs regex find/replace on in-place files.
43
+ - **Ownership marker**: every synced file gets the two-line `# Auto-generated by Cimas` header from the single managed-file writer (`write_managed` under `copy_file`/`write_rendered`). `cleanup-orphan-files` uses this header (first 500 bytes) as proof cimas owns a file before deleting it; **patches deliberately do not add the header** because they edit existing files.
44
+ - **`push`** skips repos without local drift (`WorkingCopy#drift?`); `open-prs` already tolerates missing/empty remote branches, so no-op pushes are skipped rather than forced.
45
+ - **Scope**: `-g` takes a group name, a bare repo name, or `all`. See "Dispatch-time scope guard" above.
46
+ - **Per-repo iteration**: commands loop via `each_configured_repo` (skips unconfigured names) and `each_target_repo` (also skips repos whose working copy is missing, uniform `skipping <command> for it` message). Use these in new commands — do not hand-roll the skip preamble.
47
+ - **Mutation safety**: every mutating step is wrapped in `dry_run("description") { ... }`, which prints instead of executing under `--dry-run`.
48
+ - **`open-prs`** handles stale prior-wave PRs via `--supersede-stale` (label + comment, reviewer keeps close authority) and `--flatten-stale` (also auto-closes).
49
+
50
+ ### Conventions
51
+
52
+ - Log lines carry severity prefixes (`[ERROR]`, `[WARNING]`, `[INFO]`); `sanity_check` is advisory — it warns and continues.
53
+ - Specs build real temp-dir working areas, real `Command` objects, real `Git.init`/local-bare-remotes (no doubles, no network); `exe_spec` boots the real executable with `RUBYOPT` cleared (installed-gem execution model). GitHub-backed commands inject `config['github'] = Cimas::GitHub.new(client: FakeGitHubClient.new)` (`spec/support/`, never `double()`); release-preflight injects `config['release_preflight_runner']`. `spec/cimas/wave_lifecycle_spec.rb` is the end-to-end money path (sync → diff → push against a local bare remote); when touching git-touching code, run it first.
54
+ - `plans/` holds dated sync-wave planning notes; consult the newest file for in-flight wave context.
55
+ - `TODO*` files are local working notes — git-ignored, never commit them.
56
+ - README.adoc is the authoritative user documentation and includes a "Gotchas summary" table; update it when adding subcommand flags or behavior changes.