Skip to content

Releases

openRepoShape v1.2.0

Choose a tag to compare

@brettheap brettheap released this 23 Oct 11:07
961640c

openRepoShape v1.2.0

v1.2.0 is a minor release, the next after v1.1.1. It adds no command and no
flag. It adds refusals and findings, each under its own code, where a tool
read a path it could not read as absent, ended in a traceback, or said ok
and failed later, after a repository or a leg was made. It fixes how
adopt-project.py check rehearses the split, how the YAML reader takes a
key written twice and a block scalar, how bump-leg.py and family.py bump
fetch, how a holder's make bootstrap says where git's update stopped, and
what the scaffold, setup and both openRepoShape commands refuse before
they act. It changes pinned shape copies, so a project or a family holder
takes those fixes into its own gates by update-shape.py (below). It is
tagged on main.

At main 961640c, on 2026-10-22, 100 pull requests had landed on main
since v1.1.1's commit, 5017830, the last of them these notes (#1155). At
19:54Z, 91 of the v1.2.0 milestone's 91 issues were closed, and 32 merged
pull requests were in it.

What 1.2.0 fixes

  • A path this user cannot read is named, never read as absent. From
    Python 3.14, Path.is_file(), is_dir() and exists() answer False to
    any OSError, so a path this user could not search read as one that was
    not there; up to 3.13 they raise, and the run ended in a traceback. Every
    shipped tool now refuses such a path by name, on every Python, but at
    eleven sites left to #1110 and over a root's own .git (#1146, #1148;
    see the known limits), and a test holds each tool's guards to that rule
    (#934, #976, #975, #958, #956, #951, #955, #970, #1072, #1111, for #430).
  • adopt-project.py check finds what execute would meet. It
    rehearses the split in the work directory, with the source's object
    format and none of this machine's hooks; it names a symlink target no
    system can make (a NUL after its first byte, or over 4095 bytes), a link
    into the clone's .git and a pin's file reached through a kept link; and
    it refuses a hand-edited plan value read as a number or a boolean, each
    before either leg exists (#1031, #965, #1070, #1091, #1016). plan asks
    its flags' own refusals before it clones, and check and execute name
    the same first problem (#963, #1015).
  • The YAML reader refuses a key written twice, and reads a block
    scalar as YAML does or refuses it by its line (#974, #1021). The scaffold
    and adopt-project.py spell a neutral_product_pins: entry one way,
    quoted wherever a stock reader would misread it (#995, #1090).
  • Fetches that answer for a branch. bump-leg.py and family.py bump
    no longer move the mount's own branches, and leave out every refspec that
    can bring a tag, or that puts a branch under another name, through one
    rule both import (#937, #946, #1027, #1068, #1087). A commit only a tag
    reaches is named so, whichever kind of tag reaches it (#988).
  • A holder's make bootstrap fails by name where git's update
    stopped:
    at a gitlink with no url, at a commit a fetch did not bring,
    or where a signal ended git, where it said family bootstrap ok. A token
    reaches no trace, a member's make under make -jN no longer warns of the
    jobserver, and the FAILED line names the member's own exit code past a
    - make, or past || true and a - command (#889, #954, #1045, #1076,
    #1075, #950, #1023, #891). validate-family.py's remedies type as
    printed, in order (#889, #1025, #1069).
  • The scaffold and setup. A push or a seed fetch no longer waits at
    git's username prompt, a login or key the host refuses names its fix, and
    the help says what can still make the re-run wait (#971, #1012, #1101);
    the plan names the tracking branch and, on disk, the visibility (#969); a
    flag passed empty is refused by name (#972, #1010); a Windows device name
    is refused (#1010, #1041); and setup's credential-helper check and its
    step (4) read what the run will meet (#1008, #1022, #949, #1011).
  • Both openRepoShape commands. The one pipx places refuses what the
    bash one refuses wherever it prints a value or hands one to git (#947);
    under the C locale a letter past ASCII is no longer "a control
    character", the regression v1.1.1 named (#986); and the bash --doctor
    checks the ref before it clones (#967).
  • Lines a person pastes. make bootstrap's merge --ff-only,
    --continue, --abort and submodule update lines give a leg's path
    and an upstream as one shell word (#944, #973); the split message names
    each path as plan shows it (#1030); and every remedy that names
    family.py bump names --family-root (#1002).
  • A leg on a branch spelled like the tracking branch. make bootstrap
    leaves a leg whose branch folds to the tracking branch on macOS or
    Windows (Main, or another Unicode form), where it moved the branch
    under HEAD and left the index at the old commit (#948, #984, #1006).
  • CI. The full suite and the Python 3.9 jobs run on ubuntu-24.04
    (#1095), the release's install check runs the doctor over a scaffolded
    root (#945), and every release's notes are held to the one-line install
    (#887, #959).

Install and upgrade

  • The newest release, by the one line:

    curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bash

    It places the bash openRepoShape. The same line again upgrades.
    … | bash -s -- v1.2.0 names this release. openRepoShape --version
    prints openRepoShape (opensoft/openRepoShape @ v1.2.0).

  • From PyPI: pipx install openreposhape==1.2.0 for this release, and
    pipx upgrade openreposhape to upgrade one already installed.
    openRepoShape --version prints
    openRepoShape 1.2.0 (opensoft/openRepoShape @ 961640c681b54d9258e6c9d3eab8c6f46a26008b).
    v1.1.0's notes say where pipx comes
    from, and that the two installers do not replace each other's command.

  • A project's and a holder's shape copies. A copied file reaches no
    project by itself. v1.2.0 changes five of the copies an assembly root
    pins: scripts/validate-pins.py, scripts/validate-manifest.py,
    scripts/bootstrap.py, scripts/repo_shape.py and
    scripts/validate-repository-naming.py. It changes four of the copies a
    family holder pins: scripts/validate-family.py, scripts/bootstrap.py,
    scripts/siblings.py and scripts/repo_shape.py. No Makefile,
    .gitignore, .gitattributes, validate.yml or AGENTS-shape.md
    changes, nor the naming policy.
    From a checkout at the v1.2.0 release commit,
    update-shape.py check --root <path> --at 961640c681b54d9258e6c9d3eab8c6f46a26008b reports
    each as upstream-changed in a root pinned at v1.1.1 that has not
    edited it, and
    update-shape.py apply --root <path> --at 961640c681b54d9258e6c9d3eab8c6f46a26008b --yes --branch shape/update-<sha>
    re-copies and re-pins them, on the person's yes, as AGENTS.md's
    "Updating a project's shape" says. Until then the root's own gates and
    make bootstrap are v1.1.1's.

  • A holder that keeps its own scripts/bootstrap.py. The new
    siblings.py imports member_make_environment (#1023) and untraced
    (#1075) from the holder's bootstrap.py. From a pin at v1.1.1 or
    earlier, bootstrap.py changed upstream, so a kept copy reads both: a
    plain apply refuses update-conflict, and apply --accept-local scripts/bootstrap.py refuses update-accept-local-missing-name, both
    before any write. #1082's gap, where a kept copy reads unchanged and is
    not asked again, reaches only a holder pinned at a main commit between
    the two releases whose bootstrap.py is already v1.2.0's (from
    ff0c72eb up to #1075's 7e00834c). There a plain apply copies the
    new siblings.py, and make siblings, make park and make resume
    then end in ImportError (#1082, open). Merge the upstream's
    bootstrap.py into the kept copy by hand, or take the upstream's bytes;
    commit, and run apply again.

  • A YAML layout the new reader refuses. A project keeps its old
    repo_shape.py until update-shape.py apply, and the old copy reads the
    last of a key written twice without a word, so a project.yaml that
    holds a key twice passes its own validate-manifest.py until then. At
    apply the new copy refuses it, yaml-unparsable with the line of the
    second key, then update-validators-red, then ROLLED BACK. Delete the
    first of the two lines to keep the reading the project has now, and run
    apply again. A block scalar's indentation digit (|2, >-2, |2-)
    ends the same way; with the digit deleted the value reads as before,
    except where the header also holds - or +, or the body holds another
    layout #1021 refuses (#974, #917; #1021, #1004). A holder's family.yaml
    is read by the same reader.

  • The rendered READMEs, and a holder's cd line. A project's or
    holder's README.md is rendered once, when it is made, and
    update-shape.py apply rewrites only a holder's closing Shape: line.
    So a holder made with family.py init --family X --name Y before v1.1.0,
    where X and Y differ, keeps cd Y in its README, while a clone of it
    is the folder X/. Edit that line by hand, to the basename of
    family.yaml's repository:; apply leaves it as it is, by design
    (#921's U097). The comments above visibility: in project.yaml and
    family.yaml reach new roots only, for the same reason (#1102, #913).

  • What a script sees. Runs that passed on v1.1.1 and fail now, among
    them:

    • adopt-project.py plan --project NUL, Con, com1 or Lpt9, exit 2
      adopt-bad-project-name, where it wrote a plan and exited 0, and
      check of such a plan, exit 1 (#1041, #1014); the scaffold over such a
      --project, exit 2 scaffold-bad-project-name, where it made the
      three repositories (#1010, #989);
    • adopt-project.py plan with --org '', --elected-on '' or
      --reference '', exit 2, where it exited 0; the scaffold and
      family.py init with a flag passed empty, such as --id '' or
      --into '', exit...
Read more

openRepoShape v1.1.1

Choose a tag to compare

@brettheap brettheap released this 18 Oct 18:31
5017830

openRepoShape v1.1.1

v1.1.1 is a patch of v1.1.0. It adds no command and no flag. It fixes text
that a terminal acts on reaching it, names that are not ASCII or not UTF-8,
.gitmodules files read otherwise than git reads them, and adoptions and
scaffolds that failed late, after the plan was built or a repository was
made. It changes pinned shape copies, so a project or a family holder
takes those fixes into its own gates by update-shape.py (below). It is
cut from release/v1.1 (#850).

At main 5017830, on 2026-10-18, 70 pull requests had landed on main since
v1.1.0's commit, 190133d. At 14:17Z, 71 of the v1.1.1 milestone's 71
issues were closed, #709 among them, closed as not planned.

What 1.1.1 fixes

  • Text a terminal acts on stays off it. install.sh and the bash
    openRepoShape, the one install.sh places, refuse a
    $OPENREPOSHAPE_BIN_DIR holding a control character, a C1 control
    included, or one of 41 code points a terminal may draw as something
    other than itself (a bidi control, a character of no width, a soft
    hyphen, U+2028, U+2029), before any request and without echoing it
    (#760, #837); where the bytes refuse a name, they read it again in a
    multibyte locale's own charset, so that locale's letters are placed
    (#858). The bash command checks $OPENREPOSHAPE_REPO and
    $OPENREPOSHAPE_REF the same way before --version answers (#866); the
    pipx command does not (see the known limits). validate-pins.py spells
    every name and path it prints (#712), and family.py init's plan rows,
    seven refusals and bump-leg.py's refusals that name the root spell the
    paths they print (#766).
  • Names that are not ASCII, or not UTF-8. The doctor keeps such a
    character where the report can show it, so a NEXT or keep line naming a
    path such as café runs as printed, and escapes a command's value whole
    where it cannot (#785); it finds a member whose .gitmodules section name
    is not UTF-8 (#732). make bootstrap moves back a leg's tracking branch
    whose upstream is not UTF-8 (#710); update-shape.py, family.py and
    bump-leg.py read the branch HEAD is on as git's bytes (#765);
    setup-project.py and a project's bootstrap.py tell each Python child
    the encoding it is read in (#836); and a byte-order mark no longer breaks
    a YAML file or the upstream notice (#855).
  • .gitmodules, read as git reads it. The doctor and both
    validate.yml templates read only the sections, keys and values git
    reads, so the doctor's NEXT lines name no url, and CI no App-token
    repository, that git would not (#733, #741, #786); make siblings and
    the doctor's members row read the same sections, keys and values, but
    still read an unmerged file (#733). A holder's make bootstrap and its
    validate-family.py name a value that git dies on, where bootstrap said
    family bootstrap ok (#768), and bootstrap reads an index or HEAD copy
    with a line git cannot parse up to that line, as git does (#856).
  • adopt-project.py asks before it makes anything. --source, --out,
    --pin-source, the source's default branch and both leg repositories are
    asked about before any leg exists, and refused there (#796, #799, #800,
    #797, #798). check holds a plan to the repository it records (#716),
    the line-ending block governs a copy written through a symlinked
    directory (#807), the recorded answers cover a directory split by hand
    (#801), and the clone plan and check make is removed only while it is
    still the one they made (#803).
  • One reading of legs:, members: and a branch name. A legs: that
    is not a list, or an entry that is not a mapping, is said in one sentence
    by the naming gate, validate-manifest.py, validate-pins.py, the
    doctor, a project's bootstrap.py, bump-leg.py and
    update-shape.py --pr (#714, #832). A holder's members: that is not a
    list, or an entry that is not a mapping, is said in its
    validate-family.py's words by the doctor, family.py and the holder's
    make bootstrap and make siblings (#864). The YAML reader keeps
    Unicode spaces as text, and a branch name holding a character that does
    not print or is white space is refused (#715).
  • Git Bash gets sh's quoting. Under Git Bash, the lines the tools
    quote for the host are quoted for sh, where they were quoted for
    PowerShell (#711).
  • Names GitHub cannot hold. scaffold-project.py,
    adopt-project.py execute, family.py init and setup's plan refuse a
    repository name over 100 characters, an id whose xf-project-<id> topic
    is over 50, and an --org over 39 or one GitHub gives no organisation,
    before any repository is made, where an id over 39 failed after the
    repositories existed; adopt-project.py check finds each of them as
    execute refuses it (#838, #863). A project name typed at
    setup's prompt is checked as --project is, a refusal names the check
    the value failed, and a name too long for GitHub's repository names is
    refused before step (4) (#729, #777).
  • setup-project.py's rehearsal, and a ref git refuses. A run passed
    -- --dry-run makes none of the preflight's offers, so it no longer
    writes git's global config or signs gh in, and --shape-ref is held to
    git's rule part by part (#857), as is a ref given to setup.sh or
    install.sh, and one the bash openRepoShape records, reads from its
    record or checks out for --doctor (#874).
  • A project's make bootstrap. A validator's refusal ends the run
    bootstrap REFUSED, exit 2, where it read as a finding; an upstream
    path, cache or pin it cannot read is no answer, where the run ended in a
    traceback; and a cherry-pick or a revert stopped in a leg whose refs are
    a reftable is left alone, where bootstrap ended it (#764).
  • The doctor. Every git it starts runs with GIT_NO_LAZY_FETCH=1, so
    on a git that reads the variable a read a partial clone cannot answer
    fails rather than fetches (#787). In a root that lost
    scripts/repo_shape.py, the legs and members rows' NEXT puts the file
    back, and the agent-files row's WHY puts it back before it names a gate,
    where each named a gate that dies on the import (#750, #776). Outside a
    shape root, a naming policy in the doctor's own checkout that does not
    load, or cannot be used, is an n/a row on the way in and, in a
    repository, the other way in, not a finding or a traceback (#707).
  • Family holders. More of family.py add's and validate-family.py's
    remedies type as printed in the states they name, for a member under
    another spelling (#761, #762, #839, #840) and for a gitlink outside
    members/ (#878); a holder's make bootstrap no longer prints the
    SHAPE_LEGS_TOKEN advice where git fetched nothing, and tells a
    member's finding, its refusal and make's own failure apart, through a
    member's own nested make too (#839, #884); add, bump and remove
    see a cherry-pick or a revert stopped in a reftable holder (#684); and
    make siblings reports a slot that is not a clone, a link to nothing
    among them and, on Python 3.12 and later, a junction to nothing, and
    places the members after it (#735, #784, #793).
  • CI on a release branch. tests.yml runs on a push to
    release/v<X.Y> as on a push to main, and release.yml's header says
    how a fix is back-ported (#852, for #850).

Install and upgrade

  • The newest release, by the one line:

    curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bash

    It places the bash openRepoShape. The same line again upgrades.
    … | bash -s -- v1.1.1 names this release. openRepoShape --version
    prints openRepoShape (opensoft/openRepoShape @ v1.1.1).

  • From PyPI: pipx install openreposhape==1.1.1 for this release, and
    pipx upgrade openreposhape to upgrade one already installed.
    openRepoShape --version prints
    openRepoShape 1.1.1 (opensoft/openRepoShape @ 50178305e4229275d18e71c2152403b4e9d3004e).
    v1.1.0's notes say where pipx comes
    from, and that the two installers do not replace each other's command.

  • A project's and a holder's shape copies. A copied file reaches no
    project by itself. v1.1.1 changes six of the copies an assembly root
    pins: scripts/validate-pins.py, scripts/validate-manifest.py,
    scripts/bootstrap.py, .github/workflows/validate.yml,
    scripts/repo_shape.py and scripts/validate-repository-naming.py. It
    changes six of the copies a family holder pins:
    scripts/validate-family.py, scripts/bootstrap.py,
    scripts/siblings.py, .github/workflows/validate.yml,
    AGENTS-shape.md and scripts/repo_shape.py. From a checkout at the
    v1.1.1 release commit, update-shape.py check --root <path> reports each
    as upstream-changed in a root pinned at v1.1.0 that has not edited it,
    and
    update-shape.py apply --root <path> --at 50178305e4229275d18e71c2152403b4e9d3004e --yes --branch shape/update-<sha>
    re-copies and re-pins them, on the person's yes, as AGENTS.md's
    "Updating a project's shape" says. Until then the root's own gates and
    make bootstrap are v1.1.0's.

  • The rendered READMEs. A project's or holder's README.md is rendered
    once, when it is made, and update-shape.py apply rewrites only a
    holder's closing Shape: line. So the new sentence on a .gitmodules
    that git refuses, in the root's README and in the holder's (#790), and
    the holder's new sentences on the one spelling of members/ and on its
    folder's name (#738, #822), reach new roots only. An existing holder
    reads the last two in its re-pinned AGENTS-shape.md; #790's sentence
    reaches no existing project or holder at all.

  • What a script sees. Runs that passed on v1.1.0 and fail now, among
    them:

    • a holder's make bootstrap over a .gitmodules va...
Read more

openRepoShape v1.1.0

Choose a tag to compare

@brettheap brettheap released this 17 Oct 21:56
190133d

openRepoShape v1.1.0

v1.1.0 puts openRepoShape on PyPI. pipx install openreposhape is a second
way in, beside the one-line install, and the first on native Windows, where
PowerShell cannot run the one line (#563). This release also carries v1.0.1's
one-line install into main (#564), fixes limits that v1.0.0 listed, and runs
CI on Linux only (#597).

At main 190133d, on 2026-10-17, 123 pull requests had landed on main since
v1.0.0's commit, bfed143; at 21:55Z, 133 of the v1.1.0 milestone's 133
issues were closed.

What 1.1.0 adds

  • openRepoShape on PyPI, as openreposhape (#590, #599, #563). One
    command installs it:

    pipx install openreposhape

    It places five commands. openRepoShape has the launcher's modes,
    <Project>, --preflight, --doctor, --version and --help, written
    again in Python, so native Windows gets the command, as an .exe.
    scaffold-project, adopt-project, update-shape and shape-doctor each
    run the tool of that name with every argument unchanged, so docs/cli.md
    is their reference. git-filter-repo 2.47.0 comes as their dependency,
    and each command puts it first on the PATH of the tools it starts.

    The wheel is thin. It carries the release's commit and the
    sorted-ls-tree-r-v1 digest of its tree, and the tools run from a full
    clone of this repository at that commit, made once in your per-user cache
    and kept. Before each run the clone must be at that commit, unchanged, and
    digest to that value, so a project scaffolded through it pins exactly this
    release. $OPENREPOSHAPE_REF names another ref for one run, with no digest
    checked. The package's --install refuses and names
    pipx upgrade openreposhape. Python 3.9 or newer.

    The version is the tag's, so 1.1.0 is the first version on PyPI. The
    v1.0.x line is not published there.

  • The one-line install, on main (#564, carried into main by #587):

    curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bash

    It installs the newest release's bash openRepoShape, pinned to the
    release tag, and ends with openRepoShape --preflight, as
    v1.0.1's notes say. Since v1.0.1:

    • it installs git-filter-repo with uv where there is no pipx, and never
      with pip install --user, which an externally managed Python refuses
      (PEP 668). With neither, it prints one command for this machine: brew's,
      pipx's from apt or dnf, pacman's, or uv's (#598, #595);
    • it ends with two lines, adoption: and then done:, so a machine that
      is ready to scaffold but not to adopt is told so (#598, #595);
    • Git Bash, the bash that Git for Windows installs, runs it and the
      openRepoShape it places, tested in CI but for a typed yes, which the
      preflight cannot take there (#596, #585);
    • it refuses a target that is a symbolic link, such as the
      openRepoShape pipx places, and so does the launcher's --install
      (#563).
  • CI on Linux only (#597). Brett Heap decided on 2026-10-15 to drop the
    macOS and Windows test jobs across the estate. tests.yml runs the whole
    suite on ubuntu-latest, on every push to main, every pull request and
    every dispatch. Since #593 each push run on main is grouped by its own
    commit, so the next landing no longer cancels it, and every landing keeps
    its own Linux result. One exception, on his word the same day: a
    tests-git-bash job runs the launcher's and install.sh's suites, and
    nothing else, on Windows under Git Bash (#596, #585). The rest of the
    suite no longer exercises Windows file-system behaviour, and the stock
    macOS bash 3.2 is a goal the bash entry points are written for rather
    than a checked claim. The release workflow still
    installs the package on Linux, macOS and Windows before it reaches PyPI.

Install and upgrade

  • The newest release, by the one line: the line above. The same line
    again upgrades. … | bash -s -- v1.1.0 names this release.
    openRepoShape --version prints openRepoShape (opensoft/openRepoShape @ v1.1.0).
  • From PyPI: pipx install openreposhape, or
    pipx install openreposhape==1.1.0 for this release.
    pipx upgrade openreposhape upgrades it. pipx itself comes from
    sudo apt-get install -y pipx on Debian and Ubuntu and
    brew install pipx on macOS; on Windows, python -m pip install --user pipx,
    then python -m pipx ensurepath and a new terminal.
    openRepoShape --version prints
    openRepoShape 1.1.0 (opensoft/openRepoShape @ 190133d5e704e380c430f87ca9c4e31746de0012), then the
    version, tag, commit, digest and the clone it would use.
  • One installer or the other. Both place ~/.local/bin/openRepoShape.
    pipx does not replace a command it did not place, and install.sh and the
    launcher's --install refuse the link pipx places, naming
    pipx upgrade openreposhape or removing the link.
  • Native Windows: pipx install openreposhape, then
    openRepoShape <Project> --org <your-org>. Downloading setup-project.py
    and running py setup-project.py <Project> --org <your-org> still works,
    and installs nothing. Git Bash runs the one line and the bash
    openRepoShape it places, tested in CI but for a typed yes, which the
    preflight cannot take there (#596, #585).
  • A project's pin on the standard works as before. Follow
    How to pin 1.0.0 with the v1.1.0 release
    commit. A scaffold run through the package pins that commit and digest by
    itself.
  • A family holder made before #642. A holder made with
    --family X --name Y before #642 (the fix for #629) keeps cd Y in its
    own README, and update-shape.py apply never re-renders a README, so
    change that one line by hand to cd X: the holder's directory is named
    after its repository, X, never after the display name. The same README
    draws its layout as Y/Y and calls the doubled Y/Y deliberate; read
    X/X there, which is what make siblings checks the folder against.

What changed since v1.0.1

v1.0.1 was cut from release/v1.0 and added only the one-line install.
Everything below landed on main after v1.0.0's commit, by area. The list
runs to main 6f64eea.

adopt-project.py

  • The source's origin, read one way. plan reads <org>/<repo> from
    every origin git accepts, a local clone or mirror path and a URL ending
    / included, so a local clone's right --org is no longer refused
    (#591, closing #521 and #427).
  • Refused before any leg exists. check finds, and execute refuses
    before any leg exists, a file the root keeps at the shape's own pin path
    and a --work-dir the user cannot write in. Both used to fail only after
    both legs were pushed (#581, #524). check recomputes each neutral pin's
    digest at the plan's commit, in the clone plan read it from, where that
    clone is still on the machine and is no partial clone, so a commit or a
    digest edited into the plan no longer reaches the root as
    adoption verified over a red validate-pins.py (#589, #449). Before any
    leg too: the branch names execute failed on, such as
    adopt_branch: a..b, a source branch Adopt or one hidden by hideRefs
    (#583, #520, #420); a kept nested .gitattributes that outranks the
    root's line-ending rule for a pinned copy, and a root .gitattributes
    that git add would re-encode (#619, #530); and a kept submodule url on
    this disk where git ls-remote reaches no repository, which used to end
    adoption verified over a recursive clone that fails (#618, #418).
  • Names outside ASCII. execute hands git rm each path the split
    moves or drops as the bytes the tree holds, so such a name under
    LC_ALL=C PYTHONUTF8=0 no longer stops the run with UnicodeEncodeError
    after both legs are pushed (#614, #527).
  • The work directory. A run without --work-dir removes the work
    directory it made where it holds no file, and execute names the one it
    keeps. check and execute clone the plan's source.repository again
    where the clone it records is gone, so neither leaves an empty
    openreposhape-adopt-* directory in the system temp folder, nor refuses
    source-unclonable once that folder is cleaned (#594, #526).
  • Answers kept. execute records the plan's answers under
    answered_paths:, and a moved submodule names its row, so an answered
    examples/ is not asked again, and the doctor names no plan for a
    deliberate move (#617, #525).
  • Leg paths that are not the defaults. The Makefile block execute
    appends, and the cross-leg follow-up, name each leg where the plan mounts
    it, so --spec-path legs/spec exports a CONTRACTS_DIR that exists and a
    nested --code-path gets the right ../ path (#572, #522).
  • A leg path edited by hand. The read-across follow-up names
    legs.code_path and legs.spec_path and no path, and the contracts/
    rule's reason names none, so the hand edit of a leg's mount that plan
    asks for leaves no plan-time path in check, the split commit or the pull
    request (#628, #600). A plan written before v1.1.0 whose legs.spec_path
    or legs.code_path was edited by hand keeps its old value-form
    read-across follow-up. check accepts it, because follow_ups: is free
    text, and execute carries the stale line into the split commit and the
    pull request's body. Re-run plan for such a plan.
  • A source's own single-repository.yaml is asked drop or root, drop by
    default, under a rule of its own, and no longer gets the general top-level
    YAML question (#566, #528).
  • What a run says is what it does. The plan's last follow-up is a review
    of the pull request execute opens itself. A failed verification names
    the pull request and the topic it left in place. Under
    ...
Read more

openRepoShape v1.0.1

Choose a tag to compare

@brettheap brettheap released this 15 Oct 14:20
8cda139

openRepoShape v1.0.1

v1.0.1 is v1.0.0 plus one change: openRepoShape installs in one line, and the
command it installs stays on the release tag it was installed from (#564).

What 1.0.1 adds

  • The one-line install. install.sh is attached to every release, so
    this line always installs the newest one:

    curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bash

    It reads which release is the newest from github.com's releases/latest
    redirect, with no gh and no login. It fetches that release's
    openRepoShape and installs it into ~/.local/bin, or
    $OPENREPOSHAPE_BIN_DIR, pinned to the release tag. It installs
    git-filter-repo 2.47.0 with pipx when pipx is there and
    git-filter-repo is not, and otherwise prints the one command that
    installs it. It ends with openRepoShape --preflight. install.sh itself
    never runs sudo; the preflight it ends with offers what is missing, each
    behind a typed yes at your terminal. Every download install.sh makes
    is HTTPS only, redirects included, at TLS 1.2 or newer. It exits 0 when the
    command is
    installed and the preflight passed, 1 when the command is installed but the
    preflight did not pass or the command does not stay on the release tag, and
    2 when it installed nothing.

  • An installed command that stays on its release tag. With
    OPENREPOSHAPE_RECORD_REF=1, which install.sh sets, openRepoShape --install records $OPENREPOSHAPE_REF in openRepoShape.ref beside the
    command it places. The installed command then fetches setup.sh at that
    ref, setup.sh clones the standard at it (--shape-ref), and --doctor
    checks its clone out at it, and so does a symlink to the command. In
    v1.0.0 an installed command fetched main on every run unless
    $OPENREPOSHAPE_REF was exported. $OPENREPOSHAPE_REF set for one run
    still overrides the record, and openRepoShape --version prints the ref in
    force. The installed command is byte for byte the release's own file.

Install and upgrade

  • The newest release: the line above. Run it again to upgrade to the
    newest release.
  • A named release:
    curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bash -s -- v1.0.1,
    or bash install.sh v1.0.1 from a checkout or a download.
  • What is installed: openRepoShape --version prints
    openRepoShape (opensoft/openRepoShape @ v1.0.1).
  • By hand, at main: the README's
    gh api repos/opensoft/openRepoShape/contents/openRepoShape … | bash -s -- --install
    line still installs main's command, which records no ref and follows main.
  • Platforms: macOS (the stock /bin/bash 3.2 is enough), Linux and WSL2.
    Git Bash on Windows is meant to work, as #564 asks, but nothing has run it
    yet: the launcher's and the installer's tests skip on the Windows job.
    #585 (v1.1.0) runs them under Git Bash. Native Windows without Git Bash
    still starts with py setup-project.py, as the README says.
  • A project's pin on the standard works as before. Follow
    How to pin 1.0.0 with the v1.0.1 release
    commit.

A script that runs --install

openRepoShape --install records a ref only when it is asked to, with
OPENREPOSHAPE_RECORD_REF=1. A script that exports $OPENREPOSHAPE_REF
around --install for its own reasons gets the install it got from v1.0.0:
nothing is recorded, a record already there is removed, and the command
follows main. To pin the command a script installs, set
OPENREPOSHAPE_RECORD_REF=1 and set $OPENREPOSHAPE_REF to the commit or
tag the command should stay on.

Known limits

v1.0.1 changes nothing else, so its known limits are v1.0.0's:
Known limits in v1.0.0.

openRepoShape v1.0.0

Choose a tag to compare

@brettheap brettheap released this 14 Oct 23:17
bfed143

openRepoShape v1.0.0

openRepoShape is the convention, as data and as running code, for a project
that spans several repositories: what they are called, how they are pinned to
each other, how one clone brings the whole project down, and what an AI
assistant does when asked to scaffold one. Its preferred shape, the Triad, is an
assembly root that mounts a spec leg and a code leg. The shape is elective and
confers nothing. Version 1.0 promises that every procedure in AGENTS.md works
end to end for ordinary repositories on Linux, macOS and Windows. On Windows,
make park and make resume run under WSL2, as the README says: native
Windows parks nothing. The release is tagged only when every in-scope issue is
closed and the full test suite passes on all three systems on the release
commit. Edge cases that only unusual input reaches
are listed below as known limits.

What 1.0 contains

  • New project: openRepoShape <Project> --org <org>, or ./setup.sh, or
    py setup-project.py on Windows. It runs a preflight, checks the names,
    shows the plan and asks for one typed yes.
  • Direct scaffold: scaffold-project.py. Add --pin open<Product>@<commit>
    for a declared descendant.
  • Adopt in place: adopt-project.py plan, then check, then execute.
  • Compliance check: openRepoShape --doctor [<path>] or
    shape-doctor.py --root <path>.
  • Stay current: update-shape.py check, then apply.
  • Advance a leg: scripts/bump-leg.py --root <path> --leg spec|code --to <40-hex>.
  • Families: scripts/family.py init|add|bump|remove|siblings.
  • Park and resume: make park and make resume.
  • Each project's own gate: make bootstrap and make validate run pinned
    copies of the scripts, so the gate never needs this standard.

Requirements and platforms

  • git and Python 3.9 or newer. The tools use the standard library only.
  • gh, logged in, wherever a tool creates repositories or pull requests on
    GitHub. git-filter-repo is needed for adoption only. make is optional.
  • openRepoShape --preflight checks the machine and creates nothing. On
    Windows, run py setup-project.py --preflight. It names what is missing,
    and offers to install gh, log it in and set up its credential helper,
    each behind a typed yes. setup-project.py --preflight also offers git.
  • Linux and macOS are supported, and the stock macOS /bin/bash 3.2 is
    enough. Native Windows uses setup-project.py, after
    git config --global core.autocrlf false. WSL2 runs the bash entry
    points. Park and resume do not work on native Windows; run them under
    WSL2.
  • CI runs the full suite on ubuntu-latest, macos-latest and
    windows-latest.

How to pin 1.0.0

A project pins the standard in contracts/shape-pin.yaml, which always
records a 40-hex commit. Name bfed143e89f97d3480cf5373051cb1ace82db312 itself rather than the tag: a
tag can be moved and a commit cannot, as the README and AGENTS.md say of every
pin.

  • New project: run
    OPENREPOSHAPE_REF=bfed143e89f97d3480cf5373051cb1ace82db312 openRepoShape <Project> --org <your-org>.
    The command forwards --shape-ref bfed143e89f97d3480cf5373051cb1ace82db312 to setup.sh. Outside a
    checkout, setup.sh and setup-project.py take --shape-ref directly.

  • Existing project or family holder: run these from a checkout of the
    standard. check lists the files that change. apply says how many it
    will re-copy and asks Type yes to proceed: before it writes anything:

    ./update-shape.py check --root <path> --at bfed143e89f97d3480cf5373051cb1ace82db312
    ./update-shape.py apply --root <path> --at bfed143e89f97d3480cf5373051cb1ace82db312 \
        --branch shape/update-bfed143e89f97d3480cf5373051cb1ace82db312
    git -C <path> push -u origin shape/update-bfed143e89f97d3480cf5373051cb1ace82db312

    Then open a pull request from that branch.

  • Confirm: run ./shape-doctor.py --root <path> from a checkout at
    bfed143e89f97d3480cf5373051cb1ace82db312. COMPLIANT, exit 0, means the pin names that commit.

What changed on the way to 1.0

adopt-project.py

  • In-place adoption. The repository becomes the assembly root and keeps
    its name and history. The legs are extracted with git filter-repo, and the
    split lands as one pull request. Every source path is verified by blob
    sha, a kept root .gitattributes as the source's bytes plus the block
    below.
    The plan asks about any path it cannot place, an empty leg is seeded with
    --allow-empty-leg, and the xf-project-<id> topic is set
    (#5, #16, #17, #24).
  • Branches and pins. The tracking branch defaults to the source's default
    branch, and check and execute refuse one that is no branch of the
    source (plan-tracking-branch-missing). The topic is set before the pull
    request, and NEXT names scripts/bootstrap.py when the source kept its own
    Makefile (#493). plan --pin takes <product>@<commit>, and execute
    writes the pin file the scaffold writes, so the adopted root's
    validate-pins.py passes; a --pin-source that cannot answer is
    pin-source-unreadable (#508). A pinned product such as 007 is listed
    in project.yaml as text, not as the number 7 (#515).
  • The source's own name. plan refuses an --org and --project that
    name another repository than the source's, compared without case
    (adopt-not-the-source). check and execute refuse a plan edited to
    name one (plan-not-the-source), and a source whose own name the naming
    policy refuses is told to rename it first (#513).
  • Keywords as typed. plan writes a value bare only where the project's
    own YAML loader reads it back as that text, so --spec-path null,
    --id null and --tracking-branch true, on a source with a branch true,
    adopt as typed. Before, the first mounted the leg at spec, the second made
    the id northwind, and the third was refused about a value nobody typed. An
    explicit null leg path in a plan (spec_path: null) is a
    plan-bad-leg-path finding, not the default spec. A backstop
    (adopt-manifest-misread, plan-manifest-misread) refuses, before any leg
    exists, a value the loader would read back as something else from the line
    execute writes. The shipped templates quote those lines, so it names none
    today (#518).
  • Refused before any leg exists. execute refuses everything check
    finds before it creates a leg. That covers bad mount paths, root paths that
    clash with the shape's files, the source's own project.yaml kept in the
    root, a symlink with an empty target, values no file can hold
    (adopt-unwritable-value), and paths it cannot extract, which plan now
    asks about. A split branch an earlier run left, or a branch in its way, is
    adopt-branch-exists or plan-split-branch-taken, and a hand-edited key
    of the wrong type, or a date the tools cannot read, is a finding, not a
    traceback
    (#170, #187, #198, #231, #270, #313, #353, #356, #377, #382, #387, #435).
  • Symlinks the root keeps. check follows each kept symlink that a
    path the shape writes runs through. One the shape cannot write through,
    because it dangles, leads out of the tree, enters a leg or a submodule,
    loops, or carries the shape's scripts out of their directory, is a
    plan-root-symlink finding, and execute refuses it before any leg
    exists. Where git checks a symlink out as a file, execute also refuses
    one the shape writes beneath (adopt-symlinks-off). Such links used to
    break the run after both legs were pushed, or put the shape's files
    outside the assembly. A link at a shape path to something that is there
    still gets the copy beside it (#504).
  • A source's own .gitattributes. A source that keeps one at its root
    gets one marked block of /<copy> text eol=lf lines appended to it in
    the split, a line for each pinned copy outside shape/, so a Windows
    clone writes those copies with the bytes the pin records. The
    verification counts that file as the source's bytes followed by exactly
    that block, and update-shape.py apply --add extends the block (#511).
  • Submodules. .gitmodules stays in one leg with its submodules. An
    unreadable one is a plan-gitmodules-unreadable finding, not a crash, and
    relative URLs are checked from both sides. Verification accepts no root
    gitlink but the two leg mounts, each at the commit pushed to its leg
    (adopt-root-submodule, adopt-mount-commit and others). execute
    records in project.yaml the leg each kept submodule went to, and the
    doctor holds it there
    (#162, #173, #194, #226, #240, #245, #268, #394).
  • Ignore rules and odd names. Shape files are committed even when the
    source's .gitignore matches them, and leg mounts use --force. Findings
    spell out control characters and bytes that are not UTF-8, in a
    hand-edited question: and a submodule's name too, follow-up searches take
    paths literally, and a cp1252 console gets escaped names
    (#182, #200, #223, #228, #246, #247, #378, #384).
  • Late failures. After a leg exists, a failure prints REFUSED <step>.
    and what the run left behind (#445). Exit 1 means the verification failed.
    Exit 2 with IS pushed is finished by hand with the command it prints,
    never by re-running (#371).

update-shape.py and bump-leg.py

  • Re-syncing copies. check gives each pinned file a verdict
    (unchanged, upstream-changed, locally-modified, both and others)
    and names files the standard has added (upstream-added). It exits 1 when
    there is work to do. apply re-copies and re-pins, and rolls back if the
    project's validators fail. --add and --accept-local each name one file
    and can be repeated. Family holders, and their README's Shape: line, update the same
    way, and the commit and refusals name the holder's own manifest
    (#10, #29, #47, #52, #85, #152, #159).
  • **Local edits and merged c...
Read more