Improve libgit2 build reliability - #97
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved cache invalidation, dependency tracking, error handling, and architecture issues block approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Improves libgit2 build reliability through incremental rebuilds and Xcode dependency tracking.
Changes:
- Adds archive freshness checks and POSIX shell syntax.
- Declares Xcode script inputs and output.
- Restricts supported build architectures.
File summaries
| File | Review summary |
|---|---|
script/update_libgit2 |
Adds caching, but revision/deletion changes, architecture differences, build recipe changes, and masked find failures can leave stale or incompatible archives. |
ObjectiveGitFramework.xcodeproj/project.pbxproj |
Adds dependency declarations, but the source set is incomplete and excluding x86_64 conflicts with the Intel CI job. |
Review details
Suppressed comments (4)
ObjectiveGitFramework.xcodeproj/project.pbxproj:1790
- The Release project configuration is used by the archive job, but excluding
x86_64here leaves no valid architecture on the workflow'smacos-15-inteljob (.github/workflows/BuildPR.yml:17-22). The Mac archive therefore fails on Intel. Either keepx86_64enabled or update/remove that CI job as part of this change.
EXCLUDED_ARCHS = x86_64;
ObjectiveGitFramework.xcodeproj/project.pbxproj:1926
- This project-level Test setting excludes
x86_64, so the repository's Intel test/build configuration cannot produce a valid architecture. This conflicts with the Intel job still declared in.github/workflows/BuildPR.yml:17-22; retain that architecture or update the job and supported-architecture contract together.
EXCLUDED_ARCHS = x86_64;
ObjectiveGitFramework.xcodeproj/project.pbxproj:2145
- This project-level Profile setting excludes
x86_64, making Profile builds on the repository's Intel environment unable to select a valid architecture. It is inconsistent with the Intel build matrix in.github/workflows/BuildPR.yml:17-22; retain that architecture or update the supported-architecture contract and CI together.
EXCLUDED_ARCHS = x86_64;
script/update_libgit2:8
- This mtime scan only considers files that still exist. If the libgit2 submodule is switched to a revision that deletes or renames a source file,
findcan return no newer file while the old archive remains present and newer than every remaining file, causing the script to exit with stale code. Track the submodule revision or another manifest/sentinel so deletions invalidate the archive.
newer_source=$(find External/libgit2 \
\( -path 'External/libgit2/.git' -o -path 'External/libgit2/build' \) -prune -o \
-type f -newer "$product" -print -quit 2>/dev/null || true)
- Files reviewed: 2/2 changed files
- Comments generated: 5
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Sorry for the late review, I miss this pull request. My bad |
goneng
left a comment
There was a problem hiding this comment.
One blocker from a quick read: the four EXCLUDED_ARCHS = x86_64; additions in the build configurations.
That makes the project unbuildable on Intel Macs, and this repo still runs a macos-15-intel job in CI.
Dropping those four lines should be all it needs. The rest of the PR looks like a real improvement.
EXCLUDED_ARCHS = x86_64 was added to work around archiving on an Apple Silicon host: ARCHS defaults to arm64 + x86_64 there, while script/update_libgit2 builds a single-arch libgit2.a for the host, so the x86_64 slice fails to link. Excluding x86_64 project-wide was the wrong lever. All four settings sat on project-level configurations, so they cascaded to every target; they contradicted ObjectiveGit-Mac's own VALID_ARCHS = "x86_64 arm64"; and they left the macos-15-intel CI job with no buildable architecture. Architecture selection belongs at the invocation instead - ARCHS=, as the workflow already passes, or ONLY_ACTIVE_ARCH, which Debug and Release already set. Building a universal libgit2.a is not an option here because libgit2 links Homebrew OpenSSL, which is native-arch only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeDNs6jJrjDDdpo2T1YUDF
The mtime scan only considered files that still existed. Switching the libgit2 submodule to a revision that deletes or renames a source could leave every surviving file older than External/libgit2.a, so the script reported "libgit2 is up to date." and the build linked stale code. Replace it with a stamp file recording a key built from the submodule revision, the submodule working tree state, the host architecture and a hash of this script. That covers revision switches including ones that only delete files, uncommitted edits and deletions, an archive built for the other architecture, and changes to the cmake flags. The submodule's build directory is excluded so its own output does not force a rebuild. Failures are no longer masked: git errors go to stderr instead of 2>/dev/null, and any part of the key that cannot be computed yields an empty key, which rebuilds rather than trusting an archive we cannot account for. The stamp is removed before building and written only after the archive is installed, so an interrupted build cannot look up to date. The run script phase now sets alwaysOutOfDate instead of declaring inputPaths. libgit2's sources cannot be enumerated statically, so listing only CMakeLists.txt let Xcode skip the phase when sources changed - the same staleness, one level up. The script's own check is now the single source of truth, and it is cheap. Also anchor the script to the repository root so the CI invocation and Xcode's $SRCROOT invocation behave identically, stop the product variable shadowing itself, and teach clean_externals and .gitignore about the stamp. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeDNs6jJrjDDdpo2T1YUDF
The workflow interpolated matrix.arch, but the matrix defines abi, so both jobs passed an empty ARCHS and neither pinned the architecture it claims to test. Use matrix.abi, in the archive step and in the commented-out test step so it is correct if it is re-enabled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeDNs6jJrjDDdpo2T1YUDF
Resolves the freshness rewrite against master's switch from BUILD_CLAR to BUILD_TESTS, and accepts master's removal of script/clean_externals, which this branch had only touched to teach it about the build stamp. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeDNs6jJrjDDdpo2T1YUDF
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The freshness check can miss source-content changes, and Xcode dependency optimization is not enabled.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (2)
Resolved since last review (5)
This cache decision keys only on source mtimes, butExternal/libgit2.ais built for the current… This freshness guard only compares mtimes of currently existing submodule files with the archive.… This project-level Debug setting excludesx86_64, while the repository still defines an Intel…|| truemasks everyfindfailure. If the submodule is missing or unreadable while an ignored… Xcode usesinputPathsfor dependency analysis, so with only the script andCMakeLists.txt…
| git -C "$submodule" status --porcelain --untracked-files=all \ | ||
| -- . ':(exclude)build' || return 1 |
There was a problem hiding this comment.
As it's a High finding, I'll give Copilot a try ...
There was a problem hiding this comment.
This could be an alternative: copy&past from google
BUILD_KEY=$(
(
git -C "$submodule" ls-files -s -- . ':(exclude)build'
git -C "$submodule" ls-files -o --exclude-standard -- . ':(exclude)build'
) | sort -u | git -C "$submodule" hash-object --stdin
) || return 1
| }; | ||
| D0A330F116027F2300A616FA /* libgit2 */ = { | ||
| isa = PBXShellScriptBuildPhase; | ||
| alwaysOutOfDate = 1; |
| # run: xcodebuild -workspace ObjectiveGitFramework.xcworkspace -scheme "ObjectiveGit Mac" test ARCHS="${{ matrix.abi }}" | ||
| - name: Archive project | ||
| run: xcodebuild -workspace ObjectiveGitFramework.xcworkspace -scheme "ObjectiveGit Mac" archive ARCHS="${{ matrix.arch }}" | ||
| run: xcodebuild -workspace ObjectiveGitFramework.xcworkspace -scheme "ObjectiveGit Mac" archive ARCHS="${{ matrix.abi }}" |
hannesa2
left a comment
There was a problem hiding this comment.
As it works, we could solve it later too. But this is just my opinion



Summary
Why
GitX rebuilds libgit2 on every application and test invocation, substantially increasing local and CI turnaround time. The build phase did not declare its dependencies, and its script had no freshness check.
Validation
sh -n script/update_libgit2