feat: add -c/--config flag to every command

This commit is contained in:
cătălin 2023-03-04 23:16:00 +02:00
commit 6f45b76579
Signed by: catalin
GPG key ID: 686088EF78EE4083
10 changed files with 163 additions and 51 deletions

View file

@ -12,12 +12,14 @@ tests:
dist:
pdm build
build:
pdm build
publish-pypi:
pdm publish -u $(PYPI_REGISTRY_USERNAME) -P $(PYPI_REGISTRY_PASSWORD)
publish-roboces:
pdm publish --build -u $(ROBOCES_REGISTRY_USERNAME) -p $(ROBOCES_REGISTRY_PASSWORD) -r roboces
pdm publish -u $(ROBOCES_REGISTRY_USERNAME) -P $(ROBOCES_REGISTRY_PASSWORD) -r https://git.roboces.dev/api/packages/catalin/pypi
publish:
make publish-pypi

View file

@ -1,17 +1,43 @@
# halig
[(r)age](https://github.com/woodruffw/pyrage) encrypted note-taking CLI app
![PyPI](https://img.shields.io/pypi/v/halig?logo=python)
![PyPI - License](https://img.shields.io/pypi/l/halig)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/halig)
[![pdm-managed](https://img.shields.io/badge/pdm-managed-blueviolet)](https://pdm.fming.dev)
## install
[(R)age](https://github.com/woodruffw/pyrage) encrypted note-taking CLI app.
`halig` opens, using your favorite `$EDITOR`, an in-memory copy of a file and upon save-and-exit,
it encrypts the new contents into an [age](https://github.com/FiloSottile/age) file that
you can store, _relatively_ safe, anywhere.
## Install
```shell
pip install halig
```
## cli mode
PS: I recommend using [pipx](https://pypa.github.io/pipx/) instead
## Setup TLDR
```shell
$ halig edit some_notebook # edit today's note
$ halig edit some_notebook/foo # edit /path/to/some_notebook/foo.age
$ halig notebooks # list current notebooks
set -e
ssh-keygen -t ed25519
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/halig"
cat << EOF > "${XDG_CONFIG_HOME:-$HOME/.config}/halig/halig.yml"
---
notebooks_root_path: /home/$(id -un)/Documents/Notebooks
identity_path: /home/$(id -un)/.ssh/id_ed25519
recipient_path: /home/$(id -un)/.ssh/id_ed25519.pub
EOF
```
## Usage TLDR
```shell
halig edit some_notebook # edit today's note relative to <notebooks_root_path>/some_notebook
halig edit some_notebook/foo # edit <notebooks_root_path>/some_notebook/foo.age
halig notebooks # list current notebooks
```

View file

@ -1,3 +0,0 @@
from typer import Typer
app = Typer()

View file

@ -1,32 +1,72 @@
#!/usr/bin/env python
#!/usr/bin/env python3
from pathlib import Path
from typing import Optional
from halig import app
from halig.commands.edit import EditCommand
from halig.commands.notebooks import NotebooksCommand
from halig.commands.show import ShowCommand
from halig.settings import load_from_file
from typer import Typer, Option, Argument
@app.command()
def notebooks(max_depth: int = -1):
if max_depth < 0:
max_depth = float("inf") # type: ignore[assignment]
settings = load_from_file()
command = NotebooksCommand(settings=settings, max_depth=max_depth)
app = Typer(pretty_exceptions_enable=False)
config_option = Option(
None,
"--config",
"-c",
help="Configuration file. Must be YAML and schema compatible",
)
@app.command(help="List all notebooks and notes, tree-style")
def notebooks(
level: int = Option(
-1,
"--level",
"-l",
help="Tree max recursion level; negative numbers indicate a value of infinity",
),
config: Optional[Path] = config_option,
):
if level < 0:
level = float("inf") # type: ignore[assignment]
settings = load_from_file(config)
command = NotebooksCommand(settings=settings, max_depth=level)
command.run()
@app.command()
def edit(note: Path):
settings = load_from_file()
@app.command(help="Edit or add a note into a notebook")
def edit(note: Path = Argument(
...,
help="""A valid, settings-relative path.
Be aware that valid can also mean implicit notes, that is, pointing to a
current-day note just by its notebook name. For example, if today is
2023-04-04 and you have a notebook containing a 2023-04-04.age note,
simply pointing to the notebook's name, e.g. `halig edit notebook` will
edit the 2023-04-04.age note. Also keep in mind that the note may or may
not exist and it'll be created accordingly; the only requirement is that
the notebook folder structure is correct and exists""",
), config: Optional[Path] = config_option):
settings = load_from_file(config)
command = EditCommand(settings=settings, note_path=note)
command.run()
@app.command()
def show(note: Path):
settings = load_from_file()
@app.command(help="Show a note's contents")
def show(
note: Path = Argument(
...,
help="""A valid, settings-relative path.
Be aware that valid can also mean implicit notes, that is, pointing to a
current-day note just by its notebook name. For example, if today is
2023-04-04 and you have a notebook containing a 2023-04-04.age note,
simply pointing to the notebook's name, e.g. `halig show notebook` will
print the 2023-04-04.age note""",
),
config: Optional[Path] = config_option,
):
settings = load_from_file(config)
command = ShowCommand(settings=settings, note_path=note)
command.run()

View file

@ -5,10 +5,20 @@ VERSIONS = ["3.10", "3.11"]
@nox.session(python=VERSIONS)
def tests(session):
session.run('pdm', 'export', '-G', 'tests', '-f', 'requirements', '-o', 'requirements.txt', external=True)
session.install('-r', "requirements.txt")
session.run(
"pdm",
"export",
"-G",
"tests",
"-f",
"requirements",
"-o",
"requirements.txt",
external=True,
)
session.install("-r", "requirements.txt")
session.run("make", "tests", external=True)
session.run('rm', "requirements.txt", external=True)
session.run("rm", "requirements.txt", external=True)
@nox.session(python=VERSIONS)

View file

@ -1,11 +1,3 @@
[tool.pdm.build]
includes = []
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[project]
authors = [
{ name = "cătălin", email = "185504a9@duck.com" },
@ -20,9 +12,37 @@ dependencies = [
"pendulum>=2.1.2",
]
name = "halig"
version = "0.1.2"
version = "0.1.7"
description = "age-encrypted, file-based, note-taking CLI app"
readme = "README.md"
keywords = ["cli", "notes", "age", "rage", "encryption", "notebook"]
license = { text = "GPL-3.0-or-later" }
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Natural Language :: English",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Terminals",
"Topic :: Utilities",
"Typing :: Typed"
]
[project.urls]
Homepage = "https://git.roboces.dev/catalin/halig"
Repository = "https://git.roboces.dev/catalin/halig"
Documentation = "https://git.roboces.dev/catalin/halig"
Changelog = "https://git.roboces.dev/catalin/halig"
[tool.pdm.build]
excludes = ["**/.pytest_cache/**"]
includes = []
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[project.scripts]
halig = "halig.main:app"

View file

@ -10,15 +10,15 @@ from halig.settings import Settings
@pytest.fixture()
def notes(notebooks_path: Path):
personal = (notebooks_path / "Personal")
work = (notebooks_path / "Work")
personal = notebooks_path / "Personal"
work = notebooks_path / "Work"
personal.mkdir()
work.mkdir()
personal_todos = (personal / "todos.age")
personal_todos = personal / "todos.age"
personal_todos.touch()
work_todos = (work / "todos.age")
work_todos = work / "todos.age"
work_todos.touch()
dailies = work / "Dailies"
@ -47,7 +47,9 @@ def current_note(notes, settings, encryptor) -> Path:
@pytest.fixture
def current_daily(notes, settings, encryptor) -> Path:
note_path = settings.notebooks_root_path / "Work" / "Dailies" / f"{utils.now().date()}.age"
note_path = (
settings.notebooks_root_path / "Work" / "Dailies" / f"{utils.now().date()}.age"
)
data = encryptor.encrypt("foo".encode())
with note_path.open("wb") as f:
f.write(data)
@ -60,4 +62,4 @@ def mock_edit(mocker):
with open(callargs[1], "wb") as f:
f.write("edited".encode())
mocker.patch('halig.commands.edit.subprocess.call', side_effect=edit)
mocker.patch("halig.commands.edit.subprocess.call", side_effect=edit)

View file

@ -8,11 +8,16 @@ def test_edit_raises_invalid_age_file(notes, settings: Settings):
note_path = settings.notebooks_root_path / "foo.txt"
note_path.touch()
with pytest.raises(ValueError, match="is not a valid AGE file"):
EditCommand(note_path, settings=settings, )
EditCommand(
note_path,
settings=settings,
)
def test_edit_current_note(mock_edit, current_note, settings: Settings, encryptor):
edit_command = EditCommand(note_path=settings.notebooks_root_path, settings=settings)
edit_command = EditCommand(
note_path=settings.notebooks_root_path, settings=settings
)
assert edit_command.note_path == current_note
edit_command.run()
with current_note.open("rb") as f:

View file

@ -8,18 +8,26 @@ from halig.settings import Settings
def test_show_raises_note_path_does_not_exist(notes, settings: Settings):
with pytest.raises(ValueError, match="does not exist"):
ShowCommand(Path('foo'), settings=settings, )
ShowCommand(
Path("foo"),
settings=settings,
)
def test_show_raises_note_path_is_not_age_valid(notes, settings: Settings):
note_path = settings.notebooks_root_path / "foo.txt"
note_path.touch()
with pytest.raises(ValueError, match="is not a valid AGE file"):
ShowCommand(note_path, settings=settings, )
ShowCommand(
note_path,
settings=settings,
)
def test_show_current_note(current_note, settings):
show_command = ShowCommand(note_path=settings.notebooks_root_path, settings=settings)
show_command = ShowCommand(
note_path=settings.notebooks_root_path, settings=settings
)
assert show_command.note_path == current_note
assert show_command.decrypt() == "foo"

View file

@ -12,9 +12,11 @@ from halig.settings import Settings
@pytest.fixture()
def halig_ssh_public_key():
return "ssh-ed25519 " \
"AAAAC3NzaC1lZDI1NTE5AAAAIGjHhIF/DlVCb2dRFMlKia7nij1Aq+zRDCaMIwe/VKDh" \
" foo@bar"
return (
"ssh-ed25519 "
"AAAAC3NzaC1lZDI1NTE5AAAAIGjHhIF/DlVCb2dRFMlKia7nij1Aq+zRDCaMIwe/VKDh"
" foo@bar"
)
@pytest.fixture()