From 388425e25b6bd0b902774f6eab56f083c00a0fe6 Mon Sep 17 00:00:00 2001 From: Mick F Date: Mon, 9 Feb 2026 19:23:16 +0100 Subject: [PATCH 1/4] Add changelog and automated release workflow - Add retroactive CHANGELOG.md covering v1.0.0 through v1.1.0 - Add GitHub Actions workflow to build and publish DMG on tag push - Workflow handles code signing, notarization, and GitHub Release creation Co-Authored-By: Claude Opus 4.5 --- .github/workflows/release.yml | 114 ++++++++++++++++++++++++++++++++++ CHANGELOG.md | 67 ++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 CHANGELOG.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7979100 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,114 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + build: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Import Code Signing Certificate + env: + CERTIFICATE_BASE64: ${{ secrets.CERTIFICATE_BASE64 }} + CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # Create a temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # Import certificate + CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12 + echo -n "$CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH + security import $CERTIFICATE_PATH -P "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + - name: Build Universal Binary + run: | + swift build -c release --product pomodoro-cli --arch arm64 --arch x86_64 + + - name: Code Sign Binary + env: + CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }} + run: | + xcrun codesign -s "$CODESIGN_IDENTITY" \ + --options=runtime \ + --timestamp \ + .build/apple/Products/Release/pomodoro-cli + + - name: Create Package + env: + PKG_CODESIGN_IDENTITY: ${{ secrets.PKG_CODESIGN_IDENTITY }} + REVERSED_DOMAIN: ${{ secrets.REVERSED_DOMAIN }} + run: | + VERSION=${{ steps.version.outputs.VERSION }} + PKG_ROOT=./pkg/pomodoro-cli-${VERSION} + PKG_DIR=${PKG_ROOT}/usr/local/bin + PKG_DMG_ROOT=./pkg/out + PKG=./pkg/out/pomodoro-cli-${VERSION}.pkg + BUNDLE_ID=${REVERSED_DOMAIN}.pomodoro-cli + + mkdir -p ${PKG_DIR} + mkdir -p ${PKG_DMG_ROOT} + cp .build/apple/Products/Release/pomodoro-cli ${PKG_DIR} + + xcrun pkgbuild --root ${PKG_ROOT} \ + --identifier "${BUNDLE_ID}" \ + --version "${VERSION}" \ + --install-location "/" \ + --sign "$PKG_CODESIGN_IDENTITY" \ + ${PKG} + + - name: Notarize Package + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + TEAM_ID: ${{ secrets.TEAM_ID }} + run: | + VERSION=${{ steps.version.outputs.VERSION }} + PKG=./pkg/out/pomodoro-cli-${VERSION}.pkg + + xcrun notarytool submit \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_PASSWORD" \ + --team-id "$TEAM_ID" \ + --wait \ + "$PKG" + + - name: Staple Package + run: | + VERSION=${{ steps.version.outputs.VERSION }} + xcrun stapler staple "./pkg/out/pomodoro-cli-${VERSION}.pkg" + + - name: Create DMG + run: | + VERSION=${{ steps.version.outputs.VERSION }} + hdiutil create \ + -volname "pomodoro-cli" \ + -srcfolder "./pkg/out" \ + -ov \ + -format UDZO \ + "./pkg/pomodoro-cli-${VERSION}.dmg" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + ./pkg/pomodoro-cli-${{ steps.version.outputs.VERSION }}.dmg + ./pkg/out/pomodoro-cli-${{ steps.version.outputs.VERSION }}.pkg + generate_release_notes: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ffca083 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,67 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Support for Ctrl+C to gracefully interrupt a running pomodoro +- `--catch-up` flag to run hooks only (without starting a timer) +- Hooks now receive pomodoro metadata (duration, message, timestamps) +- Sample hooks for SQLite database integration + +### Changed + +- Migrated to Swift 5.9 +- Removed `swift-tools-support-core` dependency +- Modernized Swift style with more idiomatic patterns + +### Fixed + +- Single quote escaping in bash parameter expansion +- Single quote escaping in SQL insertion for hooks + +## [1.1.0] - 2023-03-29 + +### Added + +- Distribution pipeline with Apple notarization support +- Short argument versions (`-d` for `--duration`, `-m` for `--message`) +- DocC documentation (replacing Jazzy) + +### Changed + +- Migrated from `altool` to `notarytool` for notarization +- Upgraded to Swift 5.5 +- Replaced SwiftCLI with ArgumentParser + +### Fixed + +- macOS Monterey compatibility (removed units when calling sleep) +- Runtime issue with SwiftToolsSupport-auto product + +## [1.0.1] - 2020-02-01 + +### Fixed + +- Hooks are now activated by default + +## [1.0.0] - 2017-11-04 + +### Added + +- Initial release +- Pomodoro timer with configurable duration +- Progress bar display +- Message support with journaling to `~/.pomodoro-cli/journal.yml` +- Hook system for custom scripts at pomodoro start/finish +- Human-readable duration format (e.g., "25m", "1h30m") + +[Unreleased]: https://github.com/dirtyhenry/pomodoro-cli/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/dirtyhenry/pomodoro-cli/compare/v1.0.1...v1.1.0 +[1.0.1]: https://github.com/dirtyhenry/pomodoro-cli/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/dirtyhenry/pomodoro-cli/releases/tag/v1.0.0 From 806917e601de4bbeeeabdabdc9d08255c43c7b4c Mon Sep 17 00:00:00 2001 From: Mick F Date: Mon, 9 Feb 2026 19:29:41 +0100 Subject: [PATCH 2/4] Update README installation instructions - Remove stray HTML tag - Point to GitHub Releases instead of manual make commands Co-Authored-By: Claude Opus 4.5 --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 934b6e9..33af1fb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ Pomodoro is a command-line interface to run timers from your terminal for people
pomodoro-cli usage example -
## Usage @@ -141,15 +140,9 @@ Installing `swiftlint` and `swiftformat` via [Homebrew](https://brew.sh/) is rec Check out [`Makefile`](https://github.com/dirtyhenry/pomodoro-cli/blob/main/Makefile) for more development convenience commands. -### From Distribution Images +### From GitHub Releases -The `.dmg` files are created via: - -```bash -make clean notarize -# and upon successful feedback from the Apple notary service: -make image -``` +Download the latest `.dmg` or `.pkg` from [GitHub Releases](https://github.com/dirtyhenry/pomodoro-cli/releases). ## Contributing From 6a2906b1e13bf17bb04c89f719b70335b4e89d9c Mon Sep 17 00:00:00 2001 From: Mick F Date: Mon, 9 Feb 2026 19:31:47 +0100 Subject: [PATCH 3/4] Update LICENSE copyright year Co-Authored-By: Claude Opus 4.5 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 861adbf..2d038a1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2025 Mickaël Floc'hlay +Copyright (c) 2017-2026 Mickaël Floc'hlay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c2275a900dea71c21fbe88a9a4a9978839fd62ba Mon Sep 17 00:00:00 2001 From: Mick F Date: Mon, 9 Feb 2026 22:06:12 +0100 Subject: [PATCH 4/4] Improve hooks documentation and preserve custom hooks - Expand hooks section with setup instructions and examples - Add examples for notifications, SQLite, Focus apps, sounds - Fix `make deploy` to not overwrite existing custom hooks Co-Authored-By: Claude Opus 4.5 --- Makefile | 4 +- README.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index f1b9e1b..569943b 100644 --- a/Makefile +++ b/Makefile @@ -53,8 +53,8 @@ deploy: sudo install ".build/release/${PRODUCT}" "$(bindir)/pomodoro-cli" mkdir -p "$(HOME)/.pomodoro-cli" touch "$(HOME)/.pomodoro-cli/journal.yml" - cp Resources/SampleHooks/sample-pomodoro-finish.sh "$(HOME)/.pomodoro-cli/pomodoro-finish.sh" - cp Resources/SampleHooks/sample-pomodoro-start.sh "$(HOME)/.pomodoro-cli/pomodoro-start.sh" + @test -f "$(HOME)/.pomodoro-cli/pomodoro-finish.sh" || cp Resources/SampleHooks/sample-pomodoro-finish.sh "$(HOME)/.pomodoro-cli/pomodoro-finish.sh" + @test -f "$(HOME)/.pomodoro-cli/pomodoro-start.sh" || cp Resources/SampleHooks/sample-pomodoro-start.sh "$(HOME)/.pomodoro-cli/pomodoro-start.sh" deploy-my-hooks: cp Resources/SampleHooks/mickf-pomodoro-finish.sh "$(HOME)/.pomodoro-cli/pomodoro-finish.sh" diff --git a/README.md b/README.md index 33af1fb..1f39393 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,28 @@ Use `--indefinite` for meetings or tasks with unknown duration: ## Hooks -Pomodoro can optionally run shell scripts when a pomodoro starts and/or finishes. +Hooks let you run custom shell scripts when a pomodoro starts and/or finishes. This is one of the most powerful features of pomodoro-cli — you can integrate with any tool or service. -### Hook Locations +### Setup -- **Start hook**: `~/.pomodoro-cli/pomodoro-start.sh` -- **Finish hook**: `~/.pomodoro-cli/pomodoro-finish.sh` +1. Create the hooks directory (if it doesn't exist): + ```bash + mkdir -p ~/.pomodoro-cli + ``` + +2. Create your hook scripts: + ```bash + touch ~/.pomodoro-cli/pomodoro-start.sh + touch ~/.pomodoro-cli/pomodoro-finish.sh + ``` + +3. Make them executable: + ```bash + chmod +x ~/.pomodoro-cli/pomodoro-start.sh + chmod +x ~/.pomodoro-cli/pomodoro-finish.sh + ``` + +Both hooks are optional — create only the ones you need. ### Hook Parameters @@ -85,25 +101,90 @@ Both hooks receive 4 arguments: **Note**: For indefinite pomodoros, the start hook receives `"indefinite"` for end date and duration. The finish hook always receives actual values. -### Example Hook +### Examples + +#### Basic notification (finish hook) ```bash #!/usr/bin/env bash -# ~/.pomodoro-cli/pomodoro-finish.sh - -START_DATE=$1 -END_DATE=$2 -DURATION=$3 +set -e MESSAGE=$4 - -# Send a notification osascript -e "display notification \"$MESSAGE\" with title \"Pomodoro Complete!\"" +``` + +#### Text-to-speech announcements + +```bash +#!/usr/bin/env bash +set -e +# Start hook: Italian-accented motivation +/usr/bin/say --voice=Alice "Andiamo a lavorare!" +``` -# Log to a custom file -echo "$(date): Completed pomodoro - $MESSAGE ($DURATION seconds)" >> ~/pomodoro-log.txt +```bash +#!/usr/bin/env bash +set -e +# Finish hook: Announce completion and lock screen +/usr/bin/say --voice=Alice "Il pomodoro è finito." +sleep 5 +/usr/bin/pmset displaysleepnow ``` -Sample scripts can be found in [the `SampleHooks` directory](https://github.com/dirtyhenry/pomodoro-cli/blob/main/Resources/SampleHooks). +#### Log to SQLite database + +```bash +#!/usr/bin/env bash +set -e + +DATABASE="$HOME/pomodoros.sqlite" + +# Create table if needed +sqlite3 "$DATABASE" <