fix(ci): fail in one second on duplicate sources, not twenty minutes into an archive - #430
Merged
Merged
Conversation
…into an archive
The iOS App Store archive failed on 2026-08-11 after ~20 minutes with:
'WatchSessionIdentity' is ambiguous for type lookup in this context
found this candidate: WatchSessionBoundary 2.swift
found this candidate: WatchSessionBoundary 3.swift
found this candidate: WatchSessionBoundary.swift
There were 25 such files under CLIPulseCore/Sources, every one byte-identical to
its original, dated 07-30 to 08-03. SwiftPM globs its Sources directory, so
unlike the Xcode app target — which compiles only what project.pbxproj lists —
every stray copy is compiled.
.gitignore already matches `* [2-9].*`, and that is precisely why this needed a
separate guard: **ignoring a file protects the index, not the build.** They were
invisible to `git status`, invisible to review, and fatal to `xcodebuild
archive`. The error also reads like a code bug rather than a stray file, which
is the worst possible failure mode at the end of a long archive.
The guard distinguishes byte-identical strays (delete them) from same-name
files whose CONTENT DIFFERS (inspect — that is divergent work, not a copy).
Verified both directions: clean tree passes; recreating one of each makes it
fail and report them correctly.
Runs in Repo Hygiene, which has no path filter, so it also covers the docs-only
and migration-only PRs that swift-ci skips.
ON THE CAUSE, HONESTLY: unknown. Long assumed to be iCloud, and that is
probably wrong — `~/Documents` is a plain directory with Desktop & Documents
sync OFF, the repo holds zero `.icloud` placeholders, and OneDrive syncs only
`~/OneDrive - keio.jp`. Since the source is unidentified, deleting them is a
reset rather than a fix; they have already returned once (14 deleted 08-07, 25
present 08-11). Hence a guard rather than a cleanup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a CI hygiene guard to fail fast when duplicate-named source files (e.g., Foo 2.swift) appear in SwiftPM-compiled directories, preventing long xcodebuild archive runs from failing late with “ambiguous for type lookup” errors.
Changes:
- Introduce
scripts/check_no_duplicate_sources.shto detect and classify duplicate-style files in SwiftPMSources/andTests/directories. - Run the new guard as part of the
Repo HygieneGitHub Actions workflow to ensure it executes even for PRs that don’t trigger Swift CI.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| scripts/check_no_duplicate_sources.sh | New repo hygiene script to detect duplicate-named compiled sources and fail CI early. |
| .github/workflows/repo-hygiene.yml | Wires the duplicate-source guard into the existing Repo Hygiene workflow. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+57
to
+70
| while IFS= read -r -d '' f; do | ||
| base="$(basename "$f")" | ||
| canonical_base="$(sed -E 's/ [0-9]+(\.[A-Za-z0-9]+)$/\1/' <<< "$base")" | ||
| canonical="$(dirname "$f")/$canonical_base" | ||
| if [[ -f "$canonical" ]]; then | ||
| if cmp -s "$f" "$canonical"; then | ||
| offenders+="${f} — byte-identical duplicate of ${canonical_base}"$'\n' | ||
| else | ||
| offenders+="${f} — duplicate NAME of ${canonical_base}, but CONTENT DIFFERS (inspect before deleting)"$'\n' | ||
| fi | ||
| else | ||
| offenders+="${f} — duplicate-style name with no original present"$'\n' | ||
| fi | ||
| done < <(find "$dir" -type f -name "* [0-9].*" -print0 2>/dev/null) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The iOS App Store archive died today after ~20 minutes:
25 such files under
CLIPulseCore/Sources, every one byte-identical to its original, dated 07-30 to 08-03.Why
.gitignoredidn't save usIt already matches
* [2-9].*— and that is exactly the point. Ignoring a file protects the index, not the build. SwiftPM globsSources/andTests/, so unlike the Xcode app target (which compiles only whatproject.pbxprojlists), every stray copy gets compiled.They were invisible to
git status, invisible to review, and fatal toxcodebuild archive— surfacing as a type-ambiguity error that reads like a code bug rather than a stray file, at the end of a long archive. Worst possible failure mode.The guard
Distinguishes two cases, because they need opposite responses:
byte-identical duplicate of Xduplicate NAME of X, but CONTENT DIFFERSVerified both directions — clean tree passes; recreating one of each makes it fail and classify them correctly.
Runs in
Repo Hygiene, which has no path filter, so it also covers the docs-only and migration-only PRs thatswift-ciskips.On the cause — honestly, unknown
Long assumed to be iCloud. That's probably wrong:
~/Documentsis a plain directory; Desktop & Documents sync is off.icloudplaceholders anywhere in the repo~/OneDrive - keio.jpNeither candidate covers this path. Since the source is unidentified, deleting them is a reset, not a fix — they have already come back once (14 deleted 08-07, 25 present 08-11). That's the argument for a guard rather than another cleanup.
🤖 Generated with Claude Code