diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afa7a57d..ef1db458 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,211 +1,40 @@ -name: release +name: Release on: workflow_dispatch: inputs: - tag: - description: "Tag to (re)release (e.g. v0.1.0)" + version: + description: SemVer to release required: true type: string - include_macos: - description: "Also rebuild and upload the macOS archive" - required: false - default: false - type: boolean -permissions: - contents: write +permissions: {} jobs: - macos-release: - if: ${{ inputs.include_macos }} - runs-on: macos-15 - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - name: Determine tag - id: tag - shell: bash - run: | - if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then - echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" - else - echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" - fi - - - name: Checkout release tag - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git checkout ${{ steps.tag.outputs.tag }} - - - name: Resolve packages - run: swift package resolve - - - name: Patch dependencies - run: scripts/patch-deps.sh - - - name: Sync version - run: scripts/generate-version.sh - - - name: Build - run: | - rm -rf dist - OUTPUT_DIR=dist scripts/build-universal.sh - - - name: Package artifact - run: | - ( - cd dist - shopt -s nullglob - bundles=( *.bundle ) - if [ ${#bundles[@]} -gt 0 ]; then - zip -r imsg-macos.zip imsg imsg-bridge-helper.dylib "${bundles[@]}" - else - zip -r imsg-macos.zip imsg imsg-bridge-helper.dylib - fi - ) - - - name: Publish release assets - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ steps.tag.outputs.tag }} - name: ${{ steps.tag.outputs.tag }} - files: dist/imsg-macos.zip - fail_on_unmatched_files: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Update GitHub release notes from CHANGELOG - shell: bash - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: ${{ steps.tag.outputs.tag }} - run: | - version="${TAG#v}" - notes_file="/tmp/release-notes.md" - - awk -v v="$version" ' - $0 ~ ("^## " v "($|[[:space:]]-)") { in_section=1; next } - in_section && $0 ~ "^## " { exit } - in_section { print } - ' CHANGELOG.md > "$notes_file" - - if ! grep -q '[^[:space:]]' "$notes_file"; then - echo "No CHANGELOG.md section found for version $version" >&2 - exit 1 - fi - - gh release edit "$TAG" --notes-file "$notes_file" - - linux-release: - runs-on: ubuntu-latest - container: swift:6.3.3-noble - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - name: Determine tag - id: tag - shell: bash - run: | - if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then - echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" - else - echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" - fi - - - name: Checkout release tag - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git checkout ${{ steps.tag.outputs.tag }} - - - name: Install Python - run: | - apt-get update - apt-get install -y --no-install-recommends python3 - - - name: Resolve packages - run: swift package resolve - - - name: Patch dependencies - run: scripts/patch-deps.sh - - - name: Sync version - run: scripts/generate-version.sh - - - name: Build Linux archive - run: | - rm -rf dist - OUTPUT_DIR=dist scripts/build-linux.sh - - - name: Publish Linux release asset - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ steps.tag.outputs.tag }} - name: ${{ steps.tag.outputs.tag }} - files: dist/imsg-linux-x86_64.tar.gz - fail_on_unmatched_files: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - update-homebrew-tap: - if: ${{ inputs.include_macos }} - runs-on: ubuntu-latest - needs: macos-release - steps: - - name: Resolve release tag - run: echo "RELEASE_TAG=${{ inputs.tag }}" >> "$GITHUB_ENV" - - - name: Dispatch tap formula update - env: - GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} - run: | - if [ -z "$GH_TOKEN" ]; then - echo "::error::Set HOMEBREW_TAP_TOKEN with workflow access to steipete/homebrew-tap" - exit 1 - fi - - request_id="imsg-${RELEASE_TAG}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" - expected_title="Update imsg for ${RELEASE_TAG} (${request_id})" - - gh workflow run update-formula.yml \ - --repo steipete/homebrew-tap \ - --ref main \ - -f formula=imsg \ - -f tag="$RELEASE_TAG" \ - -f repository=openclaw/imsg \ - -f macos_artifact=imsg-macos.zip \ - -f request_id="$request_id" - - run_id="" - for _ in {1..30}; do - run_id=$(gh run list \ - --repo steipete/homebrew-tap \ - --workflow update-formula.yml \ - --branch main \ - --event workflow_dispatch \ - --limit 20 \ - --json databaseId,displayTitle \ - --jq ".[] | select(.displayTitle == \"$expected_title\") | .databaseId" | head -n1) - if [ -n "$run_id" ]; then - break - fi - sleep 5 - done - - if [ -z "$run_id" ]; then - echo "::error::Could not find tap workflow run with title: $expected_title" - exit 1 - fi - - gh run watch "$run_id" \ - --repo steipete/homebrew-tap \ - --exit-status \ - --interval 10 + release: + permissions: + actions: read + checks: read + contents: write + pull-requests: write + statuses: read + uses: openclaw/release-workflows/.github/workflows/release-swift-cli.yml@v1 + with: + version: ${{ inputs.version }} + repository-type: personal + homebrew-tap: steipete/homebrew-tap + homebrew-formula: imsg + binary-name: imsg + helper-name: imsg-bridge-helper.dylib + binary-identifier: com.steipete.imsg + helper-identifier: com.steipete.imsg.bridge-helper + macos-archive-name: imsg-macos.zip + linux-archive-name: imsg-linux-x86_64.tar.gz + ci-check-events: '["push","pull_request"]' + secrets: + MACOS_SIGNING_P12: ${{ secrets.MACOS_SIGNING_P12 }} + MACOS_SIGNING_P12_PASSWORD: ${{ secrets.MACOS_SIGNING_P12_PASSWORD }} + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} + ASC_PRIVATE_KEY_P8: ${{ secrets.ASC_PRIVATE_KEY_P8 }} + TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/Tests/imsgTests/ReleasePackagingTests.swift b/Tests/imsgTests/ReleasePackagingTests.swift index 7304f0f9..f2f059fe 100644 --- a/Tests/imsgTests/ReleasePackagingTests.swift +++ b/Tests/imsgTests/ReleasePackagingTests.swift @@ -5,9 +5,15 @@ import Testing func releaseWorkflowPackagesUniversalBuildOutput() throws { let workflow = try readRepositoryFile(".github/workflows/release.yml") - #expect(workflow.contains("OUTPUT_DIR=dist scripts/build-universal.sh")) - #expect(workflow.contains("files: dist/imsg-macos.zip")) - #expect(workflow.contains("imsg-bridge-helper.dylib")) + #expect( + workflow.contains( + "uses: openclaw/release-workflows/.github/workflows/release-swift-cli.yml@v1")) + #expect(workflow.contains("macos-archive-name: imsg-macos.zip")) + #expect(workflow.contains("helper-name: imsg-bridge-helper.dylib")) + #expect(workflow.contains("binary-identifier: com.steipete.imsg")) + #expect(workflow.contains("helper-identifier: com.steipete.imsg.bridge-helper")) + #expect(workflow.contains("MACOS_SIGNING_P12: ${{ secrets.MACOS_SIGNING_P12 }}")) + #expect(workflow.contains("TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}")) #expect(!workflow.contains("swift build -c release --product imsg")) #expect(!workflow.contains("cp .build/release/imsg dist/imsg")) } @@ -36,28 +42,6 @@ func universalBuildScriptShipsArm64eHelperSlice() throws { #expect(script.contains(#"cp "${DIST_DIR}/${HELPER_NAME}" "$OUTPUT_DIR/$HELPER_NAME""#)) } -@Test -func signAndNotarizeScriptDefaultsHelperToArm64e() throws { - let script = try readRepositoryFile("scripts/sign-and-notarize.sh") - - // The notarize path defaults the helper to arm64e as well, and its lipo guard - // must validate the HELPER arch list — not the CLI ARCH_LIST, which omits - // arm64e. Assert the loop and its lipo check as one contiguous block so this - // can't pass by matching the separate clang-args HELPER_ARCH_LIST loop. - #expect(script.contains(#"HELPER_ARCHES_VALUE=${HELPER_ARCHES:-"arm64e arm64 x86_64"}"#)) - #expect(script.contains("--scratch-path")) - #expect(script.contains("--show-bin-path")) - #expect(script.contains(#"for bundle in "${PRODUCT_DIRS[0]}"/*.bundle"#)) - #expect(!script.contains(#".build/${ARCH}-apple-macosx"#)) - #expect( - script.contains( - """ - for ARCH in "${HELPER_ARCH_LIST[@]}"; do - if ! lipo -archs "$DIST_DIR/$HELPER_NAME" | tr ' ' '\\n' | grep -Fxq "$ARCH"; then - echo "Helper missing required architecture slice: $ARCH" >&2 - """)) -} - @Test func linuxReleaseStaticallyLinksSwiftRuntime() throws { let script = try readRepositoryFile("scripts/build-linux.sh") @@ -79,17 +63,14 @@ func dependencyPatchTargetsPhoneNumberKitV5BundleResource() throws { func bridgeHelperBuildsUseRelocatableInstallName() throws { let developmentBuild = try readRepositoryFile("Makefile") let universalBuild = try readRepositoryFile("scripts/build-universal.sh") - let notarizedBuild = try readRepositoryFile("scripts/sign-and-notarize.sh") #expect(developmentBuild.contains("-install_name @rpath/imsg-bridge-helper.dylib")) - for script in [universalBuild, notarizedBuild] { - #expect(script.contains(#"-install_name "@rpath/${HELPER_NAME}""#)) - } + #expect(universalBuild.contains(#"-install_name "@rpath/${HELPER_NAME}""#)) } @Test func bridgeHelperBuildsLinkRichLinkFrameworks() throws { - for path in ["Makefile", "scripts/build-universal.sh", "scripts/sign-and-notarize.sh"] { + for path in ["Makefile", "scripts/build-universal.sh"] { let contents = try readRepositoryFile(path) #expect(contents.contains("-framework ImageIO")) #expect(contents.contains("-framework LinkPresentation")) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 2bff5f2e..ad33b48b 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -1,68 +1,38 @@ ---- -title: Releasing -description: "Cutting an imsg release: changelog, version bump, signed/notarized build, tag, GitHub release, Homebrew tap update." ---- +# Releasing imsg -## Release notes source -- GitHub Release notes come from `CHANGELOG.md` for the matching version section (`## X.Y.Z - YYYY-MM-DD`). -- Keep the unreleased section at the top. During a release train it may be - versioned, for example `## 0.8.0 - Unreleased`; before tagging, change it to - `## X.Y.Z - YYYY-MM-DD`. +imsg uses the fleet-standard Swift CLI workflow from `openclaw/release-workflows@v1`. The repository caller supplies imsg's stable artifact, signing-identifier, and Homebrew contracts; the shared workflow owns the protected-source freeze, annotated release tag, builds, signing and notarization, independent verification, exact publication, Homebrew handoff, and closeout. -## Steps -1. Update `CHANGELOG.md` and version - - Move entries from `Unreleased` into a new `## X.Y.Z - YYYY-MM-DD` section, - or date the existing `## X.Y.Z - Unreleased` section. - - Credit contributors (e.g. `thanks @user`). - - Update `version.env` to `X.Y.Z`. - - Run `scripts/generate-version.sh` (also refreshes `Sources/imsg/Resources/Info.plist`). -2. Ensure CI is green on `main` - - `make lint` - - `make test` - - GitHub Actions `linux-read-core` - - `make format` (optional, if formatting changes are expected) -3. Build, sign, and notarize - - Requires `APP_STORE_CONNECT_API_KEY_P8`, `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`; - for automation, provide these through `MAC_RELEASE_OP_ITEM` + `MAC_RELEASE_OP_FIELDS`. - - Run `scripts/sign-and-notarize.sh` through the shared release wrapper, for example - `MAC_RELEASE=/path/to/agent-scripts/skills/release-mac-app/scripts/mac-release; "$MAC_RELEASE" codesign-run --with-package-secrets -- scripts/sign-and-notarize.sh`. - - Set `MAC_RELEASE_CODESIGN_IDENTITY`, `MAC_RELEASE_CODESIGN_KEYCHAIN_MANAGED=1`, and either direct - `MAC_RELEASE_CODESIGN_KEYCHAIN` + `MAC_RELEASE_CODESIGN_KEYCHAIN_PASSWORD` or `MAC_RELEASE_CODESIGN_OP_ITEM`. - The wrapper aborts before packaging if its locked-keychain canary cannot sign noninteractively and restore - the original user keychain search list. - - The script outputs `/tmp/imsg-macos.zip` by default. - - Linux release archives are built by `.github/workflows/release.yml` with - `scripts/build-linux.sh` and uploaded as `imsg-linux-x86_64.tar.gz`. - - Verify the zip contains `imsg-bridge-helper.dylib` and required SwiftPM - bundles (e.g. `PhoneNumberKit_PhoneNumberKit.bundle`). - - Verify entitlements/signing: - - `unzip -q /tmp/imsg-macos.zip -d /tmp/imsg-check` - - `codesign -d --entitlements :- /tmp/imsg-check/imsg` - - `codesign --verify --strict --verbose=4 /tmp/imsg-check/imsg-bridge-helper.dylib` - - `spctl -a -t exec -vv /tmp/imsg-check/imsg` -4. Tag, push, and publish - - `git tag -a vX.Y.Z -m "vX.Y.Z"` - - `git push origin vX.Y.Z` - - `gh release create vX.Y.Z /tmp/imsg-macos.zip -t "vX.Y.Z" -F /tmp/release-notes.txt` - - Run `.github/workflows/release.yml` for the tag to upload the Linux archive - (`imsg-linux-x86_64.tar.gz`). Leave `include_macos` off unless you - intentionally want a manual macOS rebuild. - - `gh release edit vX.Y.Z --notes-file /tmp/release-notes.txt` (if needed) -5. Update Homebrew tap - - Run `scripts/update-homebrew.sh vX.Y.Z` to trigger the centralized formula updater. - - Requires a GitHub token with workflow dispatch access to `steipete/homebrew-tap`. +## Prerequisites -## What happens in CI -- Release signing + notarization are done locally via `scripts/sign-and-notarize.sh`. -- `.github/workflows/release.yml` is only for manual rebuilds, not the primary release path. +The repository must contain these Actions secrets: -## Linux support schedule -- 0.8.0 is the Linux read-only preview release. It may include an experimental - Linux `x86_64` archive, but docs must keep describing Linux as read-only - support for existing copied Messages databases. -- Linux support is staged as a read-only core pass: SwiftPM build, Linux-only - tests, release archive generation, and CI coverage for reading copied - Messages database fixtures. -- Do not document Linux send/watch/Contacts/IMCore support unless those features - are implemented and proven on Linux. They currently depend on macOS frameworks - or Messages.app automation. +- `MACOS_SIGNING_P12` +- `MACOS_SIGNING_P12_PASSWORD` +- `ASC_KEY_ID` +- `ASC_ISSUER_ID` +- `ASC_PRIVATE_KEY_P8` +- `HOMEBREW_TAP_TOKEN` + +The first five are required before the workflow can create a release tag. The preflight intentionally fails before mutating release state when any are unavailable. + +## Release contract + +- `version.env`, generated `Sources/imsg/Version.swift`, and the requested version must agree. +- `CHANGELOG.md` must contain exactly one dated level-two section for the requested version. +- `scripts/build-universal.sh` must emit the universal `imsg` CLI, `imsg-bridge-helper.dylib` with `arm64e`, `arm64`, and `x86_64` slices, and at least one Swift resource bundle. +- `scripts/build-linux.sh` must emit `imsg-linux-x86_64.tar.gz` with the static Swift runtime. +- The Darwin payload retains `com.steipete.imsg` and `com.steipete.imsg.bridge-helper` under Peter Steinberger's Developer ID identity. +- Both native macOS verifier jobs must accept the checksum inventory, signatures, architecture slices, resource bundles, notarization, native version output, and Linux executable format before publication. +- The Homebrew formula in `steipete/homebrew-tap` must resolve to the exact verified `imsg-macos.zip` URL and SHA-256. + +## Dispatch and verification + +Dispatch only after the release-preparation PR is merged and current `main` CI is green: + +```bash +gh workflow run release.yml --repo openclaw/imsg --ref main -f version=0.13.5 +``` + +Watch the exact run. After success, verify that the public release is non-draft and non-prerelease, the annotated tag peels to the frozen main commit, all six control/platform assets are present, `SHA256SUMS` validates them, the Homebrew workflow succeeded, and the formula hash matches the published macOS ZIP. + +Retries reuse the immutable annotated version tag and frozen commit. Never move or replace a consumer release tag to recover a failed run. diff --git a/scripts/build-universal.sh b/scripts/build-universal.sh index 74364084..6ea064bc 100755 --- a/scripts/build-universal.sh +++ b/scripts/build-universal.sh @@ -47,7 +47,7 @@ clang -dynamiclib "${HELPER_CLANG_ARCH_ARGS[@]}" -fobjc-arc \ -o "${DIST_DIR}/${HELPER_NAME}" \ "${ROOT}/Sources/IMsgHelper/IMsgInjected.m" -# This is the shipping path (release.yml runs only this script), so fail the +# This is the shipping path (the shared release workflow runs this script), so fail the # build if any required helper slice is missing — a dropped arm64e slice # silently kills the bridge on macOS 26 Messages. for ARCH in "${HELPER_ARCH_LIST[@]}"; do diff --git a/scripts/sign-and-notarize.sh b/scripts/sign-and-notarize.sh deleted file mode 100755 index b5c23405..00000000 --- a/scripts/sign-and-notarize.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -source "$ROOT/version.env" - -APP_NAME="imsg" -HELPER_NAME="imsg-bridge-helper.dylib" -CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-"Developer ID Application: Peter Steinberger (Y5PE65HELJ)"} -ENTITLEMENTS="${ROOT}/Resources/imsg.entitlements" -OUTPUT_DIR="${OUTPUT_DIR:-/tmp}" -ZIP_PATH="${OUTPUT_DIR}/imsg-macos.zip" -ARCHES_VALUE=${ARCHES:-"arm64 x86_64"} -ARCH_LIST=( ${ARCHES_VALUE} ) -# arm64e is mandatory for the helper: macOS 26 Messages will not load an -# arm64-only dylib. The HELPER_ARCH_LIST validation below fails the notarize run -# if any slice (incl. arm64e) is missing. -HELPER_ARCHES_VALUE=${HELPER_ARCHES:-"arm64e arm64 x86_64"} -HELPER_ARCH_LIST=( ${HELPER_ARCHES_VALUE} ) -SWIFT_SCRATCH_ROOT=${SWIFT_SCRATCH_ROOT:-"${ROOT}/.build/universal"} -DIST_DIR="$(mktemp -d "/tmp/${APP_NAME}-dist.XXXXXX")" -API_KEY_FILE="$(mktemp "/tmp/${APP_NAME}-notary.XXXXXX.p8")" - -cleanup() { - rm -f "$API_KEY_FILE" - rm -rf "$DIST_DIR" -} -trap cleanup EXIT - -if [[ -z "${APP_STORE_CONNECT_API_KEY_P8:-}" || -z "${APP_STORE_CONNECT_KEY_ID:-}" || -z "${APP_STORE_CONNECT_ISSUER_ID:-}" ]]; then - echo "Missing APP_STORE_CONNECT_* env vars (API key, key id, issuer id)." >&2 - exit 1 -fi - -echo "$APP_STORE_CONNECT_API_KEY_P8" | sed 's/\\n/\n/g' > "$API_KEY_FILE" - -BINARIES=() -PRODUCT_DIRS=() -for ARCH in "${ARCH_LIST[@]}"; do - SCRATCH_PATH="${SWIFT_SCRATCH_ROOT}/${ARCH}" - swift build -c release --product "$APP_NAME" --arch "$ARCH" \ - --scratch-path "$SCRATCH_PATH" - PRODUCT_DIR=$(swift build -c release --arch "$ARCH" \ - --scratch-path "$SCRATCH_PATH" --show-bin-path) - BINARIES+=("${PRODUCT_DIR}/${APP_NAME}") - PRODUCT_DIRS+=("$PRODUCT_DIR") -done - -lipo -create "${BINARIES[@]}" -output "$DIST_DIR/imsg" -HELPER_CLANG_ARCH_ARGS=() -for ARCH in "${HELPER_ARCH_LIST[@]}"; do - HELPER_CLANG_ARCH_ARGS+=("-arch" "$ARCH") -done -clang -dynamiclib "${HELPER_CLANG_ARCH_ARGS[@]}" -fobjc-arc \ - -Wno-arc-performSelector-leaks \ - -install_name "@rpath/${HELPER_NAME}" \ - -framework Foundation \ - -framework AppKit \ - -framework ImageIO \ - -framework LinkPresentation \ - -o "$DIST_DIR/$HELPER_NAME" \ - "$ROOT/Sources/IMsgHelper/IMsgInjected.m" - -# Validate the HELPER slices, not the CLI ARCH_LIST — the helper defaults to a -# superset (arm64e) that the CLI list does not contain, so ARCH_LIST here would -# silently skip the arm64e check macOS 26 Messages depends on. -for ARCH in "${HELPER_ARCH_LIST[@]}"; do - if ! lipo -archs "$DIST_DIR/$HELPER_NAME" | tr ' ' '\n' | grep -Fxq "$ARCH"; then - echo "Helper missing required architecture slice: $ARCH" >&2 - exit 1 - fi -done - -codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \ - --entitlements "$ENTITLEMENTS" \ - "$DIST_DIR/imsg" -codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \ - --identifier com.steipete.imsg.bridge-helper \ - "$DIST_DIR/$HELPER_NAME" - -for bundle in "${PRODUCT_DIRS[0]}"/*.bundle; do - if [[ -e "$bundle" ]]; then - cp -R "$bundle" "$DIST_DIR/" - fi -done - -chmod -R u+rw "$DIST_DIR" -xattr -cr "$DIST_DIR" -find "$DIST_DIR" -name '._*' -delete - -DITTO_BIN=${DITTO_BIN:-/usr/bin/ditto} -rm -f "$ZIP_PATH" -( - cd "$DIST_DIR" - "$DITTO_BIN" --norsrc -c -k . "$ZIP_PATH" -) - -xcrun notarytool submit "$ZIP_PATH" \ - --key "$API_KEY_FILE" \ - --key-id "$APP_STORE_CONNECT_KEY_ID" \ - --issuer "$APP_STORE_CONNECT_ISSUER_ID" \ - --wait - -codesign --verify --strict --verbose=4 "$DIST_DIR/imsg" -codesign --verify --strict --verbose=4 "$DIST_DIR/$HELPER_NAME" -if ! spctl -a -t exec -vv "$DIST_DIR/imsg"; then - echo "spctl check failed (CLI binaries often report 'not an app')." >&2 -fi - -echo "Done: $ZIP_PATH" diff --git a/scripts/update-homebrew.sh b/scripts/update-homebrew.sh deleted file mode 100755 index dd9368b5..00000000 --- a/scripts/update-homebrew.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [[ $# -ne 1 ]]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -TAG="$1" - -gh workflow run update-formula.yml \ - --repo steipete/homebrew-tap \ - --ref main \ - -f formula=imsg \ - -f tag="$TAG" \ - -f repository=openclaw/imsg \ - -f macos_artifact=imsg-macos.zip - -echo "Homebrew tap update dispatched. Monitor: https://github.com/steipete/homebrew-tap/actions"