Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
122 changes: 98 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Pomodoro is a command-line interface to run timers from your terminal for people

<div align="center">
<img src="https://github.com/dirtyhenry/pomodoro-cli/blob/main/Resources/usage-carbon.png?raw=true" alt="pomodoro-cli usage example" width="673" height="250">
</a>
</div>

## Usage
Expand Down Expand Up @@ -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

Expand All @@ -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" <<EOF
CREATE TABLE IF NOT EXISTS pomodoros (
id INTEGER PRIMARY KEY AUTOINCREMENT,
start TEXT NOT NULL,
end TEXT NOT NULL,
message TEXT
);
EOF

# Escape single quotes for SQL
SAFE_MESSAGE="${4//\'/\'\'}"

sqlite3 "$DATABASE" <<EOF
INSERT INTO pomodoros (start, end, message) VALUES ('$1', '$2', '$SAFE_MESSAGE');
EOF
```

#### Integration with Focus apps

```bash
#!/usr/bin/env bash
set -e
# Start hook: Enable focus mode
/usr/bin/open focus://focus
```

# Log to a custom file
echo "$(date): Completed pomodoro - $MESSAGE ($DURATION seconds)" >> ~/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

Expand Down Expand Up @@ -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

Expand Down