Skip to content

Sync repos to current template versions, with three-way merges - #33

Merged
sameeralam3127 merged 2 commits into
mainfrom
feat/sync
Sep 14, 2026
Merged

sameeralam3127 merged 2 commits into
mainfrom
feat/sync

Conversation

@sameeralam3127

Copy link
Copy Markdown
Owner

v0.8: check reported that a repo was behind; nothing could bring it back. Every repo on python-service 1.0.0 has reported behind since 1.1.0 shipped.

On a real python-service 1.0.0 repo, with a committed local edit

$ ansari check payments
Version:  1.0.0 → 1.1.0 (behind)
1 file(s) modified locally:
  helm/payments/values.yaml

$ ansari sync payments --dry-run
python-service  1.0.0 → 1.1.0
  updated      helm/payments/templates/deployment.yaml
  merged       helm/payments/values.yaml
Dry run: nothing written.

$ ansari sync payments
Synced. Review the changes with `git diff`, then commit.

$ ansari check payments
Version:  1.1.0 (current)
1 file(s) modified locally:
  helm/payments/values.yaml      ← the edit survived the merge

How each file is handled

On disk Sync does
untouched replace with the new output
edited three-way merge; overlapping edits get conflict markers + non-zero exit
deleted leave deleted
new in this version add
dropped, untouched / edited remove / keep and untrack

The merge ancestor comes from git history. This build only ships each template's current version, but the manifest recorded the sha256 of what the old version generated — so sync finds that exact blob in the repo's own history and runs git merge-file.

Refusals (nothing written)

  • Per template: edited file with no ancestor in history · new file would land on an untracked one · destination owned by another template · new required variable with no value
  • Whole sync: not a git working tree · uncommitted changes (unless --allow-dirty) · a template this build can't resolve

--pr and --fleet

--pr commits on ansari/sync-<template>-<version>, pushes, and opens a PR via the git and gh CLIs — they hold the credentials, ANSARI holds no token. No PR while anything is refused or conflicted: conflict markers are never committed. --fleet syncs every repo under a directory, one PR each. All of it lives in integrations/github.py, the only code that touches the network; tests swap the runner and never push.

Acceptance test

Upgrades a real python-service 1.0.0 repo: the three 1.0.0 sources are pulled from history into tests/fixtures/, and the test first proves they reproduce all 7 files byte for byte against the golden hashes.

Gates

before after
Tests (scaffold + cli) 236 273
Full suite 270 310
Coverage 97% 97% (sync.py 97%, github.py 100%)
ruff / format --check / mypy --strict clean clean

Docs updated, including two README statements sync made wrong (where the merge ancestor comes from, and what recorded variables are for).

🤖 Generated with Claude Code

sameeralam3127 and others added 2 commits September 14, 2026 23:12
…y merges

`check` reported that a repo was behind; nothing could bring it back. Every repo
on python-service 1.0.0 has reported behind since 1.1.0 shipped, with no way up.

    ansari sync payments --dry-run
    ansari sync --fleet ~/src --pr

For each attached template that is behind, each file is handled by what its
recorded hash says: untouched files are replaced with the new output, edited ones
three-way merged, deleted ones left deleted. Files the new version adds are
written; files it drops are removed if untouched and kept (and untracked) if
edited. Overlapping edits get conflict markers and a non-zero exit.

A three-way merge needs what the old version generated, and this build only
ships each template's current version. The manifest recorded that output's
sha256, so sync finds it in the repo's own git history -- the committed blob with
that hash -- and merges with `git merge-file`. The new version renders with the
variables the repo was scaffolded with; new variables take their defaults, and
schema-1 string values are typed on the way.

A template is refused, with nothing written for it, when an edited file has no
ancestor in history, a new file would land on an untracked one, a destination
belongs to another template, or a newly required variable has no value. The
whole sync refuses a repo that isn't a git working tree, has uncommitted changes
(unless --allow-dirty), or carries a template this build can't resolve. Planning
does all the work, merges included, so --dry-run reports exactly what a real run
would.

--pr commits on ansari/sync-<template>-<version>, pushes, and opens a pull
request through the git and gh CLIs, which already hold the user's credentials,
so ANSARI holds no token. It opens nothing while any template is refused or
conflicted: conflict markers are never committed. --fleet syncs every repo under
a directory, one pull request each. This lives in integrations/, the only code
that touches the network; every command goes through a runner tests replace.

The acceptance test upgrades a real python-service 1.0.0 repo: the three 1.0.0
sources come from history into tests/fixtures, and a test confirms they reproduce
all seven files byte for byte against the golden hashes. After sync, the
deployment carries the autoscaling guard and values.yaml keeps its local edit.

Tests 236 -> 273 (scaffold + cli), 310 in the full suite; coverage 97%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Marks v0.8 done and documents sync: what happens to each file, where the merge
ancestor comes from, the refusals, and --pr. Corrects two statements sync made
wrong: the README said the merge ancestor comes from re-rendering the old template
(it comes from git history), and that the recorded variables exist to reconstruct
the old version (they render the new one). Retires the "manual python-service
1.1.0 upgrade" limitation for the true remaining one, and records that the sync
--pr tripwire can no longer be crossed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pr-reviewer-bots

Copy link
Copy Markdown

Thanks for opening this PR. Assigned to @sameeralam3127. Labels added: documentation, size/extra-large. Checklist: 1/3 passed (see checks tab for details).

@sameeralam3127
sameeralam3127 merged commit 39470e7 into main Sep 14, 2026
6 of 7 checks passed
@sameeralam3127
sameeralam3127 deleted the feat/sync branch September 14, 2026 17:44
@sameeralam3127 sameeralam3127 mentioned this pull request Sep 15, 2026
4 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant