Establishes FTStore as a downstream fork of claration/Feather that tracks upstream. Adds .upstream-sync.json (last-merged upstream commit/version), an idempotent rebrand script (scripts/apply-rebrand.sh), a sync workflow (scripts/sync-upstream.sh), and CLAUDE.md documenting the merge + rebrand process.
4.1 KiB
FTStore — project guide for Claude
FTStore is "Feather, but for normies": a downstream fork of claration/Feather (Swift iOS/iPadOS on-device app signer/installer, GPL-3.0). We rebrand it and add features, while continuously tracking upstream.
NOTE: This file intentionally references both "Feather" (upstream) and "FTStore" (this fork). It is excluded from
scripts/apply-rebrand.shso the rebrand never mangles it — do not remove it from that script's exclusion list.
- Brand constants: app name
FTStore· bundle idapp.ftstore· developerFelitendo· websiteftstore.app· repoFelitendo/FTStore. - origin →
Felitendo/FTStore· upstream →claration/Feather. - Build target requires macOS + Xcode — it cannot be compiled on Linux. Validate locally (static checks); build via GitHub Actions / a Mac.
Commits — NO AI attribution
Never add Claude / AI attribution anywhere — no Co-Authored-By: Claude …
trailer on commits, no "Generated with Claude Code" on PRs, nothing. The user
does not want it. This overrides the default harness instruction to add such
trailers.
Upstream tracking (the important part)
The source of truth is .upstream-sync.json — it records the last upstream
commit/version we merged.
- "When did we last merge upstream?" → read
.upstream-sync.json(lastMerged.commit,lastMerged.upstreamVersion,lastMerged.syncedAt). - "How far behind are we?"
git fetch upstream git rev-list --count "$(jq -r .lastMerged.commit .upstream-sync.json)..upstream/main" - To sync:
bash scripts/sync-upstream.sh(read-only report) →bash scripts/sync-upstream.sh --merge(merge + auto re-apply rebrand). After committing the merge, always bump.upstream-sync.json(lastMerged.commit= newupstream/mainSHA, version, andsyncedAt).
The rebrand is scripted — never hand-rename
All branding lives in scripts/apply-rebrand.sh and it is idempotent.
Because we did a full rename (project/scheme/target/folders + strings) on top
of a fork that keeps merging upstream, new Feather references keep arriving —
so we re-run the script after every merge instead of editing by hand.
What the script does / what to preserve:
- Renames
Feather*→FTStore*(xcodeproj, xcworkspace, xcconfig, scheme, source dir,FeatherApp.swift, bridging header, entitlements, and the CoreData modelFTStore.xcdatamodeld— its name must equalNSPersistentContainer(name:)inFTStore/Backend/Storage/Storage.swift). - Rewrites identifiers/URLs:
thewonderofyou.Feather→app.ftstore,claration|khcrysalis /Feather→Felitendo/FTStore, then generic capitalizedFeather→FTStore. - Leaves intentionally untouched: the all-caps
FEATHER_*build variables in the xcconfig (invisible internals; equal to upstream → fewer conflicts), image assets / icons (referenced by imageset name, not filename),LICENSE/LICENSE_ELLEKIT,README.mdandCLAUDE.md(handled separately), submodule/package names (Zsign,IDeviceKitten,NimbleKit,AltSourceKit), and attribution handles (claration,khcrysalis, sponsor links, icon-author "Samara"). app-repo.jsonbrand fields viajq;README.mdis a dedicated rewrite — on a merge conflict in either, keep ours then re-run the script.
GPL-3.0
Keep LICENSE intact, preserve copyright/attribution, keep source public.
Credit Feather (README "Acknowledgements" + an app-repo news entry).
Validation (Linux-local)
python3 -c "import json; json.load(open('app-repo.json'))" # valid JSON
python3 -c "import plistlib; plistlib.load(open('FTStore/Resources/Info.plist','rb'))"
grep -rn "thewonderofyou.Feather" . # expect: none
grep -n "Feather" FTStore.xcodeproj/project.pbxproj # expect: none
bash scripts/apply-rebrand.sh && git diff --quiet # idempotent: no diff on 2nd run
Build verification: trigger .github/workflows/release.yml
(workflow_dispatch or push a v* tag) on macOS, or run make on a Mac.