Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d748647e9b
|
||
|
|
dc40fa43bc | ||
|
|
2ae7ded357 | ||
|
|
dd15f22031 |
No files matched your search
@@ -134,12 +134,30 @@ jobs:
|
||||
# meaningless and can mis-bump, so gate to push (PLAN §4.2).
|
||||
- run: scripts/determine_next_version.sh
|
||||
if: github.event_name == 'push'
|
||||
- run: scripts/replace_environment_variables_in_app.sh
|
||||
# alt-tab-free [depaywall]: decouple the release apparatus from "every signing-ready push".
|
||||
# determine_next_version.sh writes an EMPTY VERSION.txt when no commit since the last tag is
|
||||
# release-worthy (semantic-release dry-run finds no bump). Gate the sign/notarize/release steps
|
||||
# on this so a docs/ci/chore push still runs guards + commitlint + run_tests (Release-config
|
||||
# compile check) but does NOT sign-build, notarize, or cut a release — no [skip ci] needed (§4.3).
|
||||
- name: release-decision — did semantic-release compute a version?
|
||||
id: release-decision
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
if [ -n "$(tr -d '[:space:]' < "$VERSION_FILE" 2>/dev/null)" ]; then
|
||||
echo "will-release=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Release-worthy: semantic-release will cut v$(cat "$VERSION_FILE")."
|
||||
else
|
||||
echo "will-release=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::No release-worthy commit — build/test only; notarize+release tail skipped."
|
||||
fi
|
||||
# Inject the release version only when releasing; an empty CURRENT_PROJECT_VERSION would crash
|
||||
# App.version (see config/local.xcconfig). Non-release builds use the committed 0.0.0 default.
|
||||
- run: scripts/replace_environment_variables_in_app.sh
|
||||
if: github.event_name == 'push' && steps.release-decision.outputs.will-release == 'true'
|
||||
# codesign needs APPLE_P12_CERTIFICATE — gated on signing-ready so a not-yet-provisioned fork
|
||||
# does not RED on the missing-cert import (PLAN §3.3/§4.3).
|
||||
- run: scripts/codesign/setup_ci_master.sh
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
# Guard A — the anti-relock unit test (appended to LicenseManagerTests.swift). Runs on both
|
||||
# push and PR (no secrets needed); on the push path it sits before the sign/tag steps below
|
||||
# so a relock fails BEFORE any irreversible side-effect (PLAN §4.4). This is what keeps push
|
||||
@@ -152,7 +170,7 @@ jobs:
|
||||
# prove the tree compiles without a cert. When the owner adds the secrets AND sets the fork's
|
||||
# Developer ID in config/local.xcconfig, this builds signed for the release tail (PLAN §3.3/§4.3).
|
||||
- name: build app (signed — owner cert provisioned)
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
run: scripts/build_app.sh
|
||||
- name: build app (ad-hoc — until signing-provisioned)
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready != 'true'
|
||||
@@ -160,24 +178,24 @@ jobs:
|
||||
# --- Release tail: requires the owner's Apple Developer ID + Sparkle secrets. Skipped cleanly
|
||||
# (NOT failed) when they are absent, so push CI stays GREEN until the owner provisions them. ---
|
||||
- run: scripts/package_and_notarize_release.sh
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
# alt-tab-free [depaywall]: upload_symbols_to_appcenter.sh REMOVED — telemetry is off (PLAN §3.4).
|
||||
- run: scripts/update_appcast.sh
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
# alt-tab-free [depaywall]: update_readme_and_website.sh REMOVED — it queried the UPSTREAM
|
||||
# lwouis/alt-tab-macos GitHub API for stars/downloads and rewrote docs/contributors.md from the
|
||||
# UPSTREAM contributor graph (semantic mismatch for the fork) and dispatched the lwouis website.
|
||||
# Cosmetic only; dropped per PLAN §4.0/§4.3 (alongside update_website.sh, already removed).
|
||||
- run: npx semantic-release
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
- id: set_release_info
|
||||
run: scripts/extract_latest_changelog.sh
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
# alt-tab-free [depaywall]: belt-and-suspenders copy of Guard B + Guard C immediately before the
|
||||
# release is created. The load-bearing copy is at the top of the job (pre-tag-push); this second
|
||||
# copy guards the gh-release object itself (PLAN §4.4).
|
||||
- name: anti-relock + anti-conflict re-check (belt-and-suspenders)
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
run: |
|
||||
grep -q 'depaywall' src/pro/license/LicenseManager.swift \
|
||||
|| { echo "PAYWALL RELOCKED: depaywall marker missing from computeState()"; exit 1; }
|
||||
@@ -190,7 +208,7 @@ jobs:
|
||||
# (release.config.js has no @semantic-release/github plugin, so softprops is the sole release
|
||||
# creator — PLAN §4.3). repository defaults to ${{ github.repository }} = the fork.
|
||||
- uses: softprops/action-gh-release@v3
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true'
|
||||
if: github.event_name == 'push' && steps.release-gate.outputs.signing-ready == 'true' && steps.release-decision.outputs.will-release == 'true'
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Approved historical findings (exact fingerprints only, no patterns): readOnlyToken values in contributor/l10n scripts and an AppCenter
|
||||
# encryption key size constant, all introduced upstream before this fork. Any new or different finding is still reported.
|
||||
9147a4a864689ab94531c75b1b04d7c4f3581def:vendor/AppCenter/Sources/AppCenter/Internals/Util/MSACEncrypterPrivate.h:generic-api-key:20
|
||||
44007195b77d6809048c134c78a6d68d1895704f:scripts/update_readme_and_website.sh:generic-api-key:6
|
||||
528028d41bbbe57ea90b89a04a96a114872c405f:scripts/export_l10n_strings_to_poeditor.sh:generic-api-key:5
|
||||
d544ff3b7a6d4023ccd25783008836f50ec5b79d:scripts/import_l10n_strings_from_poeditor.sh:generic-api-key:5
|
||||
9a04c275cb9fba763c2272acc09055bf6476755d:scripts/update_contributors.sh:generic-api-key:5
|
||||
13c1cb0d000401df11a1c496b5273338db1886d7:scripts/import_l10n_strings_from_poeditor.sh:generic-api-key:6
|
||||
@@ -1,3 +1,5 @@
|
||||
## [100.2.1](https://github.com/KofTwentyTwo/CommandTabFree/compare/v100.2.0...v100.2.1) (2026-06-16)
|
||||
|
||||
# [100.2.0](https://github.com/KofTwentyTwo/CommandTabFree/compare/v100.1.0...v100.2.0) (2026-06-16)
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// docs: https://help.apple.com/xcode/mac/11.4/#/itcaec37c2a6
|
||||
|
||||
SWIFT_VERSION = 5.8
|
||||
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13
|
||||
SWIFT_TREAT_WARNINGS_AS_ERRORS = NO // replaced by the explicit flags below so the deprecation group can be downgraded after them
|
||||
// Raising the unit-test target to 12.0 turns uses of APIs deprecated in macOS 11/12 (kept for the 10.13 app) into warnings; only that
|
||||
// group is downgraded here (after -warnings-as-errors, which would otherwise win); every other warning stays an error.
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -warnings-as-errors -Wwarning DeprecatedDeclaration
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0 // unit-tests only: Xcode 27 supports 12.0+; the shipping app keeps 10.13 in base.xcconfig
|
||||
CODE_SIGNING_REQUIRED = NO // no need for codesign
|
||||
CODE_SIGNING_ALLOWED = NO // no need for codesign
|
||||
ONLY_ACTIVE_ARCH = YES // only need to run on the host machine
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# CI/CD readiness — CommandTabFree (fork of lwouis/alt-tab-macos)
|
||||
|
||||
**Date:** 2026-06-16 **Branch:** `master` **Repo:** `KofTwentyTwo/CommandTabFree` (public)
|
||||
**Status:** full release pipeline **proven** on the real runner — signed + notarized **v100.1.0** released (run `27643432063`, Developer ID `James Maes (2X834TJ5MA)`, `spctl`-accepted + stapled). Non-release pushes stay green via the ad-hoc build path (run `27634828800`).
|
||||
**Status:** publishing pipeline + Sparkle auto-update **LIVE** — signed + notarized **v100.2.0** released (Developer ID `James Maes (2X834TJ5MA)`, `spctl`-accepted + stapled), feed served on GitHub Pages (`koftwentytwo.github.io/CommandTabFree/appcast.xml`). The release apparatus (cert-import → sign-build → notarize → appcast → semantic-release → gh-release) is gated on a `will-release` signal (empty `VERSION.txt` ⇒ no release), so docs/ci pushes build+test without cutting a release or needing `[skip ci]`.
|
||||
**Authoritative design:** `docs/PLAN-maintained-fork.md` §4; owner checklist `docs/EXECUTION-STATUS.md` §3.4/§3.5.
|
||||
|
||||
This is the state after the CI-fix pass. The pass applied only **safe, owner-secret-independent**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SESSION-STATE — CommandTabFree (de-paywalled AltTab fork)
|
||||
|
||||
_Updated 2026-06-16. `master` carries the fork; the **full CI/CD publishing pipeline is LIVE** — signed + notarized **v100.1.0** released and Gatekeeper-accepted. Homebrew tap live._
|
||||
_Updated 2026-06-16. `master` carries the fork; the **full CI/CD publishing pipeline + auto-update are LIVE** — signed + notarized **v100.2.0** released (Gatekeeper-accepted), Sparkle feed served on GitHub Pages. Homebrew tap live._
|
||||
|
||||
## ✅ SIGNED + NOTARIZED RELEASE LIVE
|
||||
|
||||
@@ -8,12 +8,13 @@ The complete release pipeline ran green end-to-end (run `27643432063`, commit `2
|
||||
|
||||
**How signing is wired:** `config/local.xcconfig` sets `CODE_SIGN_IDENTITY` + `CODE_SIGN_STYLE = Manual` + `TEAM_ID 2X834TJ5MA`; the vendored Sparkle helpers (`vendor/Sparkle/Helpers/*`) are committed **re-signed** under the same Developer ID; `setup_ci_master.sh` imports the cert with an optional `APPLE_P12_PASSWORD`. The release secrets (`APPLE_P12_CERTIFICATE`/`APPLE_P12_PASSWORD`/`APPLE_ID`/`APPLE_PASSWORD`/`APPLE_TEAM_ID` + `SPARKLE_ED_PRIVATE_KEY`) are set in the `production` env. Getting here also required two CI fixes earlier this session: `d1e1b296` (regenerate base `Localizable.strings` to clear the genstrings gate) and `6070f0cb` (ad-hoc build until the cert existed).
|
||||
|
||||
**Remaining (NOT yet done):**
|
||||
- **Auto-update** (deferred by choice): `appcast.xml` is published to gh-pages, but the app's feed URL is still `fork.invalid` and `SUEnableAutomaticChecks=false`. To enable N→N+1 updates: set a real `DOMAIN` (kof22.com), serve gh-pages at `https://<DOMAIN>/appcast.xml`, flip checks on.
|
||||
- **Homebrew cask**: now notarized — drop the quarantine-strip postflight and bump the cask to v100.1.0.
|
||||
- **Upstream-sync**: `.fork-sync-state` reset to `v11.3.0` (the release tail no longer clobbers it — see ci_cd.yml). Before enabling upstream-sync, advance the cursor from the sync-merge flow (recording the UPSTREAM tag), and provision `SYNC_BOT_TOKEN` + an external cron. The 3 sync labels exist.
|
||||
**Auto-update is LIVE (shipped in v100.2.0):** `DOMAIN = koftwentytwo.github.io/CommandTabFree`, `SUEnableAutomaticChecks = true`, the in-tree `appcast.xml` skeleton removed + gitignored. GitHub Pages serves `https://koftwentytwo.github.io/CommandTabFree/appcast.xml` (gh-pages branch, published by `update_appcast.sh`); the feed carries v100.1.0 + v100.2.0 enclosures. A kof22.com custom domain can be added later (GitHub redirects the github.io URL — no broken builds). **Homebrew cask** bumped to v100.2.0 + quarantine-strip postflight dropped (`KofTwentyTwo/homebrew-tap`). **Release-gate decoupled:** the sign/notarize/release apparatus is gated on a `will-release` signal (empty `VERSION.txt` ⇒ no release), so docs/ci pushes build+test without cutting a release or needing `[skip ci]` (ci_cd.yml).
|
||||
|
||||
**Repo facts:** GitHub = `KofTwentyTwo/CommandTabFree` (local dir `~/Git.Local/kof22/alt-tab-free`, SSH remote). Latest release **v100.1.0** (signed + notarized zip). ⌘⇥ icon FINAL. Owner-gated checklist: `docs/TODO.md`.
|
||||
**Remaining (owner-gated, NOT automatable):**
|
||||
- **Upstream-sync**: provision `SYNC_BOT_TOKEN` + an external `repository_dispatch` cron; re-wire the `.fork-sync-state` cursor advance into the sync-merge flow (recording the UPSTREAM tag). `.fork-sync-state` = `v11.3.0`; the 3 sync labels exist.
|
||||
- **(Optional)** kof22.com custom domain for the appcast (one DNS CNAME → `koftwentytwo.github.io`).
|
||||
|
||||
**Repo facts:** GitHub = `KofTwentyTwo/CommandTabFree` (local dir `~/Git.Local/kof22/alt-tab-free`, SSH remote). Latest release **v100.2.0** (signed + notarized + auto-updating). ⌘⇥ icon FINAL. Owner-gated checklist: `docs/TODO.md`.
|
||||
|
||||
|
||||
## What this is
|
||||
|
||||
+14
-25
@@ -1,34 +1,23 @@
|
||||
# TODO — CommandTabFree
|
||||
|
||||
_Updated 2026-06-16. The **signed + notarized publishing pipeline is LIVE** — **v100.1.0** is released (Gatekeeper-accepted). Remaining items are auto-update, the Homebrew cask, and upstream-sync. Full detail: `docs/CICD-STATUS.md`, `docs/EXECUTION-STATUS.md` §3._
|
||||
_Updated 2026-06-16. The **publishing pipeline + auto-update are LIVE** — **v100.2.0** released (signed, notarized, Gatekeeper-accepted, auto-updating). The only substantial remaining work is upstream-sync automation. Full detail: `docs/CICD-STATUS.md`, `docs/EXECUTION-STATUS.md` §3._
|
||||
|
||||
## ✅ Signed + notarized releases — DONE (2026-06-16)
|
||||
- [x] Apple Developer ID `Developer ID Application: James Maes (2X834TJ5MA)` provisioned; cert + app-specific password in `production` secrets (`APPLE_P12_CERTIFICATE`/`APPLE_P12_PASSWORD`/`APPLE_ID`/`APPLE_PASSWORD`/`APPLE_TEAM_ID`).
|
||||
- [x] `CODE_SIGN_IDENTITY` + Manual style + `TEAM_ID` set in `config/local.xcconfig`.
|
||||
- [x] Vendored `vendor/Sparkle/Helpers/*` committed re-signed under the fork Developer ID.
|
||||
- [x] Full release tail proven on CI (run `27643432063`): signed → notarized → stapled → released. **v100.1.0** published.
|
||||
## ✅ Done this session (2026-06-16)
|
||||
- [x] **Signed + notarized releases** — Developer ID `James Maes (2X834TJ5MA)`; cert + creds in `production` secrets; `CODE_SIGN_IDENTITY`/Manual/`TEAM_ID` in `config/local.xcconfig`; vendored Sparkle helpers re-signed. Full tail proven (runs `27643432063` v100.1.0, `27647918163` v100.2.0).
|
||||
- [x] **Auto-update** — `DOMAIN = koftwentytwo.github.io/CommandTabFree`, `SUEnableAutomaticChecks = true`, in-tree `appcast.xml` removed + gitignored. GitHub Pages serves the gh-pages feed; v100.2.0 ships auto-updating.
|
||||
- [x] **Homebrew cask** — bumped to v100.2.0 + quarantine-strip postflight dropped (`KofTwentyTwo/homebrew-tap`).
|
||||
- [x] **Release-gate decouple** — sign/notarize/release apparatus gated on a `will-release` signal (empty `VERSION.txt` ⇒ no release); non-release pushes build+test without cutting a release or needing `[skip ci]`.
|
||||
- [x] genstrings gate fixed (`d1e1b296`); ad-hoc-until-cert build (`6070f0cb`); `.fork-sync-state` reset to `v11.3.0` + release tail no longer clobbers it; 3 sync labels created.
|
||||
|
||||
## Auto-update (deferred by choice — appcast already publishes to gh-pages)
|
||||
- [ ] **Real `DOMAIN`** — replace `fork.invalid` in `config/local.xcconfig` with the fork host (kof22.com).
|
||||
- [ ] Serve the gh-pages `appcast.xml` at `https://<DOMAIN>/appcast.xml` (CNAME the gh-pages site).
|
||||
- [ ] `git rm` the in-tree `appcast.xml` skeleton + gitignore it (feed is published out-of-tree to gh-pages); flip `SUEnableAutomaticChecks` back on (PLAN §3.2).
|
||||
|
||||
## Homebrew cask (now that builds are notarized)
|
||||
- [ ] Drop the quarantine-strip postflight (`xattr`) from `KofTwentyTwo/homebrew-tap` `Casks/commandtabfree.rb` — no longer needed for a notarized app.
|
||||
- [ ] Bump the cask to v100.1.0.
|
||||
|
||||
## Upstream-sync automation
|
||||
- [x] `.fork-sync-state` reset to `v11.3.0`; release tail no longer clobbers the cursor with the fork tag (`ci_cd.yml`).
|
||||
- [ ] **Re-wire the cursor advance** into the upstream-sync merge flow so it records the synced **UPSTREAM** tag (not the fork release tag) — `upstream_sync.yml` (PLAN §4.2).
|
||||
## Remaining — Upstream-sync automation (owner-gated)
|
||||
- [ ] **`SYNC_BOT_TOKEN`** — GitHub App installation token (or fork PAT with contents + pull-requests write). Default `GITHUB_TOKEN` does NOT re-trigger PR checks, so guards never run on the sync PR.
|
||||
- [ ] **External cron** — hit `repository_dispatch` (type `upstream-sync`) from an external scheduler (immune to GitHub's 60-day in-repo `schedule:` auto-disable).
|
||||
- [x] Sync PR labels `sync` / `conflict` / `chokepoint-refresh` created.
|
||||
- [ ] **Re-wire the `.fork-sync-state` cursor advance** into the upstream-sync merge flow so it records the synced **UPSTREAM** tag (not the fork release tag) — `upstream_sync.yml` (PLAN §4.2).
|
||||
|
||||
## Bootstrap acceptance tests (after the above)
|
||||
## Optional
|
||||
- [ ] kof22.com custom domain for the appcast (one DNS CNAME → `koftwentytwo.github.io`; GitHub redirects the github.io URL, so existing builds keep updating).
|
||||
|
||||
## Bootstrap acceptance tests (after upstream-sync is wired)
|
||||
- [ ] Sync PR through the bot shows guard checks running.
|
||||
- [ ] A deliberately-relocked branch goes RED on `guard.yml`.
|
||||
- [ ] Fork build N→N+1 auto-updates via Sparkle (needs DOMAIN/appcast live).
|
||||
|
||||
## Done earlier this session (2026-06-16)
|
||||
- [x] genstrings consistency gate fixed — regenerated base `Localizable.strings` from source (`d1e1b296`).
|
||||
- [x] `build_app.sh` builds ad-hoc until cert provisioned; was failing on upstream's Developer ID (`6070f0cb`). Push CI green (run `27634828800`).
|
||||
- [x] Fork build N→N+1 auto-updates via Sparkle — infra live (feed served + v100.2.0 auto-update-enabled); end-to-end N→N+1 confirmed on the next release.
|
||||
Reference in new issue
Block a user