Skip to content

adopt-project.py --source: a file, a directory inside a repository, an unknown ~user/, an empty value and owner/.git are not refused by name, so plan ends in a traceback, plans only a subtree or the current directory, or hands gh a name it cannot clone #796

Closed

Description

@brettheap

Goal: a --source (or a recorded source.local_path) that does not name a repository's top level, or a repository on GitHub, is refused by name with a way out. It never ends in a traceback, never plans the current directory or a subtree, and never reaches gh with a name it cannot clone.

Filed by lane openRepoShape-1 on Brett Heap's word of 2026-10-17T10:21:32Z, under lane 3's v1.1.1 rule (#473, issuecomment-6216697223). Lane 1's triage of its #457 lines grouped these lines into this one issue, because they share _source_spec, Source.open and Source.in_place. They come from:

Each line was measured again on main bf1f099b with a stub gh.

Milestone: v1.1.1, not a regression. Each of the following is an ordinary slip:

  • passing a README or an archive as --source;
  • running plan --source . from a subdirectory;
  • one mistyped user name in ~user/;
  • an unset shell variable.

Lines

  • L1-64: a file. plan, plan --work-dir, check --source and execute --source each exit 1 with NotADirectoryError: [Errno 20] Not a directory: '<path>'. The traceback runs Source.in_place → _remote_repository → _origin_url → repo_shape.git_out. A directory that is not a repository is already refused git-failed (exit 2).
  • L1-88: a directory inside a repository.
    • plan --source <repo>/src on a 10-file repository exits 0 with "2 files", local_path: "…/Thing/src" and 1 entry.
    • plan --source . run inside <repo>/docs exits 0 with 1 file.
    • check then exits 2: REFUSED a git or gh command failed … fatal: '…/Thing/docs' does not appear to be a git repository.
    • execute refuses adopt-remote-unreadable before any leg.
    • Rider: the source report line names origin's repository even when plan read a local directory.
  • L1-95: ~nouser/x. plan --source '~nouser/x' exits 1 with RuntimeError: Could not determine home directory. from _source_spec's Path(spec).expanduser().
    • check --source and execute --source exit 1 the same way.
    • A plan whose local_path is "~nouser/x" makes check exit 1 at Plan._recorded_source.
    • bash passes an unknown ~user through unchanged, so one mistyped user name is enough.
  • L1-96: an empty --source. From inside a repository here, plan --source '' exits 0 and writes a plan of here. So does --source "$SRC" with SRC unset.
    • Outside any repository it is refused git-failed: git rev-parse main in <cwd>, which never names --source.
    • check --source '' and execute --source '' drop the empty value (if override:) and read the plan's own record.
  • L1-100 (rider): owner/.git. --source owner/.git and https://github.com/owner/.git reach gh repo clone owner/.git with the target source-readonly/.git. They are then refused source-unclonable with a gh auth status remedy, because _github_name strips .git only from a longer segment.
  • L1-101 (rider): www.github.com. --source https://www.github.com/testorg/Thing is refused source-unresolvable as "a repository on www.github.com". www.github.com/testorg/Thing is refused as neither a path nor a name.
  • L1-77 (rider): a source that lost a blob. git ls-tree -r -l HEAD prints BAD for the size and still exits 0, and plan then exits 1 with ValueError: invalid literal for int() with base 10: 'BAD' at Source.tree.

Direction

  • The checks, in _source_spec and Source.open, before any git runs:
    • a path that exists but is not a directory;
    • a local directory that is not a repository's top level: git rev-parse --show-prefix is not empty. The refusal names --show-toplevel.
    • an empty --source, refused in the style of pin-source-no-path; open_source tests override is not None.
  • Each is refused by name, with a remedy: pass the repository's top-level directory, or an org/repo.
  • The source line names the local path, with the repository beside it.
  • Expanding ~: use os.path.expanduser, which leaves an unknown ~user as typed, at each Path(...).expanduser().
    • An unknown user then becomes source-unresolvable for --source.
    • In a recorded local_path it becomes a path that is gone: plan-source-gone, or a re-clone under cloned: true.
  • _github_name refuses the repository segment .git.
  • www.github.com reads as github.com in _repository_in_url and in the bare url form.
  • Source.tree refuses a size that is neither - nor digits, naming the path and git fsck.

Tests: extend tests/test_adopt_source_clone_record.py and tests/test_adopt_work_dir_refusal.py.

  • For each line, plan, check and execute exit 2 with the code: no traceback, no plan written, and no gh call where none is needed.
  • Controls: the top level, . at the top level, an org/repo, and https://github.com/o/r.
  • Mutation-check each guard.

Not here: L1-65 (a source-readonly that is a file) goes with the clone-removal hardening issue, beside Source.clone.

Overlaps: #749 (#652) rewrote --source reading; #797 (_default_branch) and #798 (_create_leg_remotes) are lane 1's siblings in the same file.

Lane: openreposhape-1 (openRepoShape-1)

🤖 Generated with Claude Code

Activity

  1. added this to the v1.1.1 milestone on Oct 17, 2026
  2. brettheap commented on Oct 17, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-1, session 300f8f46-004a-4e72-8587-4f192be2e108@Eagle, 2026-10-17T10:43:08Z, for #796

    Logged in lanes/log/openRepoShape-1.md at ed0308db2c2633a7c518e5bef9137c4ad68cc826.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#796:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#796"`, kept where the row or its body names `#796`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `796` as a whole token:
       none
    
  3. changed the title [-]adopt-project.py: a `--source` that is a file ends `plan`, `check` and `execute` in a NotADirectoryError traceback, so a mistyped path gets no refusal and no way out[/-] [+]adopt-project.py `--source`: a file, a directory inside a repository, an unknown `~user/`, an empty value and `owner/.git` are not refused by name, so `plan` ends in a traceback, plans only a subtree or the current directory, or hands `gh` a name it cannot clone[/+] on Oct 17, 2026
  4. added a commit that references this issue on Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions