Project

cimas

0.0
No release in over 3 years
see --help
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 2.0
~> 10.0
~> 3.0

Runtime

>= 0
>= 0
 Project Readme

Cimas (“Continuous integration master”)

Gem Version Build Status Pull Requests Commits since latest

Purpose

Cimas handles synchronizing CI configuration across multiple repositories.

When there are many repositories that need to share identical or similar CI (continuous integration) configuration, it is not only cumbersome but error prone to keep these configuration files in sync.

For example, adding new value to the CI test matrix for multiple repositories should not itself be a chore.

Note
This problem can technically be mitigated if we can externalize and consolidate CI configuration outside of data repositories. However, most CI solutions don’t support external configuration (i.e. Travis, GitHub Actions, Appveyor). For GitHub, see: GHA external workflow, GHA calling another action

History

Cimas was built to synchronize CI configuration across the the Metanorma and Relaton repositories.

We had a hard time managing them individually!

Installation

Cimas (and its CLI command, cimas) relies on Git. Please ensure git is installed on your system.

Installing is as easy as:

gem install cimas

Or, by specifying it in your Gemfile if you’re using Bundler.

Concepts

CI configuration file

file to configure CI behavior of a repository

CI configuration master file

CI configuration file that applies to one or more target repositories

CI configuration master directory

directory (or a repo) where master CI configuration files reside

Cimas working area

local directory where Cimas works within

Cimas managed repository

repository with CI configuration managed by Cimas

Demonstration

The metanorma-build-scripts repository contains Cimas configuration under cimas-config/ for the Metanorma repositories.

  • cimas-config/: the CI master configuration directory

  • cimas-config/cimas.yml: the Cimas configuration file

Have a look to see how it’s being used!

Prerequisites

Setting up the CI configuration master directory

You need to first create a “CI configuration master directory” to contain master files for CI configuration. It is advisable to store the directory in Git for better version management.

For example, with Metanorma repositories, the CI master configuration directory is at metanorma/ci under cimas-config/. (Historical note: prior to 2024 this lived in metanorma-build-scripts; older documentation may still reference that location.)

Setting the GITHUB_TOKEN environment variable

