Skip to content

fix: preserve regular-file modes in push_files - #3410

Open
nateberkopec wants to merge 2 commits into
github:mainfrom
nateberkopec:fix/push-files-mode
Open

nateberkopec wants to merge 2 commits into
github:mainfrom
nateberkopec:fix/push-files-mode

Conversation

@nateberkopec

Copy link
Copy Markdown

This is AI-assisted. Model: gpt-6.1-sol

Summary

Preserve existing regular-file modes when push_files changes content, and accept an optional per-file mode of 100644 or 100755. New files without a mode remain 100644.

Why

Fixes #2578. Updating an executable shell script through push_files currently resets it to 100644, which breaks workflows that execute the script directly. This happened while publishing a dotfiles change.

Thanks to @why-pengo for the earlier mode-support work in #2579. This proposal adds automatic preservation as well as explicit regular-file modes. It deliberately excludes tree, symlink, and gitlink creation: these cannot safely be represented by the existing blob-with-content input.

What changed

  • Add optional files[].mode, limited to the strings 100644 and 100755, with runtime validation before client acquisition.
  • Resolve omitted modes against the pinned base commit/tree. Cache nonrecursive tree reads for shared parent directories.
  • Reject writes to existing symlinks, gitlinks, directories, inconsistent type/mode entries, and paths with nondirectory ancestors, including with explicit mode. Fail closed on lookup errors, truncated trees, or missing ancestor tree SHAs.
  • Validate populated-repository paths before creating a missing branch; use the pinned commit for branch creation and commit parenting.
  • Add real-handler HTTP regression tests; update the tool snapshot, generated README, and symlink recovery advice.

MCP impact

  • Tool schema or behavior changed
    files[].mode is optional. Existing regular-file updates retain their executable bit by default; explicit mode can add or remove it. New files still default to ordinary mode. Writes that would replace special entries now return an error. No new tool is added.

Prompts tested (tool changes only)

No end-to-end model prompts or live GitHub writes were used to validate this patch. Local tests invoke the actual handler with MCP arguments and inspect go-github HTTP requests. Covered use cases correspond to:

  • "Update run.sh without changing its executable permission."
  • "Create run.sh as executable with mode 100755."
  • "Change an executable file to ordinary mode 100644."
  • "Update nested files together; do not overwrite a symlink or submodule."

Security / limits

  • Auth / permissions considered
    Uses the existing authenticated client and Git APIs; no new credentials or authorization mechanism. Mode lookup adds tree reads, cached by SHA within the request.
  • Data exposure, filtering, or token/size limits considered
    Traversal does not follow symlinks and rejects incomplete lookup results. Only regular-file modes are accepted. Untouched special entries remain inherited from the base tree.

This is not a transactional rollback change: existing empty-repository initialization still writes an initial README before tree resolution, and failures in later Git API mutations can leave created objects or a newly created branch.

Tool renaming

  • I am not renaming tools as part of this PR

Lint & tests

  • Linted locally with ./script/lint
    Implementer ran the script; independent reviewer also ran golangci-lint 2.14.0 (zero issues) and checked formatting.
  • Tested locally with ./script/test
    Full race suite passed for the implementer and independent reviewer.

Also passed focused PushFiles/CreateOrUpdateFile tests, UPDATE_TOOLSNAPS=true go test ./..., go test -v ./..., and script/generate-docs. The independent reviewer found no P0/P1/P2 defects. A preserved baseline regression demonstrated 100644 instead of expected 100755 before the fix. npm audit was not run; no UI or dependency files changed. Tests establish local handler/SDK behavior, not the revision or behavior of the hosted MCP service.

Docs

  • Updated (README / docs / examples)
    Regenerated README parameter documentation and the push_files tool snapshot.

@nateberkopec
nateberkopec requested a review from a team as a code owner October 4, 2026 09:38
Copilot AI balanced review requested due to automatic review settings October 4, 2026 09:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Deep existing paths need a traversal limit to bound sequential GitHub API requests.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Updates push_files to preserve executable permissions and support explicit regular-file modes, addressing #2578.

Changes:

  • Adds optional 100644 and 100755 modes.
  • Validates paths against the pinned base tree before creating missing branches.
  • Adds regression tests and updates documentation and symlink recovery advice.
File Description
README.md Documents optional modes and preservation behavior.
pkg/​github/​repositories.go Adds mode validation and deferred branch creation.
pkg/​github/​repositories_test.go Updates mocks and recovery-message expectations.
pkg/​github/​repositories_helper.go Resolves modes and rejects unsupported entries.
pkg/​github/​push_files_test.go Adds handler-level regression coverage.
pkg/​github/​__toolsnaps__/​push_files.snap Records the updated input schema.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +83 to +85
parts := strings.Split(entry.GetPath(), "/")
mode := "100644"
for i, part := range parts {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add file mode (executable bit) support to push_files

2 participants