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
47 changes: 41 additions & 6 deletions .github/workflows/linkcheck.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
name: Link check

on: [push, pull_request]
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: "03 22 * * *"

jobs:
linkcheck:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/checkout@v4
- name: Restore lychee cache
uses: actions/cache@v4
with:
path: .lycheecache
key: cache-lychee-${{ github.run_id }}
restore-keys: cache-lychee-
- name: Check links with Lychee
id: lychee
uses: lycheeverse/lychee-action@v2
with:
fail: true
fail: false
args: >-
--config .lychee.toml
--timeout 10
--max-retries 2
'**/*.md'
'**/*.rst'
--timeout 20
--max-retries 3
--cache
--max-cache-age 14d
.
- name: Create or update Link Checker issue
if: steps.lychee.outputs.exit_code != 0
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
EXISTING=$(gh issue list --label "report" --state open --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING" ]; then
gh issue edit "$EXISTING" --body-file ./lychee/out.md
else
gh issue create --title "Link Checker Report" --body-file ./lychee/out.md --label "report"
fi
- name: Close Link Checker issue if all links are healthy
if: steps.lychee.outputs.exit_code == 0
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
EXISTING=$(gh issue list --label "report" --state open --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING" ]; then
gh issue close "$EXISTING" --comment "All links are now healthy."
fi
27 changes: 7 additions & 20 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,29 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for poetry-dynamic-versioning
fetch-depth: 0 # Fetch all history for hatch-vcs

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --with dev
run: pip install -e ".[dev]"

- name: Run Ruff linter
uses: astral-sh/ruff-action@v3
with:
args: check .

- name: Run tests
run: |
poetry run pytest
run: python -m pytest

publish:
needs: test # Only publish if tests pass
Expand All @@ -50,27 +46,18 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for poetry-dynamic-versioning
fetch-depth: 0 # Fetch all history for hatch-vcs

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build

- name: Verify version from git tags
run: |
git describe --tags --long
echo "Git tag detected above ^"
run: pip install build hatch-vcs

- name: Build package
run: |
# Build using PEP 517 with poetry-dynamic-versioning backend
python -m build --sdist --wheel
run: python -m build --sdist --wheel

- name: Display built packages
run: |
Expand Down
15 changes: 6 additions & 9 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,21 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for hatch-vcs

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --with dev
run: pip install -e ".[dev]"

- name: Run Ruff linter
uses: astral-sh/ruff-action@v3
Expand All @@ -39,9 +38,7 @@ jobs:

- name: Check dependencies with deptry
if: matrix.python-version != '3.9'
run: |
poetry run deptry .
run: python -m deptry .

- name: Run tests
run: |
poetry run pytest
run: python -m pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ tmp/
# OS
.DS_Store
Thumbs.db
plann/_version.py
21 changes: 17 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
rev: v0.15.6
hooks:
# Run the linter with auto-fix on commit
- id: ruff
Expand All @@ -12,7 +12,7 @@ repos:
stages: [pre-commit]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
stages: [pre-commit]
Expand Down Expand Up @@ -46,10 +46,23 @@ repos:
pass_filenames: false
always_run: true
stages: [pre-push]
# Prevent direct pushes to master on a v1.0+ project
- id: no-push-to-main
name: Prevent direct push to master
entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "master" ]; then echo "Direct pushes to master are not allowed. Use a branch and PR."; exit 1; fi'
language: system
pass_filenames: false
stages: [pre-push]

- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.4.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]

- repo: https://github.com/lycheeverse/lychee
rev: lychee-v0.22.0
rev: lychee-v0.23.0
hooks:
- id: lychee-docker
args: ["--no-progress", "--timeout", "10"]
args: ["--no-progress", "--timeout", "10", "--config", ".lychee.toml"]
types: [markdown, rst]
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ and I do try to adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.h

### Added

* Added possibility to add calendar name and calendar url to the template. Ref https://github.com/tobixen/plann/issues/14 by @rjolina at github.
* `now` should be an acceptable timestamp. Ref https://github.com/tobixen/plann/issues/16
* Added possibility to add calendar name and calendar url to the template. Ref https://github.com/pycalendar/plann/issues/14 by @rjolina at github.
* `now` should be an acceptable timestamp. Ref https://github.com/pycalendar/plann/issues/16

### Changed

* Various documentation improvements, some of it by @WhyNotHugo at github in https://github.com/tobixen/plann/pull/15
* Various documentation improvements, some of it by @WhyNotHugo at github in https://github.com/pycalendar/plann/pull/15

### Fixed

* `--help` had some wrong information, ref https://github.com/tobixen/plann/issues/16 by Thomas Maeder
* `--help` had some wrong information, ref https://github.com/pycalendar/plann/issues/16 by Thomas Maeder

## [v1.0.0] - 2024-12-01

Changelogs up until 1.0 has been dropped, as development was going
rather fast-paced and erratic, with the priority of getting a tool the
author can use for his daily planning. Very little development was
done in 2024, but the tool works for me.
done in 2024 and 2025, but the tool works for me.

The 1.0.0-version probably has plenty of rough edges as it hasn't been
tested much and is lacking some test code, but if nothing else I will
Expand Down
41 changes: 41 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Contributing to plann

Contributions are mostly welcome (but do inform about it if you've used AI or other tools). If the length of this text scares you, then I'd rather want you to skip reading and just produce a pull-request in GitHub. If you find it too difficult to write test code, etc, then you may skip it and hope the maintainer will fix it.

## What to include

Every submission should ideally include:

- **Test code** covering the new behaviour or bug fix
- **Documentation** updates where relevant
- **A changelog entry** in `CHANGELOG.md` under `[Unreleased]`

## Development setup

```
make dev
```

This installs plann in editable mode with all dev dependencies.

## Running tests and linting

```
make test
make lint
```

## Commit messages

Please follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and write messages in the imperative mood:

- `fix: correct time-range search handling for recurring events`
- `feat: add journal entry support`
- `docs: update README`

Rather than:

- `This commit fixes the time-range search`
- `Added journal support`

Note: older commits in this repository predate this convention and do not follow it.
28 changes: 16 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ help:
@echo " make uninstall Uninstall plann"
@echo ""
@echo "Development:"
@echo " make dev Install in development mode (Poetry)"
@echo " make dev Install in development mode"
@echo " make test Run tests"
@echo " make lint Run linter"
@echo " make clean Clean build artifacts"
Expand All @@ -33,16 +33,20 @@ venv:
$(VENV)/bin/pip install --upgrade pip; \
fi

# Install package via pip
# When run as root (sudo make install), installs system-wide with --break-system-packages.
# When run as a normal user, installs to the user's home directory with --user.
# Install package: auto-detects root, uv, pipx, or falls back to pip --user
install:
@if [ "$$(id -u)" = "0" ]; then \
echo "Installing plann system-wide..."; \
pip install --break-system-packages .; \
pip install .; \
elif command -v uv >/dev/null 2>&1; then \
echo "Installing with uv..."; \
uv tool install .; \
elif command -v pipx >/dev/null 2>&1; then \
echo "Installing with pipx..."; \
pipx install .; \
else \
echo "Installing plann for current user..."; \
pip install --user .; \
echo "Tip: install uv or pipx for isolated installs (pacman -S uv, apt install pipx)"; \
PIP_BREAK_SYSTEM_PACKAGES=1 pip install --user .; \
fi

# Uninstall plann
Expand All @@ -56,17 +60,17 @@ uninstall:
# Install in development mode
dev:
@echo "Installing in development mode..."
@poetry install
@PIP_BREAK_SYSTEM_PACKAGES=1 pip install -e ".[dev]"
@echo ""
@echo "Installed! Run: poetry run plann --help"
@echo "Installed! Run: plann --help"

# Run tests
test:
@poetry run pytest tests/ -v
@python -m pytest tests/ -v

# Run linter
lint:
@poetry run ruff check .
@python -m ruff check .

# Clean build artifacts
clean:
Expand All @@ -84,7 +88,7 @@ install-completion-user:
@echo "Installing shell completion for current user..."
@mkdir -p "$(COMPLETION_DIR_USER)"
@_PLANN_COMPLETE=bash_source plann > "$(COMPLETION_DIR_USER)/plann" 2>/dev/null || \
poetry run python -c "import click; from plann.cli import cli; print(click.shell_completion.get_completion_class('bash')(cli, {}, 'plann', '_PLANN_COMPLETE').source())" > "$(COMPLETION_DIR_USER)/plann"
python -c "import click; from plann.cli import cli; print(click.shell_completion.get_completion_class('bash')(cli, {}, 'plann', '_PLANN_COMPLETE').source())" > "$(COMPLETION_DIR_USER)/plann"
@echo "Completion script installed to $(COMPLETION_DIR_USER)/plann"
@echo "Restart your shell or run: source $(COMPLETION_DIR_USER)/plann"

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Plann is a command-line CalDAV client, making it possible to add calendar events, browse an agenda and do task management.

This is the "next generation" version of my old [calendar-cli project](https://github.com/tobixen/calendar-cli/).
This is the "next generation" version of my old [calendar-cli project](https://github.com/pycalendar/calendar-cli/).

Perhaps this work is moot ... or perhaps just 20 years too late. Considering the recent progress of AI and chatbots, probably soon all calendar queries can be done in natural language.

Expand Down Expand Up @@ -44,7 +44,7 @@ For development work using Poetry:

## Support

\#calendar-cli at irc.oftc.net (I'm not available 24/7 there), eventually support@plann.no, eventually the issue tracker at https://github.com/tobixen/plann/issues
\#calendar-cli at irc.oftc.net (I'm not available 24/7 there), eventually support@plann.no, eventually the issue tracker at https://github.com/pycalendar/plann/issues

Before reaching out, please make sure all the dependencies are installed, and that you've installed the latest version of the caldav python library. If you're using the master branch of plann, you should also be using the master branch of the caldav python library.

Expand Down Expand Up @@ -83,7 +83,7 @@ not be up-to-date and may contain features not implemented yet.
* --nocaldav: don't connect to a caldav server
* --timezone: any "naive" timestamp should be considered to belong to the given time zone, timestamps outputted should be in this time zone, timestamps given through options should be considered to be in this time zone (Olson database identifiers, like UTC or Europe/Helsinki). (default: local timezone)

The caldav URL should be something like i.e. http://some.davical.server/caldav.php/ - it is only supposed to relay the server location, not the user or calendar. Things will most likely work if you give http://some.davical.server/caldav.php/tobixen/work-calendar/ - but it will ignore the calendar part of it, and use first calendar it can find - which perhaps may be `tobixen/family-calendar/`. Use http://some.davical.server/caldav.php/ as the caldav URL, and `/tobixen/family-calendar` as the calendar-url.
The caldav URL should be something like i.e. http://some.davical.server/caldav.php/ - it is only supposed to relay the server location, not the user or calendar. Things will most likely work if you give http://some.davical.server/caldav.php/pycalendar/work-calendar/ - but it will ignore the calendar part of it, and use first calendar it can find - which perhaps may be `tobixen/family-calendar/`. Use http://some.davical.server/caldav.php/ as the caldav URL, and `/pycalendar/family-calendar` as the calendar-url.

### Commands

Expand Down
Loading
Loading