Cimas needs a GitHub token to look up per-repo visibility (public vs. private) when the CI configuration file uses visibility-driven template selection (see the metanorma/metanorma-docker template selection in metanorma/ci#347 Option B).

Without the token, cimas sync prints a warning per repo and defaults to private template picks — safe (won’t clobber public repos with private templates), but the sync is inaccurate for public repos.

Set the token before running any sub-command that touches remotes or inspects visibility:

export GITHUB_TOKEN=$(gh auth token)

gh auth token reuses the token the local gh CLI is authenticated with. Alternatively, export an explicit PAT with repo scope.

Usage

Command line

Cimas works through the cimas executable. cimas provides the following sub-commands.

  • setup

  • sync

  • diff

  • push

  • pull

  • open-prs

  • for-each

  • lint (disabled right now)

Global options (must appear before the sub-command name)

  • --dry-run: Skips destructive/remote operations (branches, pushes, PR opens, deletes); prints what would be done instead. Applies to sync, pull, push, open-prs, cleanup-*.

  • -v / --verbose (currently disabled).

Warning

--dry-run is a global option, not a sub-command option. It must appear before the sub-command name, or OptionParser rejects it with invalid option: --dry-run:

  • cimas --dry-run push -r …​ -f …​ -b …​

  • cimas push --dry-run -r …​ -f …​ -b …​

Note

Do not confuse --dry-run with -d. -d is a sub-command option on sync and diff — it stands for --master-path (the CI configuration master directory), not for dry-run. See per-sub-command flag lists below.

cimas setup

The setup sub-command sets up the Cimas working area/directory with Git repos cloned as described in your Cimas configuration file (default: cimas.yml).

This command must be run before any other sub-commands are run, and must be run when the repository addresses change within the Cimas configuration file (it will prompt you).

cimas setup -f {cimas-config-file} -r {cimas-working-area}

# e.g.
# cimas setup -f cimas.yml -r ~/src/cimas-wd
Note
This sub-command is considered “safe” as it does not alter the state of existing Git repositories within the Cimas working area.

cimas pull

The pull sub-command resets all the Git repositories to the original branch key set per-repo.

This command should be run before the cimas sync command to clean up state.

cimas pull -f {cimas-config-file} -r {cimas-working-area}

# e.g.
# cimas pull -f cimas.yml -r ~/src/cimas-wd
Note
This sub-command is considered “destructive” as it resets states of all Git repositories in the Cimas working area.

cimas sync

The sync sub-command places the necessary CI configuration files, as described in the Cimas configuration file, into the desired repositories.

After copying the files, Cimas will also stage the changed files to Git.

This command must be run before the cimas push command.

cimas sync -f {cimas-config-file} -r {cimas-working-area} \
  -d {cimas-master-config-dir}

# e.g.
# cimas sync -f cimas.yml -r ~/src/cimas-wd \
#   -d ~/src/cimas-config
Note
This sub-command is considered “destructive” as it resets states of all Git repositories in the Cimas working area before adding CI configuration files. But you can pass -k argument to not 'drop' state of the repo before sync
Proposed extension — drift-audit / opt-out detection (Gap 3, not yet implemented)
Note
This subsection documents a proposed drift-audit capability, not currently implemented. See metanorma/ci#300 (Gap 3) for the full design discussion. The naming question (separate cimas drift-audit subcommand vs. a cimas sync --drift-audit flag) is part of the open design.

cimas sync today applies templates blindly: if a cimas.yml entry maps a downstream file to a template, the file is overwritten on every sync — even when the downstream repo has deliberately opted out by replacing the template with custom content. The opt-out is silent; the maintainer rediscovers it only when their override gets clobbered.

Concrete example: metanorma/coradoc/.github/workflows/rake.yml was replaced in d91d06d ("fix(ci): replace metanorma reusable workflow with direct rake job") with a direct in-file matrix-test job. Until tonight’s metanorma/ci#318 (the cimas-config/cimas.yml one-liner removing coradoc’s rake.yml mapping), every cimas sync run silently planned to clobber that opt-out. There is no equivalent automation for the next opt-out that gets introduced.

The proposed drift-audit (subcommand or flag, naming TBD) runs before every cimas sync and classifies each planned diff into:

  • Gap 1 signal — diff removes with: block lines absent from any template the repo maps to. Likely indicates a per-repo with: override that the schema extension (above) hasn’t yet been used to capture.

  • Gap 2 signal — structural diff (full jobs: block differs) AND the downstream file references a local ./.github/workflows/ path. Likely a monorepo or other structural fork.

  • Gap 3 signal — downstream file lacks the # Auto-generated by Cimas: Do not edit it manually! header AND the structural diff to the assigned template is large. Likely an opt-out.

  • Category D (benign) — diff adds template-resident lines absent from the downstream file. Standard template-forward drift; no hazard.

The audit reports Gap 1, 2, and 3 signals to the maintainer with the proposed cimas.yml or template edit that would resolve each, and exits non-zero on Gap 1 or Gap 3 signals, blocking cimas push until the maintainer either applies the remediation or explicitly overrides the audit.

The drift-audit serves as a forcing function against the silent-overwrite class of failures — opt-outs and per-repo overrides surface as actionable signals rather than getting clobbered. Naming and exact integration point (separate subcommand vs. flag on cimas sync) are open per `#300’s discussion section.

Until either is implemented, opt-out hygiene is a manual diff-review burden on every sync run.

cimas diff

The diff sub-command provides a consolidated diff output between the repositories in the Cimas working area and remote. This is useful prior to running the cimas push command.

After copying the files, Cimas will also stage the changed files to Git.

This command must be run before the cimas push command.

cimas diff -f {cimas-config-file} -r {cimas-working-area} \
  -d {cimas-master-config-dir}

# e.g.
# cimas diff -f cimas.yml -r ~/src/cimas-wd \
#   -d ~/src/cimas-config
Note
This sub-command is considered “safe” as it does not alter state of Git repositories.

cimas push

The push sub-command:

  • commits the changes made by the sync sub-command in a new branch;

  • pushes the new branch to the first Git remote.

This command must be run before the cimas open-prs command as the branches need to be pushed before pull-requests can be opened against them.

cimas push -f {cimas-config-file} -r {cimas-working-area} \
  -b {new-branch-for-commit} \
  -m {commit-message} \

# e.g.
# cimas push -f cimas.yml -r ~/src/cimas-wd \
#  -b my-new-ci-branch \
#  -m 'My commit message' \
#  [-g {group1,group2,...}]
Note
This sub-command is considered “destructive” as it alters the state of all Git repositories in the Cimas working area by adding commits and branches.

cimas open-prs

The open-prs sub-command:

  • opens Pull Requests for all the specified repositories of the given branch;

  • creates Pull Request Review Requests for the created Pull Requests (if reviewers are set in cimas.yaml:settings or via the -w option);

  • assigns the created Pull Requests to assignees (if assignees are set in cimas.yaml:settings or via the -a option).

Since this command depends on GitHub privileged functionality, you must supply your GitHub Personal Access Token (PAT) via the GITHUB_TOKEN environment variable.

GITHUB_TOKEN=deadbeefdeadbeef; \
cimas open-prs -f {cimas-config-file} -r {cimas-working-area} \
  -b {new-branch-to-pr} \
  -m {pr-message} \

# e.g.
# cimas open-prs -f cimas.yml -r ~/src/cimas-wd \
#   -b my-new-ci-branch \
#   -m 'My pull-request message' \
#   [-w {reviewer1,reviewer2...}] \
#   [-a {assignee1,assignee2...}] \
#   [-g {group1,group2,...}]
Note
This sub-command is considered “destructive” as it alters the state of GitHub repositories by creating pull requests.
--supersede-stale flag (Gap 4, implemented 2026-06-30 in cheaper form)

When cimas open-prs runs against a repo where an earlier cimas-sync wave’s PR is still open (i.e. unmerged from a previous wave), the result without intervention is stacked PRs on the same repo carrying overlapping changes, with no automated relationship between them. The previous wave’s PR (e.g. PR#5 on the cimas-sync-2026-06-15 branch) and the new wave’s PR (e.g. PR#10 on cimas-sync-2026-06-29) coexist independently; the reviewer either merges both (redundant churn, possible conflicts), merges only the latest (`PR#5’s review thread gets orphaned), or freezes (the most common outcome). The pattern bites most on inactive-maintainer repos where PRs can sit for weeks or months before notice.

The --supersede-stale flag (opt-in) implements the cheaper-variant design discussed in metanorma/ci#300 (Gap 4). For each target repo, when set:

  1. Before opening the new PR, lists existing open PRs whose head branch starts with cimas-sync- (i.e. previous wave PRs that never merged).

  2. Prepends a Supersedes #X, #Y from prior cimas-sync waves. line to the new PR’s body, naming the superseded PRs.

  3. After the new PR is opened, labels each superseded PR with superseded-by-#N (where N is the new PR’s number) and posts a comment linking the new PR.

  4. Does NOT auto-close the superseded PRs — the reviewer keeps authority over the close decision.

cimas open-prs --supersede-stale \
  -r ~/src/cimas-wd-2026-06-29 \
  -f cimas.yml \
  -b cimas-sync-2026-06-29 \
  -m "Cimas sync 2026-06-29: ..." \
  --body-file /tmp/wave-body.md \
  -g processor

A future PR may layer a strict-superset diff gate on top (only supersede when the new wave’s diff strictly contains the old PR’s diff, so different-intent waves stay parallel). For now the simpler cheaper-variant is sufficient — the label and comment make the supersede relationship visible and let the reviewer make the final close call.

A more elegant single-branch-per-repo alternative (rebase the existing cimas-sync branch in-place rather than opening new PRs) is noted in metanorma/ci#300 as a possible Phase-B candidate — bigger mental-model shift, defer until the revival settles.

--flatten-stale flag (Gap 4 full, implemented 2026-07-04)

--flatten-stale is the full Gap 4 shape: same detection as --supersede-stale but also auto-closes the superseded PRs. Implies --supersede-stale (setting --flatten-stale alone activates both detection AND auto-close).

Use this when you’re confident the new wave’s content strictly supersedes the older waves' — which is the standard case for cimas-sync waves, since every wave regenerates the same file set from cimas.yml. If the older PR contained content that should be preserved (e.g. a mid-wave hand-edit), rebase that branch elsewhere and reopen before running the new wave.

Behavioural delta vs --supersede-stale:

  • Superseded PRs get labelled superseded-closed-by-#N (instead of superseded-by-#N).

  • The comment on each superseded PR names auto-closure and points at the new PR.

  • Each superseded PR is closed via github_client.close_pull_request.

cimas open-prs --flatten-stale \
  -r ~/src/cimas-wd-2026-06-29 \
  -f cimas.yml \
  -b cimas-sync-2026-06-29 \
  -m "Cimas sync 2026-06-29: ..." \
  --body-file /tmp/wave-body.md \
  -g processor

The strict-superset diff gate mentioned above for --supersede-stale’s follow-up is orthogonal to `--flatten-stale — the flatten variant assumes the wave-regeneration invariant rather than checking it per-PR. If the invariant is broken (e.g. a wave that intentionally covers a narrower subset of files), stick with --supersede-stale and close manually.

cimas for-each

The for-each sub-command allow to execute arbitrary shell command for each repo

There is no strict requirment to use this commant during CI configuration update. You can use it on demand, for example if some CI configuration files was removed

cimas sync -f {cimas-config-file} -r {cimas-working-area} \
  -d {cimas-master-config-dir}

# e.g.
# cimas for-each -f cimas.yml -r ~/src/cimas-wd \
#   -c "git rm .github/workflows/windows.yml"

End-to-end workflow: drift audit → preview → push

The full sequence for a “sync + push a wave of drift corrections” operation is setupsyncdiffpush. Each sub-command has its own required flags, so the full sequence is worth banking in one place.

Prerequisites for this workflow:

  • GITHUB_TOKEN exported (see prerequisites above).

  • A checkout of the CI configuration master directory (e.g. metanorma/ci for Metanorma). Below assume it lives at ../ci/cimas-config/.

  • A fresh work-dir (e.g. ../cimas-wd-YYYY-MM-DD).

Step 1 — create the work-dir.

mkdir ../cimas-wd-2026-07-22

Step 2 — setup: clone every mapped repo into the work-dir.

cimas setup \
  -r ../cimas-wd-2026-07-22 \
  -f ../ci/cimas-config/cimas.yml

Setup does not need -d (it only clones; it doesn’t apply templates).

Step 3 — sync: apply templates to each cloned repo locally.

cimas sync \
  -r ../cimas-wd-2026-07-22 \
  -f ../ci/cimas-config/cimas.yml \
  -d ../ci/cimas-config

Both -r (work-dir), -f (cimas.yml), and -d (master path) are required for sync. Missing -d produces [ERROR] config_master_path not set, aborting.

Step 4 — cimas diff: preview the aggregated diff. This is the primary preview mechanism.

cimas diff \
  -r ../cimas-wd-2026-07-22 \
  -f ../ci/cimas-config/cimas.yml \
  -d ../ci/cimas-config

Same three flags as sync. Prints git diff output per drifted repo. This is the decision surface — read it and decide which classes to propagate, and which should become opt-out entries in cimas.yml. Repos with no drift show no diff at all.

Quick count of repos with actual drift (post-sync, pre-push):

cd ../cimas-wd-2026-07-22 && for d in */; do
  [ -n "$(git -C "$d" status --porcelain 2>/dev/null)" ] && echo "${d%/}"
done | tee /tmp/drifted-repos.txt | wc -l

Useful because cimas push (and cimas --dry-run push) will report work against every mapped repo regardless of drift (see gotcha below); this one-liner isolates the actually-drifted subset.

Note

The -b and -m push options are shown in the cimas push reference above; they are required (-b) / recommended (-m) for real pushes, but not required for a cimas diff preview.

Step 5 — real push (only after diff review).

cimas push \
  -r ../cimas-wd-2026-07-22 \
  -f ../ci/cimas-config/cimas.yml \
  -b cimas-sync-2026-07-22 \
  -m "cimas: sync <date> drift-audit findings"

Creates cimas-sync-2026-07-22 on each mapped repo and pushes it. Does NOT open PRs — that’s cimas open-prs as a separate step.

Important

cimas push provisions the wave branch on every mapped repo, not only on drifted ones (see command.rb:405-410 for the rationale — the next-stage cimas open-prs needs the branch to exist on every repo it might open a PR against). This means:

  • The count of “pushed branches” is much larger than the count of actually-changed repos. A wave with ~95 drifted repos may push ~150+ branches, most of them no-ops.

  • Every branch push notifies watchers on that repo, including no-op branches. Ronald and other maintainers of many repos will receive one notification per repo, not one per real change. This is a design cost worth budgeting for.

  • cimas --dry-run push inherits the same shape — it lists every would-be-pushed branch, including the no-ops. Do not use --dry-run push as your primary preview of “what actually changes”; use cimas diff for that.

Mitigations for future waves:

  • Split by -g GROUP to spread the notification burst across multiple sub-waves and days.

  • File a feature request for a --skip-no-drift option on push if the notification cost becomes prohibitive.

Step 6 — open PRs (optional; only for the drift-having branches).

See the cimas open-prs section above for the full flag list.

Table 1. Required-flag matrix per sub-command
Sub-command -r -f -d -b -m

setup

sync

diff

push

(recommended)

open-prs

(recommended)

Table 2. Gotchas summary
Gotcha Symptom

Missing -d on sync/diff

[ERROR] config_master_path not set, aborting.

Missing -b on push/open-prs

missing argument: Missing -b/--push-branch value

--dry-run placed after sub-command

invalid option: --dry-run (OptionParser::InvalidOption)

Missing GITHUB_TOKEN

[WARNING] Visibility fetch failed …​; defaulting to \`private\ (safer).` — sync is inaccurate for public repos.

--dry-run push count includes no-op branches

Push count much larger than actual drift; use cimas diff to preview real changes.

Configuration

General

Cimas relies on reading a Cimas configuration file (default: cimas.yml) that specifies:

  • repository settings;

  • group settings; and

  • Cimas behavior

This YAML file needs to be in the following structure:

---
settings:
  {option-key}: {option-value}
  ...

repositories:
  {repo-name}:
    remote: {remote-name}
    branch: {branch-name}
    files:
      {CI-file-target-location}: {CI-configuration-master-file-location}
    ...
  ...

groups:
  {group-name}:
    - {repo-name}
    - ...

EXAMPLE: See metanorma/metanorma-build-scripts/cimas-config/cimas.yml for a working configuration.

settings

The settings object specifies run-time configuration. These options are merged with the command-line options, which have higher priority.

Syntax:

settings:
  {option-key}: {option-value}
  ...

The following options are available:

  • reviewers takes an array of GitHub user names as PR reviewers. This is only relevant to the cimas open-prs sub-command.

  • assignees takes an array of GitHub user names as assignees to PRs. This is only relevant to the cimas open-prs sub-command.

EXAMPLE: This example comes from metanorma/metanorma-build-scripts/cimas-config/cimas.yml.

settings:
  reviewers:
    - opoudjis
    - ronaldtse

repositories

The repositories object specifies all Git repositories that are managed by Cimas under this configuration file.

For example, when the cimas setup command is run, all of these repositories will be cloned under the Cimas working area.

Each repository is represented by a key under the repositories object.

  {repo-name-1}:
    remote: {remote-name}
    branch: {branch-name}
    files:
      {CI-file-target-location-1}: {CI-configuration-master-file-location-1}
      {CI-file-target-location-2}: {CI-configuration-master-file-location-2}

These attributes are mandatory for each repository:

  • remote: the remote Git location of this repository (i.e. where git clone can find this repository). SSH and HTTPS paths are supported. Single valued.

  • branch: the source branch and eventual branch to commit to (where a PR should be created against). Single valued.

  • files: composed of key value pairs of the “target file location within the repository” to the “master file location within the configuration master directory”. Multiple files are supported.

Syntax:

repositories:
  {repo-name-1}:
    remote: {remote-name}
    branch: {branch-name}
    files:
      {CI-file-target-location-1}: {CI-configuration-master-file-location-1}
      {CI-file-target-location-2}: {CI-configuration-master-file-location-2}
  {repo-name-2}:
    remote: {remote-name}
    branch: {branch-name}
    files:
      {CI-file-target-location-3}: {CI-configuration-master-file-location-3}
    ...
  ...

EXAMPLE: This example comes from metanorma/metanorma-build-scripts/cimas-config/cimas.yml.

repositories:
  metanorma-model-gb:
    remote: ssh://git@github.com/metanorma/metanorma-model-gb
    branch: master
    files:
      .github/workflows/macos.yml: gh-actions/model/macos.yml
      .github/workflows/ubuntu.yml: gh-actions/model/ubuntu.yml
      .github/workflows/windows.yml: gh-actions/model/windows.yml

Proposed schema extension — per-repo with: rendering (Gap 1, not yet implemented)

Note
This subsection documents a proposed extension to the files: schema, not currently implemented. See metanorma/ci#300 (Gap 1) for the full design discussion, including the concrete renderer change and the first intended user (private-fonts: true for metanorma).

The current files: schema maps a downstream-file path to a template path as a single string. This works for cases where the template is fully self-contained, but breaks when the synced file needs a per-repo value that the shared template can’t anticipate (e.g. metanorma’s `rake.yml needs private-fonts: true, but gh-actions/inkscape/rake.yml has no slot for it).

The proposed extension keeps the existing string form valid (fully backward compatible) and adds an optional map form for entries that need per-repo with: rendering:

  metanorma:
    remote: ssh://git@github.com/metanorma/metanorma
    branch: main
    files:
      # Existing string form — kept verbatim, no per-repo with:
      .rubocop.yml: gh-actions/master/.rubocop.yml
      .github/workflows/release.yml: gh-actions/master/release.yml

      # Proposed map form — template + per-repo with: values
      .github/workflows/rake.yml:
        template: gh-actions/master/rake.yml
        with:
          private-fonts: true

The renderer would route on the value’s type: strings render as today; maps extract template: as the path-to-render and pass with: to ERB as a local variable for injection into the template’s with: block. Templates that wish to support per-repo with: rendering gain an ERB injection block such as:

jobs:
  rake:
    uses: metanorma/ci/.github/workflows/generic-rake.yml@main
<% if defined?(file_with) && file_with.any? -%>
    with:
<% file_with.each do |k, v| -%>
      <%= k %>: <%= v.inspect %>
<% end -%>
<% end -%>
    secrets:
      pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}

Until this lands, per-repo with: overrides have to be either inlined in each downstream file (and clobbered by every cimas-sync) or kept out-of-band as documentation, neither of which is sustainable.

Proposed schema extension — monorepo sub-template family (Gap 2, not yet implemented)

Note
This subsection documents a proposed extension to support monorepo-shaped repositories, not currently implemented. See metanorma/ci#300 (Gap 2) for the full design discussion. The reusable-workflow side of the work — adding monorepo / gem_directory inputs to the relevant metanorma/ci/.github/workflows/*.yml reusable workflows — is a coupled dependency tracked separately and is part of the open design question.

The existing gh-actions/master/ and per-flavour template families (gh-actions/inkscape/, gh-actions/xml2rfc/, gh-actions/graphviz/, gh-actions/libreoffice/) all assume a one-repo-one-gem layout: a single .gemspec at the repo root, one Gemfile, one rakefile. Monorepos that have been adopted across the metanorma org since the templates were written break this assumption.

Concrete example: metanorma/pubid is a monorepo of pubid-* gems under gems/. Its .github/workflows/rake.yml has been forked structurally:

jobs:
  rake:
    uses: ./.github/workflows/generic-rake.yml   # local path, not metanorma/ci
    with:
      monorepo: true
      gem_directory: gems
    secrets:
      pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}

Note the uses: points at a local copy of generic-rake.yml, not at metanorma/ci. That’s because the metanorma/ci reusable workflow doesn’t currently accept monorepo / gem_directory inputs, so the monorepo had to fork the workflow locally to extend it. The cimas template family then has no way to express either the inputs or the structural shape, so monorepos sit outside cimas-sync entirely.

The proposed remediation pairs a new template family with reusable-workflow extensions:

  1. A new cimas-config/gh-actions/monorepo/.yml sub-template family in metanorma/ci, paralleling the existing gh-actions/master/ shape but emitting with: { monorepo: true, gem_directory: <path> } blocks.

  2. Reusable-workflow extensions to metanorma/ci/.github/workflows/generic-rake.yml (and siblings as needed) to accept monorepo / gem_directory inputs and dispatch the matrix correctly per-gem rather than per-repo.

  3. Monorepo repos' cimas.yml entries reference the new template family and (per Gap 1’s proposed schema extension) declare the per-monorepo with: values.

The Gap 1 schema extension above is a prerequisite — without per-repo with: rendering, the new template family can’t express the variable gem_directory values across different monorepos. So Gap 2 layers on top of Gap 1, not independently.

Until this lands, monorepo repos remain outside cimas-sync’s scope and maintain their CI configuration by hand.

groups

Cimas offers “grouping” functionality to allow you to work with groups of repositories. This is useful if your repositories fall into different categories, e.g. repositories for Ruby code vs C code that have different build routines.

There is a default group of all which applies if no group is specified.

Groups under the groups key are collections of repository names. Each group is represented by a key of the group’s name, with names of its repositories as array content.

One repository may belong to multiple groups. Groups have no bearing on what files to synchronize; the files must be specified per repository in the configuration file under the repositories section.

The -g switch in the various commands directly refer to the {group-name} specified in the configuration file.

Syntax:

groups:
  {group-name-1}:
    - {repo-name-1}
    - {repo-name-2}
    ...
  ...

EXAMPLE: This example comes from metanorma/metanorma-build-scripts/cimas-config/cimas.yml.

groups:
  model:
  - metanorma-model-iso
  - metanorma-model-gb
  - metanorma-model-standoc

Patches

In addition to full-file sync via files:, cimas supports in-place line-level edits via a top-level patches: section. A patch is a named regex find/replace that is applied to files already present in each target repo. Patches are typically used for values (like a minimum Ruby version) that live inside per-repo unique files (like `.gemspec`s) and therefore cannot be copied wholesale.

patches:
  ruby_version:
    files:
      - "*.gemspec"
    find: 'spec\.required_ruby_version\s*=.*'
    replace: 'spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")'
    groups: [processor, pubid, model]

Each patch entry supports these keys:

  • files — array of glob patterns, evaluated from each target repo’s root. Use */.ext for recursive matching.

  • find — Ruby regex (as a string). Compiled with Regexp.new.

  • replace — replacement string passed to gsub. Backreferences (\\1, etc.) work.

  • groups — which groups: (defined elsewhere in the config) the patch applies to. A repo receives the patch if it appears in any listed group.

Patches are applied during cimas sync, immediately after the file-copy phase, and the resulting changes are staged via git add so they flow through cimas push and cimas open-prs the same way as file-copy changes.

If a patch’s files glob matches nothing, or the find regex matches no content in a file, cimas logs a warning but does not fail — this makes it safe to leave stale patches in config without blocking other repos.

Unlike file-copy sync, patches do not prepend the "Auto-generated by Cimas" header, since they modify existing files in place rather than replacing them.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/metanorma/cimas. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Code of Conduct

Everyone interacting in the Cimas project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.