Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,21 @@ jobs:
- name: Package Release app
run: ./script/package_release.sh

- name: Create GitHub Release
- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
tag_name="${GITHUB_REF_NAME:-manual-release}"
gh release create "$tag_name" \
dist/*.zip \
dist/*.sha256 \
--title "CleanMac $tag_name" \
--notes "Unsigned CleanMac build. Download the zip and verify it with the attached sha256 file."
if gh release view "$tag_name" >/dev/null 2>&1; then
echo "Release $tag_name already exists; replacing matching assets."
gh release upload "$tag_name" \
dist/*.zip \
dist/*.sha256 \
--clobber
else
gh release create "$tag_name" \
dist/*.zip \
dist/*.sha256 \
--title "CleanMac $tag_name" \
--notes "Unsigned CleanMac build. Download the zip and verify it with the attached sha256 file."
fi
65 changes: 29 additions & 36 deletions contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,52 @@

## Task

- ID: TASK-041
- Title: CleanMac v0.3.0 release
- ID: TASK-042
- Title: Idempotent GitHub Release publishing
- Mode: continue

## Planner Notes

- Why this task now: the feature branch has a green CI run and contains several user-visible additions since v0.2.1.
- Expected value: a reproducible v0.3.0 package and GitHub release that match the current app and make the new functionality available together.
- Main risk: publishing an artifact from an unmerged or differently versioned commit, attaching stale ZIP files, or implying Apple notarization when only ad-hoc signing is available.
- Release assumption: the new feature set warrants minor version 0.3.0 with build number 4; GitHub release copy remains English.
- Why this task now: the `v0.3.0` tag workflow packaged successfully but failed when it tried to create a release that had already been published manually.
- Expected value: future tag runs can safely create a missing release or refresh matching assets on an existing release without sending a false build-failure notification.
- Main risk: overwriting release notes or introducing a second release while handling the existing-release path.
- Safety choice: preserve existing release title/notes and replace only matching ZIP/checksum assets with `gh release upload --clobber`.

## Builder Scope

- Allowed files:
- `CleanMac.xcodeproj/project.pbxproj`
- generated `dist/CleanMac.app`, ZIP, and SHA-256 assets;
- GitHub PR/release metadata;
- Loop documentation files
- `.github/workflows/release.yml`;
- Loop documentation files.
- Allowed commands:
- read-only source, Git, bundle, signing, and release inspection;
- `swift test --package-path CleanMacCore`;
- Debug `xcodebuild`;
- `./script/build_and_run.sh --verify`;
- `./script/package_release.sh`;
- PR update/merge and GitHub Release publishing explicitly approved by the user;
- `git diff --check`.
- read-only Actions run/log/release inspection;
- YAML and embedded shell syntax checks;
- execute the approved existing-release branch against `v0.3.0` with the already verified local assets;
- standard Git/PR/CI checks and merge after green status.
- Out of scope:
- new product behavior, cleanup execution, permission changes, Developer ID signing, notarization, dependencies, or architecture changes.
- Dependencies allowed: no external dependencies; system CryptoKit only
- Destructive actions allowed: replace generated ignored `dist/` artifacts only; no user-data cleanup
- moving or recreating `v0.3.0`, changing app code/assets, changing release notes, adding secrets, signing, notarization, or deleting Actions history.
- Dependencies allowed: none
- Destructive actions allowed: replace same-named `v0.3.0` release assets only; no user files

## Evaluator Checklist

- Done criteria:
- Debug and Release bundles both report version 0.3.0 and build 4.
- The feature PR is green and merged into `main` before tagging.
- The final arm64 ZIP is built from the tagged main commit, passes strict signature verification after fresh extraction, and has a matching SHA-256 file.
- GitHub Release v0.3.0 is public, latest, not prerelease, uses English notes, and contains exactly the verified ZIP and SHA-256 assets.
- The distribution note clearly says the build is ad-hoc signed and not Apple-notarized.
- Missing releases still use `gh release create` with the existing title and notes.
- Existing releases use `gh release upload --clobber` and keep their metadata.
- The exact existing-release shell path succeeds for `v0.3.0` and leaves exactly the verified ZIP and checksum assets.
- Workflow YAML, embedded shell, PR CI, and final GitHub release metadata checks pass.
- Required verification:
- `swift test --package-path CleanMacCore`;
- `./script/build_and_run.sh --verify`;
- `./script/package_release.sh`;
- extracted bundle version/build/architecture/signature inspection;
- local and downloaded SHA-256 verification;
- GitHub CI and release metadata inspection;
- `git diff --check`.
- Ruby YAML parse;
- extracted publish-step `bash -n`;
- controlled existing-release execution for `v0.3.0`;
- clean downloaded SHA-256 verification;
- `git diff --check`;
- green GitHub PR checks.
- Manual checks:
- Confirm the About metadata and packaged bundle both show 0.3.0 (4).
- Confirm Gatekeeper limitations are documented without recommending security bypasses.
- Confirm `v0.3.0` remains public/latest with its English release notes unchanged.
- Confirm the historical failed run remains only as immutable history and is not presented as a broken app build.

## Result

- Status: complete
- Verification result: 40 SwiftPM tests, local Debug build/launch, green PR CI, main merge, Release packaging, strict fresh-extraction signature verification, 0.3.0 (4) metadata, arm64 architecture, portable SHA-256 verification, and downloaded GitHub asset verification all passed.
- Notes: public latest release `v0.3.0` targets merge commit `515f591`, contains the verified ZIP and portable checksum, and is transparently labeled ad-hoc signed and not notarized because no Developer ID identity is installed.
- Verification result: workflow YAML parsed; embedded shell passed `bash -n`; the stubbed missing-release path called `gh release create`; the exact existing-release path successfully refreshed `v0.3.0` assets through `--clobber`; English metadata remained in place; the clean downloaded checksum passed; final diff and PR CI passed.
- Notes: the historical run cannot be made green by rerunning because reruns use the workflow stored at the tagged commit; this task fixes future executions without rewriting the published tag.
10 changes: 10 additions & 0 deletions progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,13 @@ Append-only history. Do not erase previous entries.
- Next step: Install the packaged app in `/Applications`; Developer ID signing and Apple notarization remain the next distribution upgrade when credentials are available.
- Bottleneck: This Mac has no Developer ID identity, so Gatekeeper can reject the downloaded ad-hoc build even though its integrity and signature are valid.
- Handoff: `dist/` contains the current 0.3.0 app and verified release assets. No security setting, Login Item, privacy permission, cleanup selection, application, or user file was changed.

## 2026-07-12 - TASK-042 - Idempotent GitHub Release publishing

- What changed: Replaced the unconditional `gh release create` step with an idempotent publish step. Missing tag releases keep the existing creation behavior; existing releases preserve their title/notes and replace matching ZIP/checksum assets through `gh release upload --clobber`.
- Files touched: `.github/workflows/release.yml`, `contract.md`, `project-analysis.md`, `roadmap.md`, `progress.md`, `trace.md`.
- Checks run: GitHub failure-log inspection; Ruby YAML parse; extracted publish-step `bash -n`; stubbed missing-release branch; exact existing-release branch against `v0.3.0`; clean downloaded `shasum -a 256 -c`; release metadata/asset inspection; `git diff --check`; GitHub PR CI.
- Result: Passed. The original red run was a duplicate-publication error after a successful package, not an app build failure. `v0.3.0` remains public/latest with its English notes and exactly the verified ZIP and portable checksum assets.
- Next step: Use the normal tag workflow for the next version; no manual pre-creation is required, but the workflow now safely tolerates it.
- Bottleneck: The historical `v0.3.0` failure cannot be turned green without rewriting the tag because GitHub reruns the workflow definition stored at that tagged commit.
- Handoff: No release tag, note, app binary, security setting, secret, or user file was changed; only same-named verified release assets were refreshed.
2 changes: 1 addition & 1 deletion project-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ CleanMac is a macOS menu bar and windowed system cleanup utility. The project wa
- `script/build_and_run.sh`: local Debug build and launch entrypoint.
- `script/package_release.sh`: local Release `.app`, `.zip`, and `.sha256` packaging.
- `.github/workflows/ci.yml`: GitHub CI for core tests, Debug app build, and unsigned Release artifact.
- `.github/workflows/release.yml`: tag-driven GitHub Release workflow for unsigned/ad-hoc zip and sha256 assets, with optional Developer ID signing/notarization when private secrets are configured.
- `.github/workflows/release.yml`: idempotent tag-driven GitHub Release workflow for unsigned/ad-hoc zip and sha256 assets, with optional Developer ID signing/notarization when private secrets are configured; it creates a missing release or replaces matching assets on an existing release without overwriting its notes.
- `docs/` and `Design/`: icon and project assets.

## Tech Stack
Expand Down
14 changes: 14 additions & 0 deletions roadmap.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Roadmap

- [x] ID: TASK-042
Title: Idempotent GitHub Release publishing
Goal: Prevent a successful tag package from reporting failure when the corresponding GitHub Release already exists.
What to do: Detect the release first, create it only when missing, otherwise replace same-named assets with `gh release upload --clobber` while preserving notes.
Files: `.github/workflows/release.yml`, Loop docs
Definition of done: missing and existing release paths are explicit; the existing v0.3.0 path succeeds without duplicate releases or metadata loss; PR CI is green.
Verification: YAML parse; embedded shell syntax; controlled v0.3.0 asset refresh; clean downloaded checksum verification; GitHub PR checks
Priority: high
Impact: medium
Risk: low
Effort: small
Confidence: high
Score: medium impact / low risk / small

- [x] ID: TASK-041
Title: CleanMac v0.3.0 release
Goal: Ship the verified disk analysis, onboarding, system dashboard, developer cleanup, and duplicate finder as the next feature release.
Expand Down
7 changes: 7 additions & 0 deletions trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,10 @@ Append-only trace of failures, restarts, and judgment divergences.
- Cause: `package_release.sh` passed the absolute `ZIP_PATH` directly to `shasum`.
- Fix: generate the checksum from inside `dist` using only the ZIP basename, replace the GitHub checksum asset, and repeat verification from a clean temporary download directory.
- Status: resolved; the published checksum now references `CleanMac-515f591-unsigned.zip`, and clean-directory verification checks the downloaded ZIP successfully.

## 2026-07-12 - TASK-042 - Existing release made tag workflow fail

- Symptom: the `v0.3.0` Release workflow packaged the app successfully but the final job failed with `a release with the same tag name already exists: v0.3.0`.
- Cause: the workflow always called `gh release create`, while `v0.3.0` had already been published and verified through the approved local release flow.
- Fix: query the tag first; create only when missing, otherwise upload the same ZIP/checksum names with `--clobber` while preserving the current title and English notes.
- Status: resolved for future executions; the exact existing-release branch refreshed both `v0.3.0` assets successfully and clean-download SHA-256 verification passed. The historical failed run remains unchanged because reruns use the workflow stored at the tagged commit.