From d2fc41ce85186c58ec71385fd880a2d3ed5d9598 Mon Sep 17 00:00:00 2001 From: StuBehan Date: Fri, 1 May 2026 00:35:03 +0200 Subject: [PATCH] chore: switch to release-please + fix broken release workflow The existing release.yml referenced notifier/*.swift which were deleted in the single-binary refactor, and auto-tag.yml read CFBundleShortVersionString from notifier/Info.plist (now panel/Info.plist). Both have been silently broken since refactor/single-binary merged. This commit replaces them with a release-please-driven flow: - release-please.yml watches conventional commits on main, opens a Release PR that bumps .release-please-manifest.json + regenerates CHANGELOG.md. Merging that PR creates the tag and GitHub Release. - release.yml triggers on the tag, builds arm64 + x86_64 sequentially via build.sh's existing -target flag, lipos into a universal binary, re-signs (lipo invalidates the per-arch ad-hoc sigs), stamps the version from the tag into Info.plist, and packages a self-contained tarball. - The tarball includes the prebuilt bundle plus notify.sh / phrases / install.sh / uninstall.sh / notify.conf.example so users who download a release don't need swiftc. - install.sh now skips the swiftc rebuild when build/stack-nudge.app is already present, so the release-tarball install path runs in seconds instead of building from source on the user's machine. - auto-tag.yml is removed; release-please handles tagging now. Bootstrap manifest version is 1.1.2 to match the latest tag and the current Info.plist. End-to-end verified locally: tarball extracts, prebuilt-detection finds the bundle, the resulting mach-o is a valid arm64 executable, and shellcheck still passes on the extracted install.sh. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/auto-tag.yml | 39 ---------- .github/workflows/release-please.yml | 24 ++++++ .github/workflows/release.yml | 105 +++++++++++++-------------- .release-please-config.json | 12 +++ .release-please-manifest.json | 3 + install.sh | 30 +++++--- 6 files changed, 107 insertions(+), 106 deletions(-) delete mode 100644 .github/workflows/auto-tag.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-config.json create mode 100644 .release-please-manifest.json diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml deleted file mode 100644 index 9ac297e..0000000 --- a/.github/workflows/auto-tag.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Auto Tag on Merge - -on: - push: - branches: - - main - -jobs: - tag: - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - uses: actions/checkout@v6 - - - name: Read version from Info.plist - id: version - run: | - VERSION=$(grep -A1 'CFBundleShortVersionString' notifier/Info.plist \ - | tail -1 | sed 's/.*\(.*\)<\/string>.*/\1/' | tr -d '[:space:]') - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Check if tag already exists - id: tag_check - run: | - if git ls-remote --tags origin "refs/tags/v${{ steps.version.outputs.version }}" | grep -q .; then - echo "exists=true" >> $GITHUB_OUTPUT - else - echo "exists=false" >> $GITHUB_OUTPUT - fi - - - name: Create and push tag - if: steps.tag_check.outputs.exists == 'false' - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag "v${{ steps.version.outputs.version }}" - git push origin "v${{ steps.version.outputs.version }}" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..15b1e18 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,24 @@ +name: release-please + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + # Watches conventional commits on main, opens (and updates) a Release PR + # that bumps .release-please-manifest.json + regenerates CHANGELOG.md. + # When the Release PR is merged, this action creates the git tag (vX.Y.Z) + # and the GitHub Release. The release.yml workflow then triggers on that + # tag to attach prebuilt asset bundles. + - uses: googleapis/release-please-action@v4 + with: + config-file: .release-please-config.json + manifest-file: .release-please-manifest.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1dd315e..950090c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,70 +6,65 @@ on: - 'v*' jobs: - build: - name: Build (${{ matrix.arch }}) + release: + name: Build universal binary, attach to release runs-on: macos-15 - strategy: - matrix: - arch: [arm64, x86_64] - + permissions: + contents: write steps: - uses: actions/checkout@v6 - - name: Build stack-nudge.app + # Stamp the tag's version into Info.plist so the bundled app advertises + # the right version regardless of what's checked into main. + - name: Stamp version from tag run: | - APP="stack-nudge.app/Contents" - mkdir -p "$APP/MacOS" - swiftc \ - notifier/main.swift \ - notifier/Config.swift \ - notifier/Notifier.swift \ - notifier/AppActivator.swift \ - -o "$APP/MacOS/stack-nudge" \ - -target ${{ matrix.arch }}-apple-macos12.0 \ - -framework Foundation \ - -framework AppKit \ - -framework ScriptingBridge - cp notifier/Info.plist "$APP/Info.plist" - mkdir -p "$APP/Resources" - cp notifier/Icon.icns "$APP/Resources/Icon.icns" + VERSION="${GITHUB_REF_NAME#v}" + /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" panel/Info.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" panel/Info.plist - - name: Verify - run: file stack-nudge.app/Contents/MacOS/stack-nudge + # Build each arch sequentially via swiftc's -target flag (cross-compiles + # fine on macos-15 runners). Stash the per-arch mach-o so we can lipo + # them into a universal binary afterward. + - name: Build arm64 + run: bash build.sh arm64 + - name: Stash arm64 binary + run: cp build/stack-nudge.app/Contents/MacOS/stack-nudge /tmp/stack-nudge-arm64 - - name: Package - run: | - mkdir release - cp -r stack-nudge.app release/ - cp notify.sh release/ - tar czf stack-nudge-${{ matrix.arch }}.tar.gz release - shasum -a 256 stack-nudge-${{ matrix.arch }}.tar.gz \ - | awk '{print $1}' > stack-nudge-${{ matrix.arch }}.sha256 - - - uses: actions/upload-artifact@v7 - with: - name: stack-nudge-${{ matrix.arch }} - path: | - stack-nudge-${{ matrix.arch }}.tar.gz - stack-nudge-${{ matrix.arch }}.sha256 + - name: Build x86_64 + run: bash build.sh x86_64 + - name: Stash x86_64 binary + run: cp build/stack-nudge.app/Contents/MacOS/stack-nudge /tmp/stack-nudge-x86_64 - release: - needs: build - runs-on: ubuntu-latest - permissions: - contents: write + - name: Combine into universal binary + run: | + # The x86_64 build is what's currently in build/, so just replace + # its mach-o with the lipo'd universal one. + lipo -create /tmp/stack-nudge-arm64 /tmp/stack-nudge-x86_64 \ + -output build/stack-nudge.app/Contents/MacOS/stack-nudge + # lipo invalidates the per-arch ad-hoc signatures — re-sign the bundle. + codesign --force --deep --sign - build/stack-nudge.app + file build/stack-nudge.app/Contents/MacOS/stack-nudge - steps: - - uses: actions/download-artifact@v8 - with: - merge-multiple: true + # Tarball includes the prebuilt bundle plus everything install.sh needs + # at runtime. install.sh skips the build step when build/stack-nudge.app + # is already present, so users who download the release run a fast + # install (no swiftc dependency on the user's machine). + - name: Package + run: | + VERSION="${GITHUB_REF_NAME#v}" + mkdir -p release/build + cp -R build/stack-nudge.app release/build/ + cp notify.sh install.sh uninstall.sh notify.conf.example release/ + cp -R phrases release/ + tar czf "stack-nudge-${VERSION}-universal.tar.gz" -C release . + shasum -a 256 "stack-nudge-${VERSION}-universal.tar.gz" \ + | awk '{print $1}' > "stack-nudge-${VERSION}-universal.sha256" - - name: Create GitHub Release - uses: softprops/action-gh-release@v3 + # release-please creates the GitHub Release before this workflow runs, + # so action-gh-release attaches assets to the existing release rather + # than creating a duplicate. + - uses: softprops/action-gh-release@v3 with: - generate_release_notes: true files: | - stack-nudge-arm64.tar.gz - stack-nudge-arm64.sha256 - stack-nudge-x86_64.tar.gz - stack-nudge-x86_64.sha256 + stack-nudge-*-universal.tar.gz + stack-nudge-*-universal.sha256 diff --git a/.release-please-config.json b/.release-please-config.json new file mode 100644 index 0000000..ecc7338 --- /dev/null +++ b/.release-please-config.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "simple", + "include-component-in-tag": false, + "packages": { + ".": { + "package-name": "stack-nudge", + "release-type": "simple", + "changelog-path": "CHANGELOG.md" + } + } +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..53b7bc9 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.1.2" +} diff --git a/install.sh b/install.sh index e56eefb..8bf8b39 100755 --- a/install.sh +++ b/install.sh @@ -11,24 +11,30 @@ echo "Installing stack-nudge..." mkdir -p "$INSTALL_DIR" -# Build and install the native app bundle (single persistent binary). -# build.sh's output (stdout + stderr — Swift emits ~120 lines of UserNotifications -# deprecation warnings on every build) is redirected to a log so the install -# transcript stays scannable. On real build failure the log's last 20 lines -# are dumped so the actual error doesn't get hidden. +# Build (or use a prebuilt) native app bundle. Release tarballs ship with a +# universal binary already at build/stack-nudge.app — in that case skip the +# rebuild so users who download a release don't need swiftc on their machine. +# build.sh's output (Swift emits ~120 lines of UserNotifications deprecation +# warnings on every build) goes to a log so the install transcript stays +# scannable. On real build failure the log's last 20 lines are dumped. +PREBUILT_APP="$SCRIPT_DIR/build/stack-nudge.app" BUILD_LOG="/tmp/stack-nudge-install-build.log" if [[ "$(uname -s)" == "Darwin" ]]; then echo "" - echo "Building stack-nudge.app..." - if ! bash "$SCRIPT_DIR/build.sh" > "$BUILD_LOG" 2>&1; then - echo "" - echo " ✗ Build failed. Last 20 lines of $BUILD_LOG:" - tail -20 "$BUILD_LOG" | sed 's/^/ /' - exit 1 + if [[ -d "$PREBUILT_APP" ]]; then + echo "Using prebuilt stack-nudge.app from release bundle..." + else + echo "Building stack-nudge.app..." + if ! bash "$SCRIPT_DIR/build.sh" > "$BUILD_LOG" 2>&1; then + echo "" + echo " ✗ Build failed. Last 20 lines of $BUILD_LOG:" + tail -20 "$BUILD_LOG" | sed 's/^/ /' + exit 1 + fi fi rm -rf "$HOME/Applications/stack-nudge.app" rm -rf "$HOME/Applications/stack-nudge-panel.app" # clean up old panel binary - cp -r "$SCRIPT_DIR/build/stack-nudge.app" "$HOME/Applications/stack-nudge.app" + cp -r "$PREBUILT_APP" "$HOME/Applications/stack-nudge.app" echo " Installed stack-nudge.app -> ~/Applications/stack-nudge.app" fi