From e763fa7d63f7691c84eae68e5d8efd2b7d97a05a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 3 Jan 2026 06:31:55 +0100 Subject: [PATCH] chore: replace pnpm with make --- .github/workflows/ci.yml | 18 ++++------- AGENTS.md | 29 +++++++++-------- Makefile | 42 +++++++++++++++++++++++++ README.md | 10 +++--- docs/RELEASING.md | 8 ++--- package.json | 15 --------- scripts/build-universal.sh | 61 ++++++++++++++++++++++++++++++++++++ scripts/sign-and-notarize.sh | 19 ++++++++--- 8 files changed, 147 insertions(+), 55 deletions(-) create mode 100644 Makefile delete mode 100644 package.json create mode 100755 scripts/build-universal.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 916bdd7..3e81a80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,15 +14,9 @@ jobs: run: swift --version - name: Install SwiftLint run: brew install swiftlint - - name: Resolve packages - run: swift package resolve - - name: Patch dependencies - run: scripts/patch-deps.sh - - name: Swift format lint - run: swift format lint --recursive Sources Tests - - name: SwiftLint - run: swiftlint - - name: Swift test - run: swift test - - name: Swift build - run: swift build -c release --product imsg + - name: Lint + run: make lint + - name: Test + run: make test + - name: Build + run: make build ARCHES=$(uname -m) diff --git a/AGENTS.md b/AGENTS.md index b811ed8..195b394 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,30 +1,29 @@ # Repository Guidelines ## Project Structure & Module Organization -- `cmd/imsg` holds the CLI entrypoint (`main.go`) and top-level Cobra command wiring. -- `internal/db`, `internal/watch`, `internal/send`, `internal/util` contain SQLite access, polling/streaming, AppleScript send logic, and helpers; keep shared code inside `internal` to avoid external API drift. -- `bin/` is created by the build script for local artifacts; `coverage.out` is optional and should not be committed unless updating reports. +- `Sources/imsg` holds the CLI entrypoint and command wiring. +- `Sources/IMsgCore` contains SQLite access, watchers, AppleScript send logic, and helpers. +- `bin/` is created by `make build` for local artifacts. ## Build, Test, and Development Commands -- `pnpm imsg` (or `go run ./cmd/imsg`) — run the CLI locally. -- `pnpm build` — compile to `bin/imsg` using the current module versions. -- `pnpm lint` — run `golangci-lint` with `gofmt`, `goimports`, `revive`, `staticcheck`, etc.; fix before sending a PR. -- `pnpm test` — execute `go test ./...`; use `-run` to target specific packages when iterating. +- `make imsg` — clean rebuild + run debug CLI (use `ARGS=...`). +- `make build` — universal release build into `bin/`. +- `make lint` — run `swift format` lint + `swiftlint`. +- `make test` — run `swift test` after syncing version + patching deps. ## Coding Style & Naming Conventions -- Go 1.24 module; rely on standard library patterns, idiomatic error handling, and early returns. -- Formatting is enforced by `gofmt`/`goimports`; do not hand-edit import order or indentation (tabs per Go defaults). -- Keep package-visible types intentional; prefer concrete types over `interface{}` and avoid global state in `internal` packages. -- Cobra command flags: prefer long-form, kebab-case (`--chat-id`, `--attachments`) consistent with existing commands. +- Swift 6 module; prefer concrete types, early returns, and minimal globals. +- Formatting is enforced by `swift format` and `swiftlint`. +- CLI flags use long-form, kebab-case (`--chat-id`, `--attachments`). ## Testing Guidelines -- Unit tests live alongside code as `*_test.go`; name tests `TestFunctionBehavior` and table-drive where useful. -- For DB or watch logic, prefer deterministic fixtures over touching the user’s live Messages DB; skip or mark tests that require macOS integration. -- Aim to keep `go test ./...` clean; add regression tests for every bug fix touching parsing, filtering, or attachment metadata. +- Unit tests live in `Tests/` as `*Tests.swift`. +- Prefer deterministic fixtures over touching the live Messages DB. +- Add regression tests for fixes touching parsing, filtering, or attachment metadata. ## Commit & Pull Request Guidelines - Follow the existing short, lowercase prefixes seen in history (`ci:`, `chore:`, `fix:`, `feat:`) with an imperative summary (e.g., `fix: handle missing attachments`). -- PRs should include: brief description, steps to repro/verify, and outputs of `pnpm lint` and `pnpm test`. For CLI changes, include sample commands and before/after snippets. +- PRs should include: brief description, steps to repro/verify, and outputs of `make lint` and `make test`. For CLI changes, include sample commands and before/after snippets. - Keep changeset focused; avoid drive-by refactors unless they reduce risk or remove duplication in touched areas. ## Security & macOS Permissions diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..daf463d --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +SHELL := /bin/bash + +.PHONY: help format lint test build imsg clean + +help: + @printf "%s\n" \ + "make format - swift format in-place" \ + "make lint - swift format lint + swiftlint" \ + "make test - sync version, patch deps, run swift test" \ + "make build - universal release build into bin/" \ + "make imsg - clean rebuild + run debug binary (ARGS=...)" \ + "make clean - swift package clean" + +format: + swift format --in-place --recursive Sources Tests + +lint: + swift format lint --recursive Sources Tests + swiftlint + +test: + scripts/generate-version.sh + swift package resolve + scripts/patch-deps.sh + swift test + +build: + scripts/generate-version.sh + swift package resolve + scripts/patch-deps.sh + scripts/build-universal.sh + +imsg: + scripts/generate-version.sh + swift package resolve + scripts/patch-deps.sh + swift package clean + swift build -c debug --product imsg + ./.build/debug/imsg $(ARGS) + +clean: + swift package clean diff --git a/README.md b/README.md index fb8a887..424497a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A macOS Messages.app CLI to send, read, and stream iMessage/SMS (with attachment ## Install ```bash -pnpm build +make build # binary at ./bin/imsg ``` @@ -67,15 +67,15 @@ If you see “unable to open database file” or empty output: ## Testing ```bash -pnpm test +make test ``` -Note: pnpm scripts apply a small patch to SQLite.swift to silence a SwiftPM warning about `PrivacyInfo.xcprivacy`. +Note: `make test` applies a small patch to SQLite.swift to silence a SwiftPM warning about `PrivacyInfo.xcprivacy`. ## Linting & formatting ```bash -pnpm lint -pnpm format +make lint +make format ``` ## Core library diff --git a/docs/RELEASING.md b/docs/RELEASING.md index d7094cc..eb6b2fe 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -8,12 +8,12 @@ 1. Update `CHANGELOG.md` and version - Move entries from `Unreleased` into a new `## X.Y.Z - YYYY-MM-DD` section. - Credit contributors (e.g. `thanks @user`). - - Update `version.env` and `package.json` to `X.Y.Z`. + - 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` - - `pnpm lint` - - `pnpm test` - - `pnpm format` (optional, if formatting changes are expected) + - `make lint` + - `make test` + - `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`. - `scripts/sign-and-notarize.sh` (outputs `/tmp/imsg-macos.zip` by default) diff --git a/package.json b/package.json deleted file mode 100644 index 760ea55..0000000 --- a/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "imsg", - "version": "0.3.0", - "private": true, - "scripts": { - "version:sync": "scripts/generate-version.sh", - "deps:patch": "swift package resolve && scripts/patch-deps.sh", - "imsg": "pnpm -s version:sync && pnpm -s deps:patch && swift run imsg", - "start": "pnpm imsg", - "format": "swift format --in-place --recursive Sources Tests", - "lint": "swift format lint --recursive Sources Tests && swiftlint", - "test": "pnpm -s version:sync && pnpm -s deps:patch && swift test", - "build": "pnpm -s version:sync && pnpm -s deps:patch && mkdir -p bin && swift build -c release --product imsg && cp .build/release/imsg bin/imsg && for existing in bin/*.bundle; do [ -e \"$existing\" ] || continue; if command -v trash >/dev/null 2>&1; then trash \"$existing\"; else rm -rf \"$existing\"; fi; done && for bundle in .build/release/*.bundle; do [ -e \"$bundle\" ] || continue; cp -R \"$bundle\" bin/; done && codesign --force --sign - --entitlements Resources/imsg.entitlements --identifier com.steipete.imsg bin/imsg" - } -} diff --git a/scripts/build-universal.sh b/scripts/build-universal.sh new file mode 100755 index 0000000..d8b42c1 --- /dev/null +++ b/scripts/build-universal.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT=$(cd "$(dirname "$0")/.." && pwd) +APP_NAME="imsg" +ENTITLEMENTS="${ROOT}/Resources/imsg.entitlements" +OUTPUT_DIR="${OUTPUT_DIR:-${ROOT}/bin}" +ARCHES_VALUE=${ARCHES:-"arm64 x86_64"} +ARCH_LIST=( ${ARCHES_VALUE} ) +BUILD_MODE=${BUILD_MODE:-release} +CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-"-"} + +for ARCH in "${ARCH_LIST[@]}"; do + swift build -c "$BUILD_MODE" --product "$APP_NAME" --arch "$ARCH" +done + +FIRST_ARCH="${ARCH_LIST[0]}" +BINARIES=() +for ARCH in "${ARCH_LIST[@]}"; do + BINARIES+=("${ROOT}/.build/${ARCH}-apple-macosx/${BUILD_MODE}/${APP_NAME}") +done + +DIST_DIR="$(mktemp -d "/tmp/${APP_NAME}-universal.XXXXXX")" +trap 'rm -rf "$DIST_DIR"' EXIT + +lipo -create "${BINARIES[@]}" -output "${DIST_DIR}/${APP_NAME}" + +if [[ "$CODESIGN_IDENTITY" == "-" ]]; then + codesign --force --sign - \ + --entitlements "$ENTITLEMENTS" \ + --identifier com.steipete.imsg \ + "${DIST_DIR}/${APP_NAME}" +else + codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \ + --entitlements "$ENTITLEMENTS" \ + --identifier com.steipete.imsg \ + "${DIST_DIR}/${APP_NAME}" +fi + +for bundle in "${ROOT}/.build/${FIRST_ARCH}-apple-macosx/${BUILD_MODE}"/*.bundle; do + if [[ -e "$bundle" ]]; then + cp -R "$bundle" "$DIST_DIR/" + fi +done + +mkdir -p "$OUTPUT_DIR" +if command -v trash >/dev/null 2>&1; then + for existing in "$OUTPUT_DIR/$APP_NAME" "$OUTPUT_DIR"/*.bundle; do + [[ -e "$existing" ]] || continue + trash "$existing" + done +fi + +cp "${DIST_DIR}/${APP_NAME}" "$OUTPUT_DIR/$APP_NAME" +for bundle in "${DIST_DIR}"/*.bundle; do + if [[ -e "$bundle" ]]; then + cp -R "$bundle" "$OUTPUT_DIR/" + fi +done + +echo "Built ${OUTPUT_DIR}/${APP_NAME} (${ARCHES_VALUE})" diff --git a/scripts/sign-and-notarize.sh b/scripts/sign-and-notarize.sh index 4be4ae8..f0e8fdd 100755 --- a/scripts/sign-and-notarize.sh +++ b/scripts/sign-and-notarize.sh @@ -9,6 +9,8 @@ CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-"Developer ID Application: Peter Steinber 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} ) DIST_DIR="$(mktemp -d "/tmp/${APP_NAME}-dist.XXXXXX")" API_KEY_FILE="$(mktemp "/tmp/${APP_NAME}-notary.XXXXXX.p8")" @@ -25,14 +27,23 @@ fi echo "$APP_STORE_CONNECT_API_KEY_P8" | sed 's/\\n/\n/g' > "$API_KEY_FILE" -swift build -c release --product imsg +for ARCH in "${ARCH_LIST[@]}"; do + swift build -c release --product imsg --arch "$ARCH" +done + +BINARIES=() +for ARCH in "${ARCH_LIST[@]}"; do + BINARIES+=("$ROOT/.build/${ARCH}-apple-macosx/release/imsg") +done + +lipo -create "${BINARIES[@]}" -output "$DIST_DIR/imsg" codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \ --entitlements "$ENTITLEMENTS" \ - "$ROOT/.build/release/imsg" + "$DIST_DIR/imsg" -cp "$ROOT/.build/release/imsg" "$DIST_DIR/imsg" -for bundle in "$ROOT/.build/release"/*.bundle; do +FIRST_ARCH="${ARCH_LIST[0]}" +for bundle in "$ROOT/.build/${FIRST_ARCH}-apple-macosx/release"/*.bundle; do if [[ -e "$bundle" ]]; then cp -R "$bundle" "$DIST_DIR/" fi