Stay current with upstream
Upstream frogg ships often. A fork that merges weekly stays easy; a fork that merges twice a year becomes a project. The branding system is designed so that a normal merge touches none of your product files.
Why merges stay clean
Section titled “Why merges stay clean”- Your identity lives in
brands/<id>/or an external repository. Upstream only changesbrands/frogg/andbrands/example/. - Your brand is selected by repository variables, not by editing workflows.
- Generated output (
.generated/,apps/ui/.generated/,packages/branding/src/generated/,.branding-input/) is git-ignored, so it never conflicts. - Upstream CI rejects new hard-coded
froggorfrogg.appstrings inapps/andpackages/source (scripts/ci/branding-contribution.mjs). Product names come from the brand, so upstream features arrive already branded for you.
What can still conflict: shared files you changed on purpose (a custom workflow step,
macOS signing, a patched component), and the root package.json version.
Merge upstream
Section titled “Merge upstream”-
Start from a clean
main:Terminal window git switch maingit pull --ff-only origin maingit fetch upstream --tags -
See what is coming:
Terminal window git log --oneline main..upstream/main | head -30git diff --stat main...upstream/main -- CHANGELOG.md .github/ packages/branding/Read the new
CHANGELOG.mdsections. Upgrade notes that affect clients and daemons together are called out there. -
Merge:
Terminal window git merge --no-ff upstream/main -m "Merge upstream frogg $(git describe --tags --abbrev=0 upstream/main)" -
Reinstall and check the brand still resolves against the new schema and generator:
Terminal window npm ciFROGG_BRAND_DIR=brands/acme npm run brand:checkFROGG_BRAND_DIR=brands/acme node scripts/ci/branding-acceptance.mjsnpm run verify -- --fast -
Run the app locally with your brand (
npm run dev:server,npm run dev:app) and look at the screens you care about. -
Push and let CI run:
Terminal window git push origin main
Merge or rebase?
Section titled “Merge or rebase?”Merge. Your main is published: tags, releases and other people’s branches point
at it. Rebasing it onto upstream rewrites those commits. Merges also keep a record of
exactly which upstream version each release contains.
Rebase is fine for short-lived feature branches that nobody else has pulled.
Resolve conflicts
Section titled “Resolve conflicts”| Conflict in | Resolution |
|---|---|
package.json / package-lock.json version |
Take upstream’s lockfile, then set the version you intend to ship (see below) and run npm install to refresh the lockfile |
CHANGELOG.md |
Keep both. Put your fork’s entries in their own version sections |
.github/workflows/*.yml you customized |
Take upstream’s file, then reapply your change. Keep customizations small and commented # acme: so they are easy to find |
brands/frogg/* |
Take upstream’s version. You should not be editing it |
| Shared source you patched | Prefer upstreaming the patch (see contributing back) so the conflict disappears next time |
After resolving:
git add -Agit commitnpm ci && npm run typecheckIf brand:check fails after a merge, compare packages/branding/src/schema.ts with the
previous version. New optional fields do not require changes to your manifest.
Automate a sync check
Section titled “Automate a sync check”Upstream does not ship a sync workflow. The following is an example you can add to
your fork as .github/workflows/upstream-sync.yml. It opens a pull request when
upstream has new commits, so the merge still gets CI and review. It is not tested
upstream; adapt it to your process.
name: Upstream syncon: schedule: - cron: "0 6 * * 1" # Mondays 06:00 UTC workflow_dispatch:permissions: contents: write pull-requests: writejobs: sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Merge upstream into a sync branch env: GH_TOKEN: ${{ github.token }} run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git remote add upstream https://github.com/frogg-app/frogg.git git fetch upstream main --tags if [ -z "$(git rev-list HEAD..upstream/main)" ]; then echo "Already up to date."; exit 0 fi branch="upstream-sync/$(date +%Y-%m-%d)" git switch -c "$branch" if ! git merge --no-ff upstream/main -m "Merge upstream frogg"; then git merge --abort gh issue create --title "Upstream sync needs a manual merge" \ --body "Merging upstream/main into main conflicts. Merge locally and resolve." exit 0 fi git push origin "$branch" gh pr create --base main --head "$branch" \ --title "Merge upstream frogg" \ --body "Automated merge of frogg-app/frogg main. Review CHANGELOG.md for upgrade notes."A push by GITHUB_TOKEN does not trigger other workflows. If you want CI on the sync
pull request, push with a GitHub App token or fine-grained PAT instead, or re-run CI from
the pull request.
Version your releases
Section titled “Version your releases”The root package.json version is your product version. Release tags must equal it.
Desktop and daemon updates compare these versions with semver.
Recommended scheme:
- Ship upstream versions. After merging upstream 0.6.20, release your fork as
v0.6.20. Users can map your release to upstream’s changelog directly. - Fork-only fixes between upstream releases: bump the patch (
0.6.21). On the next upstream merge, set the version to whichever is higher, plus one patch. - Betas use a prerelease suffix:
npm run version:all:beta:patchgives0.6.21-beta.1. Stable clients ignore them.
Avoid build metadata such as 0.6.20+acme.1. Semver ignores it when comparing, so
updaters do not see +acme.2 as newer than +acme.1, and Docker tags cannot contain +.
Upgrade cadence
Section titled “Upgrade cadence”- Merge upstream at least every few weeks. Small merges resolve in minutes.
- Read each
CHANGELOG.mdsection for notes marked as coordinated client and daemon upgrades. Release those to your users with the same care upstream describes. - After a merge that changes
.github/workflows/release.ymlorscripts/release/, cut a beta release first and confirm the update path from your previous release.