feat: add -c/--config flag to every command
This commit is contained in:
parent
570c29d9f1
commit
6f45b76579
10 changed files with 163 additions and 51 deletions
6
Makefile
6
Makefile
|
|
@ -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
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -1,17 +1,43 @@
|
|||
# halig
|
||||
|
||||
[(r)age](https://github.com/woodruffw/pyrage) encrypted note-taking CLI app
|
||||

|
||||

|
||||

|
||||
[](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
|
||||
|
||||
```
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
from typer import Typer
|
||||
|
||||
app = Typer()
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
16
noxfile.py
16
noxfile.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue