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 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 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 934b6e9..1f39393 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 @@ -66,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 @@ -86,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!" +``` + +```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 +``` + +#### Log to SQLite database + +```bash +#!/usr/bin/env bash +set -e + +DATABASE="$HOME/pomodoros.sqlite" + +# Create table if needed +sqlite3 "$DATABASE" <> ~/pomodoro-log.txt +```bash +#!/usr/bin/env bash +set -e +# Finish hook: Disable focus mode +/usr/bin/open focus://unfocus ``` -Sample scripts can be found in [the `SampleHooks` directory](https://github.com/dirtyhenry/pomodoro-cli/blob/main/Resources/SampleHooks). +#### Play a sound + +```bash +#!/usr/bin/env bash +set -e +afplay /System/Library/Sounds/Glass.aiff +``` + +More sample scripts can be found in [the `SampleHooks` directory](https://github.com/dirtyhenry/pomodoro-cli/blob/main/Resources/SampleHooks). + +### Note on `make deploy` + +When installing from source with `make deploy`, sample hooks are only copied if no hooks exist yet. Your custom hooks are never overwritten. ## Journal @@ -141,15 +221,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 - -The `.dmg` files are created via: +### From GitHub Releases -```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