Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
d628ce1c42 |
40 changed files with 573 additions and 2044 deletions
|
|
@ -1,73 +0,0 @@
|
||||||
---
|
|
||||||
name: checks
|
|
||||||
on: # yamllint disable-line rule:truthy
|
|
||||||
- 'push'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pre-commit:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: https://code.forgejo.org/actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Setup PDM
|
|
||||||
uses: pdm-project/setup-pdm@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pdm install -G linting
|
|
||||||
|
|
||||||
- uses: pre-commit/action@v3.0.1
|
|
||||||
|
|
||||||
tests-10:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: https://code.forgejo.org/actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Setup PDM
|
|
||||||
uses: pdm-project/setup-pdm@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pdm install -G testing
|
|
||||||
|
|
||||||
- run: make tests
|
|
||||||
|
|
||||||
tests-11:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: https://code.forgejo.org/actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
|
|
||||||
- name: Setup PDM
|
|
||||||
uses: pdm-project/setup-pdm@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pdm install -G testing
|
|
||||||
|
|
||||||
- run: make tests
|
|
||||||
|
|
||||||
tests-12:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: https://code.forgejo.org/actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Setup PDM
|
|
||||||
uses: pdm-project/setup-pdm@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pdm install -G testing
|
|
||||||
|
|
||||||
- run: make tests
|
|
||||||
|
|
@ -19,7 +19,7 @@ repos:
|
||||||
args: [ --fix=lf ]
|
args: [ --fix=lf ]
|
||||||
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
rev: v0.6.4
|
rev: v0.4.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
|
|
|
||||||
1
.python-version
Normal file
1
.python-version
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
3.12.2
|
||||||
12
Makefile
12
Makefile
|
|
@ -1,21 +1,21 @@
|
||||||
|
|
||||||
fmt:
|
linters:
|
||||||
pre-commit run --all-files --color always
|
pre-commit run --all-files --color always
|
||||||
|
|
||||||
.PHONY: tests
|
.PHONY: tests
|
||||||
tests:
|
tests:
|
||||||
pdm run pytest --cov=halig -vv tests --report-log reportlog.json
|
rye run pytest --cov=halig -vv tests --report-log reportlog.json
|
||||||
pdm run coverage html
|
rye run coverage html
|
||||||
pdm run coverage xml
|
rye run coverage xml
|
||||||
|
|
||||||
build:
|
build:
|
||||||
pdm build
|
pdm build
|
||||||
|
|
||||||
publish-pypi:
|
publish-pypi:
|
||||||
pdm publish -u $(PYPI_REGISTRY_USERNAME) -P $(PYPI_REGISTRY_PASSWORD)
|
rye publish -u $(PYPI_REGISTRY_USERNAME) -P $(PYPI_REGISTRY_PASSWORD)
|
||||||
|
|
||||||
publish-roboces:
|
publish-roboces:
|
||||||
pdm publish -u $(ROBOCES_REGISTRY_USERNAME) -P $(ROBOCES_REGISTRY_PASSWORD) -r https://git.roboces.dev/api/packages/catalin/pypi
|
rye publish -u $(ROBOCES_REGISTRY_USERNAME) -P $(ROBOCES_REGISTRY_PASSWORD) -r https://git.roboces.dev/api/packages/catalin/pypi
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
make publish-pypi
|
make publish-pypi
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
# halig
|
# halig
|
||||||
|
|
||||||
[](https://git.roboces.dev/catalin/halig/actions)
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
@ -12,6 +11,7 @@
|
||||||
it encrypts the new contents into an [age](https://github.com/FiloSottile/age) file that
|
it encrypts the new contents into an [age](https://github.com/FiloSottile/age) file that
|
||||||
you can store, _relatively_ safe, anywhere.
|
you can store, _relatively_ safe, anywhere.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Simple notebooks management with paths autocompletion
|
- Simple notebooks management with paths autocompletion
|
||||||
|
|
@ -50,6 +50,5 @@ EOF
|
||||||
halig edit some_notebook # edit today's note relative to <notebooks_root_path>/some_notebook
|
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 edit some_notebook/foo # edit <notebooks_root_path>/some_notebook/foo.age
|
||||||
halig notebooks # list current notebooks
|
halig notebooks # list current notebooks
|
||||||
halig git commit
|
|
||||||
halig git push
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
__version__ = "0.6.2"
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from git import Repo
|
|
||||||
from rich import print
|
|
||||||
|
|
||||||
from halig.commands.base import BaseCommand
|
|
||||||
from halig.encryption import Encryptor
|
|
||||||
from halig.settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class GitBaseCommand(BaseCommand):
|
|
||||||
@staticmethod
|
|
||||||
def __init_repo(repo_path: Path) -> Repo:
|
|
||||||
"""Check if `repo_path` is a git repo. If not, initialize it"""
|
|
||||||
|
|
||||||
if not (repo_path / ".git").is_dir():
|
|
||||||
print(f"[yellow] {repo_path} is not a git repo, initializing ...")
|
|
||||||
Repo.init(repo_path)
|
|
||||||
return Repo(repo_path)
|
|
||||||
|
|
||||||
return Repo(repo_path)
|
|
||||||
|
|
||||||
def __init__(self, settings: Settings, message: str | None = None):
|
|
||||||
super().__init__(settings)
|
|
||||||
self.settings = settings
|
|
||||||
self.encryptor = Encryptor(self.settings)
|
|
||||||
self.message = message or self.settings.default_commit_message
|
|
||||||
self.repo = self.__init_repo(self.settings.notebooks_root_path)
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
from halig.commands.git.base import GitBaseCommand
|
|
||||||
|
|
||||||
|
|
||||||
class GitCommitCommand(GitBaseCommand):
|
|
||||||
def __init__(self, message: str | None = None, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.message = message or self.settings.default_commit_message
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Add all .age files to git and commit them using gitpython"""
|
|
||||||
self.repo.index.add(
|
|
||||||
[str(path) for path in self.settings.notebooks_root_path.glob("**/*.age")]
|
|
||||||
)
|
|
||||||
self.repo.index.commit(self.message)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
from halig.commands.git.base import GitBaseCommand
|
|
||||||
|
|
||||||
|
|
||||||
class GitPullCommand(GitBaseCommand):
|
|
||||||
def __init__(
|
|
||||||
self, remotes: list[str] | None = None, ref: str | None = None, *args, **kwargs
|
|
||||||
):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.remotes = remotes
|
|
||||||
self.ref = ref
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Pull all changes from the remote git repo"""
|
|
||||||
if not self.remotes:
|
|
||||||
self.repo.remotes.origin.pull(self.ref or "main")
|
|
||||||
return
|
|
||||||
|
|
||||||
for remote in self.remotes:
|
|
||||||
self.repo.remotes[remote].pull(self.ref or "main")
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
from halig.commands.git.base import GitBaseCommand
|
|
||||||
|
|
||||||
|
|
||||||
class GitPushCommand(GitBaseCommand):
|
|
||||||
def __init__(self, remotes: list[str] | None = None, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.remotes = remotes
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Push all changes to the remote git repo"""
|
|
||||||
if not self.remotes:
|
|
||||||
self.repo.remotes.origin.push()
|
|
||||||
return
|
|
||||||
|
|
||||||
for remote in self.remotes:
|
|
||||||
self.repo.remotes[remote].push()
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
from rich import print
|
|
||||||
|
|
||||||
from halig.commands.git.base import GitBaseCommand
|
|
||||||
|
|
||||||
|
|
||||||
class GitStatusCommand(GitBaseCommand):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs) # pragma: no cover
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Show the status of the git repo, including unstaged *.age files"""
|
|
||||||
|
|
||||||
print(self.repo.git.status()) # pragma: no cover
|
|
||||||
26
noxfile.py
Normal file
26
noxfile.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import nox
|
||||||
|
|
||||||
|
VERSIONS = ["3.10", "3.11", "3.12"]
|
||||||
|
|
||||||
|
|
||||||
|
@nox.session(python=VERSIONS)
|
||||||
|
def tests(session):
|
||||||
|
session.run(
|
||||||
|
"pdm",
|
||||||
|
"export",
|
||||||
|
"-G",
|
||||||
|
"testing",
|
||||||
|
"-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)
|
||||||
|
|
||||||
|
|
||||||
|
@nox.session(python=VERSIONS)
|
||||||
|
def linters(session):
|
||||||
|
session.run("make", "linters", external=True)
|
||||||
116
pyproject.toml
116
pyproject.toml
|
|
@ -1,22 +1,21 @@
|
||||||
[project]
|
[project]
|
||||||
|
name = "halig"
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "cătălin", email = "185504a9@duck.com" },
|
{ name = "cătălin", email = "catalin@roboces.dev" }
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"typer>=0.12",
|
||||||
|
"rich>=13.3.3",
|
||||||
|
"pydantic>=2.7",
|
||||||
|
"pyyaml>=6.0",
|
||||||
|
"pyrage>=1.1",
|
||||||
|
"pendulum>=3.0",
|
||||||
|
"httpx>=0.27",
|
||||||
|
"platformdirs>=4.2",
|
||||||
|
"pydantic-settings>=2.0",
|
||||||
|
"hishel>=0.0.26",
|
||||||
]
|
]
|
||||||
requires-python = ">= 3.10"
|
requires-python = ">= 3.10"
|
||||||
dependencies = [
|
|
||||||
"typer>=0",
|
|
||||||
"rich>=13.8",
|
|
||||||
"pydantic>=2.8",
|
|
||||||
"pyyaml>=6.0",
|
|
||||||
"pyrage>=1",
|
|
||||||
"httpx>=0",
|
|
||||||
"platformdirs>=4.2",
|
|
||||||
"pydantic-settings>=2",
|
|
||||||
"hishel>=0",
|
|
||||||
"whenever>=0.6",
|
|
||||||
"gitpython>=3.1",
|
|
||||||
]
|
|
||||||
name = "halig"
|
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
description = "age-encrypted, file-based, note-taking CLI app"
|
description = "age-encrypted, file-based, note-taking CLI app"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -41,39 +40,24 @@ Homepage = "https://git.roboces.dev/catalin/halig"
|
||||||
Repository = "https://git.roboces.dev/catalin/halig"
|
Repository = "https://git.roboces.dev/catalin/halig"
|
||||||
Documentation = "https://git.roboces.dev/catalin/halig"
|
Documentation = "https://git.roboces.dev/catalin/halig"
|
||||||
Changelog = "https://git.roboces.dev/catalin/halig"
|
Changelog = "https://git.roboces.dev/catalin/halig"
|
||||||
|
|
||||||
[tool.pdm]
|
|
||||||
version = { source = "file", path = "halig/__version__.py" }
|
|
||||||
|
|
||||||
[tool.pdm.build]
|
|
||||||
excludes = ["**/.pytest_cache/**"]
|
|
||||||
includes = []
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["pdm-backend"]
|
|
||||||
build-backend = "pdm.backend"
|
|
||||||
|
|
||||||
[project.scripts]
|
|
||||||
halig = "halig.main:app"
|
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
testing = [
|
testing = [
|
||||||
"pytest>=7",
|
"pytest>=7.2.2",
|
||||||
"pytest-cov>=4",
|
"pytest-cov>=4.0.0",
|
||||||
"pyfakefs>=5",
|
"pyfakefs>=5.1.0",
|
||||||
"pytest-clarity>=1",
|
"pytest-clarity>=1.0.1",
|
||||||
"pytest-reportlog>=0",
|
"pytest-reportlog>=0.2.1",
|
||||||
"pytest-duration-insights>=0",
|
"pytest-duration-insights>=0.1.1",
|
||||||
"pytest-pretty>=1",
|
"pytest-pretty>=1.1.1",
|
||||||
"pytest-mock>=3",
|
"pytest-mock>=3.10.0",
|
||||||
"mock>=5",
|
"mock>=5.0.1",
|
||||||
"faker>=28.1.0",
|
|
||||||
]
|
]
|
||||||
linting = [
|
linting = [
|
||||||
"ruff>=0.4",
|
"black>=23.3.0",
|
||||||
"pyright>=1.1",
|
"ruff>=0.1.0",
|
||||||
"mypy>=1.1",
|
"pyright>=1.1.301",
|
||||||
"types-PyYAML>=6.0",
|
"mypy>=1.1.1",
|
||||||
|
"types-PyYAML>=6.0.12.9",
|
||||||
]
|
]
|
||||||
docs = [
|
docs = [
|
||||||
"mkdocs-material>=9.1.5",
|
"mkdocs-material>=9.1.5",
|
||||||
|
|
@ -82,6 +66,41 @@ docs = [
|
||||||
dev = [
|
dev = [
|
||||||
"bump-pydantic>=0.6.0",
|
"bump-pydantic>=0.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.rye]
|
||||||
|
managed = true
|
||||||
|
dev-dependencies = []
|
||||||
|
|
||||||
|
[tool.hatch.build]
|
||||||
|
skip-excluded-dirs = true
|
||||||
|
|
||||||
|
[tool.hatch.metadata]
|
||||||
|
allow-direct-references = true
|
||||||
|
|
||||||
|
[tool.hatch.version]
|
||||||
|
path = "src/halig/__version__.py"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.sdist]
|
||||||
|
exclude = [
|
||||||
|
"**/.pytest_cache/**",
|
||||||
|
"tests/**",
|
||||||
|
"noxfile.py",
|
||||||
|
"sample.env",
|
||||||
|
"Makefile",
|
||||||
|
".pre-commit-config.yaml",
|
||||||
|
".gitignore",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/halig"]
|
||||||
|
only-packages = true
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
halig = "halig.main:app"
|
||||||
[tool.pytest]
|
[tool.pytest]
|
||||||
mock_use_standalone_module = true
|
mock_use_standalone_module = true
|
||||||
|
|
||||||
|
|
@ -91,7 +110,7 @@ reportMissingTypeStubs = false
|
||||||
reportAttributeAccessIssue = false
|
reportAttributeAccessIssue = false
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
extend-select = ["W", "C90", "I", "N", "UP", "S", "BLE", "B", "A", "COM", "C4", "DTZ", "T10", "EM", "ISC", "T20", "PT", "RSE", "RET", "SIM", "PTH", "ERA", "PGH", "PL", "TRY", "RUF", "FURB", "PERF"]
|
extend-select = ["W", "C90", "I", "N", "UP", "S", "BLE", "B", "A", "COM", "C4", "DTZ", "T10", "EM", "ISC", "T20", "PT", "RSE", "RET", "SIM", "PTH", "ERA", "PGH", "PL", "TRY", "RUF"]
|
||||||
extend-ignore = ["S101", "ISC002", "COM812", "ISC001"]
|
extend-ignore = ["S101", "ISC002", "COM812", "ISC001"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
@ -108,10 +127,3 @@ module = [
|
||||||
"httpx_cache"
|
"httpx_cache"
|
||||||
]
|
]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
[tool.coverage.run]
|
|
||||||
omit = [
|
|
||||||
"halig/__version__.py",
|
|
||||||
"halig/literals.py",
|
|
||||||
"halig/main.py"
|
|
||||||
]
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
|
||||||
}
|
|
||||||
223
requirements-dev.lock
Normal file
223
requirements-dev.lock
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
# generated by rye
|
||||||
|
# use `rye lock` or `rye sync` to update this lockfile
|
||||||
|
#
|
||||||
|
# last locked with the following flags:
|
||||||
|
# pre: false
|
||||||
|
# features: []
|
||||||
|
# all-features: true
|
||||||
|
# with-sources: false
|
||||||
|
|
||||||
|
-e file:.
|
||||||
|
annotated-types==0.7.0
|
||||||
|
# via pydantic
|
||||||
|
anyio==4.4.0
|
||||||
|
# via httpx
|
||||||
|
babel==2.15.0
|
||||||
|
# via mkdocs-material
|
||||||
|
black==24.8.0
|
||||||
|
# via halig
|
||||||
|
bump-pydantic==0.8.0
|
||||||
|
# via halig
|
||||||
|
certifi==2024.7.4
|
||||||
|
# via httpcore
|
||||||
|
# via httpx
|
||||||
|
# via requests
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
# via requests
|
||||||
|
click==8.1.7
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocstrings
|
||||||
|
# via typer
|
||||||
|
clumper==0.2.15
|
||||||
|
# via pytest-duration-insights
|
||||||
|
colorama==0.4.6
|
||||||
|
# via griffe
|
||||||
|
# via mkdocs-material
|
||||||
|
coverage==7.6.0
|
||||||
|
# via pytest-cov
|
||||||
|
ghp-import==2.1.0
|
||||||
|
# via mkdocs
|
||||||
|
griffe==0.48.0
|
||||||
|
# via mkdocstrings-python
|
||||||
|
h11==0.14.0
|
||||||
|
# via httpcore
|
||||||
|
hishel==0.0.30
|
||||||
|
# via halig
|
||||||
|
httpcore==1.0.5
|
||||||
|
# via httpx
|
||||||
|
httpx==0.27.0
|
||||||
|
# via halig
|
||||||
|
# via hishel
|
||||||
|
idna==3.7
|
||||||
|
# via anyio
|
||||||
|
# via httpx
|
||||||
|
# via requests
|
||||||
|
iniconfig==2.0.0
|
||||||
|
# via pytest
|
||||||
|
jinja2==3.1.4
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
libcst==1.4.0
|
||||||
|
# via bump-pydantic
|
||||||
|
markdown==3.6
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
# via pymdown-extensions
|
||||||
|
markdown-it-py==3.0.0
|
||||||
|
# via rich
|
||||||
|
markupsafe==2.1.5
|
||||||
|
# via jinja2
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocstrings
|
||||||
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
|
mergedeep==1.3.4
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
mkdocs==1.6.0
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
mkdocs-autorefs==1.0.1
|
||||||
|
# via mkdocstrings
|
||||||
|
mkdocs-get-deps==0.2.0
|
||||||
|
# via mkdocs
|
||||||
|
mkdocs-material==9.5.31
|
||||||
|
# via halig
|
||||||
|
mkdocs-material-extensions==1.3.1
|
||||||
|
# via mkdocs-material
|
||||||
|
mkdocstrings==0.25.2
|
||||||
|
# via halig
|
||||||
|
# via mkdocstrings-python
|
||||||
|
mkdocstrings-python==1.10.7
|
||||||
|
# via mkdocstrings
|
||||||
|
mock==5.1.0
|
||||||
|
# via halig
|
||||||
|
mypy==1.11.1
|
||||||
|
# via halig
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
# via black
|
||||||
|
# via mypy
|
||||||
|
nodeenv==1.9.1
|
||||||
|
# via pyright
|
||||||
|
packaging==24.1
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
# via pytest
|
||||||
|
paginate==0.5.6
|
||||||
|
# via mkdocs-material
|
||||||
|
parse==1.20.2
|
||||||
|
# via pytest-duration-insights
|
||||||
|
pathspec==0.12.1
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
pendulum==3.0.0
|
||||||
|
# via halig
|
||||||
|
platformdirs==4.2.2
|
||||||
|
# via black
|
||||||
|
# via halig
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
# via mkdocstrings
|
||||||
|
pluggy==1.5.0
|
||||||
|
# via pytest
|
||||||
|
pprintpp==0.4.0
|
||||||
|
# via pytest-clarity
|
||||||
|
pydantic==2.8.2
|
||||||
|
# via halig
|
||||||
|
# via pydantic-settings
|
||||||
|
pydantic-core==2.20.1
|
||||||
|
# via pydantic
|
||||||
|
pydantic-settings==2.4.0
|
||||||
|
# via halig
|
||||||
|
pyfakefs==5.6.0
|
||||||
|
# via halig
|
||||||
|
pygments==2.18.0
|
||||||
|
# via mkdocs-material
|
||||||
|
# via rich
|
||||||
|
pymdown-extensions==10.9
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
pyrage==1.1.2
|
||||||
|
# via halig
|
||||||
|
pyright==1.1.374
|
||||||
|
# via halig
|
||||||
|
pytest==8.3.2
|
||||||
|
# via halig
|
||||||
|
# via pytest-clarity
|
||||||
|
# via pytest-cov
|
||||||
|
# via pytest-mock
|
||||||
|
# via pytest-pretty
|
||||||
|
# via pytest-reportlog
|
||||||
|
pytest-clarity==1.0.1
|
||||||
|
# via halig
|
||||||
|
pytest-cov==5.0.0
|
||||||
|
# via halig
|
||||||
|
pytest-duration-insights==0.1.2
|
||||||
|
# via halig
|
||||||
|
pytest-mock==3.14.0
|
||||||
|
# via halig
|
||||||
|
pytest-pretty==1.2.0
|
||||||
|
# via halig
|
||||||
|
pytest-reportlog==0.4.0
|
||||||
|
# via halig
|
||||||
|
# via pytest-duration-insights
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
# via ghp-import
|
||||||
|
# via pendulum
|
||||||
|
# via time-machine
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
# via pydantic-settings
|
||||||
|
pyyaml==6.0.1
|
||||||
|
# via halig
|
||||||
|
# via libcst
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
# via pymdown-extensions
|
||||||
|
# via pyyaml-env-tag
|
||||||
|
pyyaml-env-tag==0.1
|
||||||
|
# via mkdocs
|
||||||
|
regex==2024.7.24
|
||||||
|
# via mkdocs-material
|
||||||
|
requests==2.32.3
|
||||||
|
# via mkdocs-material
|
||||||
|
rich==13.7.1
|
||||||
|
# via bump-pydantic
|
||||||
|
# via halig
|
||||||
|
# via pytest-clarity
|
||||||
|
# via pytest-pretty
|
||||||
|
# via typer
|
||||||
|
ruff==0.5.6
|
||||||
|
# via halig
|
||||||
|
shellingham==1.5.4
|
||||||
|
# via typer
|
||||||
|
six==1.16.0
|
||||||
|
# via python-dateutil
|
||||||
|
sniffio==1.3.1
|
||||||
|
# via anyio
|
||||||
|
# via httpx
|
||||||
|
time-machine==2.14.2
|
||||||
|
# via pendulum
|
||||||
|
typer==0.12.3
|
||||||
|
# via bump-pydantic
|
||||||
|
# via halig
|
||||||
|
# via pytest-duration-insights
|
||||||
|
types-pyyaml==6.0.12.20240724
|
||||||
|
# via halig
|
||||||
|
typing-extensions==4.12.2
|
||||||
|
# via bump-pydantic
|
||||||
|
# via hishel
|
||||||
|
# via mypy
|
||||||
|
# via pydantic
|
||||||
|
# via pydantic-core
|
||||||
|
# via typer
|
||||||
|
tzdata==2024.1
|
||||||
|
# via pendulum
|
||||||
|
urllib3==2.2.2
|
||||||
|
# via requests
|
||||||
|
watchdog==4.0.1
|
||||||
|
# via mkdocs
|
||||||
223
requirements.lock
Normal file
223
requirements.lock
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
# generated by rye
|
||||||
|
# use `rye lock` or `rye sync` to update this lockfile
|
||||||
|
#
|
||||||
|
# last locked with the following flags:
|
||||||
|
# pre: false
|
||||||
|
# features: []
|
||||||
|
# all-features: true
|
||||||
|
# with-sources: false
|
||||||
|
|
||||||
|
-e file:.
|
||||||
|
annotated-types==0.7.0
|
||||||
|
# via pydantic
|
||||||
|
anyio==4.4.0
|
||||||
|
# via httpx
|
||||||
|
babel==2.15.0
|
||||||
|
# via mkdocs-material
|
||||||
|
black==24.8.0
|
||||||
|
# via halig
|
||||||
|
bump-pydantic==0.8.0
|
||||||
|
# via halig
|
||||||
|
certifi==2024.7.4
|
||||||
|
# via httpcore
|
||||||
|
# via httpx
|
||||||
|
# via requests
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
# via requests
|
||||||
|
click==8.1.7
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocstrings
|
||||||
|
# via typer
|
||||||
|
clumper==0.2.15
|
||||||
|
# via pytest-duration-insights
|
||||||
|
colorama==0.4.6
|
||||||
|
# via griffe
|
||||||
|
# via mkdocs-material
|
||||||
|
coverage==7.6.0
|
||||||
|
# via pytest-cov
|
||||||
|
ghp-import==2.1.0
|
||||||
|
# via mkdocs
|
||||||
|
griffe==0.48.0
|
||||||
|
# via mkdocstrings-python
|
||||||
|
h11==0.14.0
|
||||||
|
# via httpcore
|
||||||
|
hishel==0.0.30
|
||||||
|
# via halig
|
||||||
|
httpcore==1.0.5
|
||||||
|
# via httpx
|
||||||
|
httpx==0.27.0
|
||||||
|
# via halig
|
||||||
|
# via hishel
|
||||||
|
idna==3.7
|
||||||
|
# via anyio
|
||||||
|
# via httpx
|
||||||
|
# via requests
|
||||||
|
iniconfig==2.0.0
|
||||||
|
# via pytest
|
||||||
|
jinja2==3.1.4
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
libcst==1.4.0
|
||||||
|
# via bump-pydantic
|
||||||
|
markdown==3.6
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
# via pymdown-extensions
|
||||||
|
markdown-it-py==3.0.0
|
||||||
|
# via rich
|
||||||
|
markupsafe==2.1.5
|
||||||
|
# via jinja2
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocstrings
|
||||||
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
|
mergedeep==1.3.4
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
mkdocs==1.6.0
|
||||||
|
# via mkdocs-autorefs
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
mkdocs-autorefs==1.0.1
|
||||||
|
# via mkdocstrings
|
||||||
|
mkdocs-get-deps==0.2.0
|
||||||
|
# via mkdocs
|
||||||
|
mkdocs-material==9.5.31
|
||||||
|
# via halig
|
||||||
|
mkdocs-material-extensions==1.3.1
|
||||||
|
# via mkdocs-material
|
||||||
|
mkdocstrings==0.25.2
|
||||||
|
# via halig
|
||||||
|
# via mkdocstrings-python
|
||||||
|
mkdocstrings-python==1.10.7
|
||||||
|
# via mkdocstrings
|
||||||
|
mock==5.1.0
|
||||||
|
# via halig
|
||||||
|
mypy==1.11.1
|
||||||
|
# via halig
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
# via black
|
||||||
|
# via mypy
|
||||||
|
nodeenv==1.9.1
|
||||||
|
# via pyright
|
||||||
|
packaging==24.1
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
# via pytest
|
||||||
|
paginate==0.5.6
|
||||||
|
# via mkdocs-material
|
||||||
|
parse==1.20.2
|
||||||
|
# via pytest-duration-insights
|
||||||
|
pathspec==0.12.1
|
||||||
|
# via black
|
||||||
|
# via mkdocs
|
||||||
|
pendulum==3.0.0
|
||||||
|
# via halig
|
||||||
|
platformdirs==4.2.2
|
||||||
|
# via black
|
||||||
|
# via halig
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
# via mkdocstrings
|
||||||
|
pluggy==1.5.0
|
||||||
|
# via pytest
|
||||||
|
pprintpp==0.4.0
|
||||||
|
# via pytest-clarity
|
||||||
|
pydantic==2.8.2
|
||||||
|
# via halig
|
||||||
|
# via pydantic-settings
|
||||||
|
pydantic-core==2.20.1
|
||||||
|
# via pydantic
|
||||||
|
pydantic-settings==2.4.0
|
||||||
|
# via halig
|
||||||
|
pyfakefs==5.6.0
|
||||||
|
# via halig
|
||||||
|
pygments==2.18.0
|
||||||
|
# via mkdocs-material
|
||||||
|
# via rich
|
||||||
|
pymdown-extensions==10.9
|
||||||
|
# via mkdocs-material
|
||||||
|
# via mkdocstrings
|
||||||
|
pyrage==1.1.2
|
||||||
|
# via halig
|
||||||
|
pyright==1.1.374
|
||||||
|
# via halig
|
||||||
|
pytest==8.3.2
|
||||||
|
# via halig
|
||||||
|
# via pytest-clarity
|
||||||
|
# via pytest-cov
|
||||||
|
# via pytest-mock
|
||||||
|
# via pytest-pretty
|
||||||
|
# via pytest-reportlog
|
||||||
|
pytest-clarity==1.0.1
|
||||||
|
# via halig
|
||||||
|
pytest-cov==5.0.0
|
||||||
|
# via halig
|
||||||
|
pytest-duration-insights==0.1.2
|
||||||
|
# via halig
|
||||||
|
pytest-mock==3.14.0
|
||||||
|
# via halig
|
||||||
|
pytest-pretty==1.2.0
|
||||||
|
# via halig
|
||||||
|
pytest-reportlog==0.4.0
|
||||||
|
# via halig
|
||||||
|
# via pytest-duration-insights
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
# via ghp-import
|
||||||
|
# via pendulum
|
||||||
|
# via time-machine
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
# via pydantic-settings
|
||||||
|
pyyaml==6.0.1
|
||||||
|
# via halig
|
||||||
|
# via libcst
|
||||||
|
# via mkdocs
|
||||||
|
# via mkdocs-get-deps
|
||||||
|
# via pymdown-extensions
|
||||||
|
# via pyyaml-env-tag
|
||||||
|
pyyaml-env-tag==0.1
|
||||||
|
# via mkdocs
|
||||||
|
regex==2024.7.24
|
||||||
|
# via mkdocs-material
|
||||||
|
requests==2.32.3
|
||||||
|
# via mkdocs-material
|
||||||
|
rich==13.7.1
|
||||||
|
# via bump-pydantic
|
||||||
|
# via halig
|
||||||
|
# via pytest-clarity
|
||||||
|
# via pytest-pretty
|
||||||
|
# via typer
|
||||||
|
ruff==0.5.6
|
||||||
|
# via halig
|
||||||
|
shellingham==1.5.4
|
||||||
|
# via typer
|
||||||
|
six==1.16.0
|
||||||
|
# via python-dateutil
|
||||||
|
sniffio==1.3.1
|
||||||
|
# via anyio
|
||||||
|
# via httpx
|
||||||
|
time-machine==2.14.2
|
||||||
|
# via pendulum
|
||||||
|
typer==0.12.3
|
||||||
|
# via bump-pydantic
|
||||||
|
# via halig
|
||||||
|
# via pytest-duration-insights
|
||||||
|
types-pyyaml==6.0.12.20240724
|
||||||
|
# via halig
|
||||||
|
typing-extensions==4.12.2
|
||||||
|
# via bump-pydantic
|
||||||
|
# via hishel
|
||||||
|
# via mypy
|
||||||
|
# via pydantic
|
||||||
|
# via pydantic-core
|
||||||
|
# via typer
|
||||||
|
tzdata==2024.1
|
||||||
|
# via pendulum
|
||||||
|
urllib3==2.2.2
|
||||||
|
# via requests
|
||||||
|
watchdog==4.0.1
|
||||||
|
# via mkdocs
|
||||||
1
src/halig/__version__.py
Normal file
1
src/halig/__version__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
__version__ = "0.5.1a1"
|
||||||
|
|
@ -31,7 +31,7 @@ class EditCommand(BaseCommand):
|
||||||
self.encryptor = Encryptor(self.settings)
|
self.encryptor = Encryptor(self.settings)
|
||||||
|
|
||||||
if self.note_path.is_dir():
|
if self.note_path.is_dir():
|
||||||
self.note_path /= f"{utils.now_as_date()}.age"
|
self.note_path /= f"{utils.now().date()}.age"
|
||||||
|
|
||||||
if not self.note_path.name.endswith(".age"):
|
if not self.note_path.name.endswith(".age"):
|
||||||
err = f"File {self.note_path.name} is not a valid AGE file"
|
err = f"File {self.note_path.name} is not a valid AGE file"
|
||||||
|
|
@ -19,7 +19,7 @@ class ReencryptCommand(BaseCommand):
|
||||||
new_data = self.encryptor.encrypt(orig_data)
|
new_data = self.encryptor.encrypt(orig_data)
|
||||||
with note_path.open("wb") as fw:
|
with note_path.open("wb") as fw:
|
||||||
fw.write(new_data)
|
fw.write(new_data)
|
||||||
except pyrage.DecryptError: # type: ignore[reportGeneralTypeIssues] # noqa: PERF203
|
except pyrage.DecryptError: # type: ignore[reportGeneralTypeIssues]
|
||||||
print(
|
print(
|
||||||
f"[yellow] Could not reencrypt {note_path} because no matching keys"
|
f"[yellow] Could not reencrypt {note_path} because no matching keys"
|
||||||
f" were found, skipping ...",
|
f" were found, skipping ...",
|
||||||
|
|
@ -19,7 +19,7 @@ class ShowCommand(BaseCommand):
|
||||||
self.plain = plain
|
self.plain = plain
|
||||||
self.console = Console()
|
self.console = Console()
|
||||||
if self.note_path.is_dir():
|
if self.note_path.is_dir():
|
||||||
self.note_path /= f"{utils.now_as_date()}.age"
|
self.note_path /= f"{utils.now().date()}.age"
|
||||||
|
|
||||||
if not self.note_path.exists():
|
if not self.note_path.exists():
|
||||||
err = f"File {self.note_path.name} does not exist"
|
err = f"File {self.note_path.name} does not exist"
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
# APPS
|
|
||||||
GIT_HELP = "Git integration"
|
|
||||||
# COMMANDS
|
# COMMANDS
|
||||||
COMMANDS_NOTEBOOKS_HELP = "List all notebooks and notes, tree-style"
|
COMMANDS_NOTEBOOKS_HELP = "List all notebooks and notes, tree-style"
|
||||||
COMMANDS_EDIT_HELP = "Edit or add a note into a notebook"
|
COMMANDS_EDIT_HELP = "Edit or add a note into a notebook"
|
||||||
|
|
@ -12,12 +10,6 @@ which are indexed into a SQLite FTS5 database located at `~/.cache/halig/halig.d
|
||||||
COMMANDS_REENCRYPT_HELP = """Reencrypt all available notes. This operation is useful
|
COMMANDS_REENCRYPT_HELP = """Reencrypt all available notes. This operation is useful
|
||||||
when new public keys have been added to the config file and you want the notes
|
when new public keys have been added to the config file and you want the notes
|
||||||
to be seen by the new pairing private keys"""
|
to be seen by the new pairing private keys"""
|
||||||
COMMANDS_GIT_COMMIT_HELP = "Commit all .age files to git"
|
|
||||||
COMMANDS_GIT_PUSH_HELP = "Push all .age files to git"
|
|
||||||
COMMANDS_GIT_PULL_HELP = "Pull all .age files from git"
|
|
||||||
COMMANDS_GIT_STATUS_HELP = (
|
|
||||||
"Show the status of the git repo, including unstaged *.age files"
|
|
||||||
)
|
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
OPTION_CONFIG_HELP = "Configuration file. Must be YAML and schema compatible"
|
OPTION_CONFIG_HELP = "Configuration file. Must be YAML and schema compatible"
|
||||||
|
|
@ -9,10 +9,6 @@ from typer import Argument, Option, Typer
|
||||||
from halig import literals
|
from halig import literals
|
||||||
from halig.__version__ import __version__
|
from halig.__version__ import __version__
|
||||||
from halig.commands.edit import EditCommand
|
from halig.commands.edit import EditCommand
|
||||||
from halig.commands.git.commit import GitCommitCommand
|
|
||||||
from halig.commands.git.pull import GitPullCommand
|
|
||||||
from halig.commands.git.push import GitPushCommand
|
|
||||||
from halig.commands.git.status import GitStatusCommand
|
|
||||||
from halig.commands.import_unencrypted import ImportCommand
|
from halig.commands.import_unencrypted import ImportCommand
|
||||||
from halig.commands.notebooks import NotebooksCommand
|
from halig.commands.notebooks import NotebooksCommand
|
||||||
from halig.commands.reencrypt import ReencryptCommand
|
from halig.commands.reencrypt import ReencryptCommand
|
||||||
|
|
@ -21,16 +17,7 @@ from halig.commands.show import ShowCommand
|
||||||
from halig.settings import load_from_file
|
from halig.settings import load_from_file
|
||||||
from halig.utils import capture
|
from halig.utils import capture
|
||||||
|
|
||||||
git_app = Typer(
|
app = Typer(pretty_exceptions_enable=False, pretty_exceptions_show_locals=False)
|
||||||
name="git",
|
|
||||||
help=literals.GIT_HELP,
|
|
||||||
pretty_exceptions_enable=False,
|
|
||||||
pretty_exceptions_show_locals=False,
|
|
||||||
)
|
|
||||||
app = Typer(
|
|
||||||
name="halig", pretty_exceptions_enable=False, pretty_exceptions_show_locals=False
|
|
||||||
)
|
|
||||||
app.add_typer(git_app)
|
|
||||||
|
|
||||||
config_option = Option(None, "--config", "-c", help=literals.OPTION_CONFIG_HELP)
|
config_option = Option(None, "--config", "-c", help=literals.OPTION_CONFIG_HELP)
|
||||||
|
|
||||||
|
|
@ -138,9 +125,8 @@ def import_unencrypted(
|
||||||
def search(
|
def search(
|
||||||
term: str,
|
term: str,
|
||||||
index: bool = Option(False, help=literals.OPTION_INDEX_HELP),
|
index: bool = Option(False, help=literals.OPTION_INDEX_HELP),
|
||||||
config: Optional[Path] = config_option, # noqa: UP007
|
|
||||||
):
|
):
|
||||||
settings = load_from_file(config)
|
settings = load_from_file()
|
||||||
command = SearchCommand(
|
command = SearchCommand(
|
||||||
term=term,
|
term=term,
|
||||||
index=index,
|
index=index,
|
||||||
|
|
@ -150,53 +136,14 @@ def search(
|
||||||
|
|
||||||
|
|
||||||
@app.command(help=literals.COMMANDS_REENCRYPT_HELP)
|
@app.command(help=literals.COMMANDS_REENCRYPT_HELP)
|
||||||
def reencrypt(config: Path | None = config_option):
|
def reencrypt():
|
||||||
settings = load_from_file(config)
|
settings = load_from_file()
|
||||||
command = ReencryptCommand(
|
command = ReencryptCommand(
|
||||||
settings=settings,
|
settings=settings,
|
||||||
)
|
)
|
||||||
command.run()
|
command.run()
|
||||||
|
|
||||||
|
|
||||||
@git_app.command(name="commit", help=literals.COMMANDS_GIT_COMMIT_HELP)
|
|
||||||
def git_commit(
|
|
||||||
message: str | None = None,
|
|
||||||
config: Path | None = config_option,
|
|
||||||
):
|
|
||||||
settings = load_from_file(config)
|
|
||||||
command = GitCommitCommand(settings=settings, message=message)
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
|
|
||||||
@git_app.command(name="push", help=literals.COMMANDS_GIT_PUSH_HELP)
|
|
||||||
def git_push(
|
|
||||||
remotes: list[str] | None = None,
|
|
||||||
config: Path | None = config_option,
|
|
||||||
):
|
|
||||||
settings = load_from_file(config)
|
|
||||||
command = GitPushCommand(settings=settings, remotes=remotes)
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
|
|
||||||
@git_app.command(name="pull", help=literals.COMMANDS_GIT_PULL_HELP)
|
|
||||||
def git_pull(
|
|
||||||
remotes: list[str] | None = None,
|
|
||||||
config: Path | None = config_option,
|
|
||||||
):
|
|
||||||
settings = load_from_file(config)
|
|
||||||
command = GitPullCommand(settings=settings, remotes=remotes)
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
|
|
||||||
@git_app.command(name="status", help=literals.COMMANDS_GIT_STATUS_HELP)
|
|
||||||
def git_status(
|
|
||||||
config: Path | None = config_option,
|
|
||||||
):
|
|
||||||
settings = load_from_file(config)
|
|
||||||
command = GitStatusCommand(settings=settings)
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
|
|
||||||
@app.command(help=literals.COMMANDS_VERSION)
|
@app.command(help=literals.COMMANDS_VERSION)
|
||||||
@capture
|
@capture
|
||||||
def version():
|
def version():
|
||||||
|
|
@ -28,7 +28,7 @@ class Settings(BaseSettings):
|
||||||
cache_path (DirectoryPath): a *valid* path used to cache some stuff,
|
cache_path (DirectoryPath): a *valid* path used to cache some stuff,
|
||||||
particularly remote public keys. Defaults to $XDG_CACHE_HOME/halig
|
particularly remote public keys. Defaults to $XDG_CACHE_HOME/halig
|
||||||
remote_public_keys_timeout (float): time after which the retrieval of external public keys
|
remote_public_keys_timeout (float): time after which the retrieval of external public keys
|
||||||
(e.g. GitHub ssh keys) should be interrupted. Defaults to 0.5.
|
(e.g. github ssh keys) should be interrupted. Defaults to 0.5.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
notebooks_root_path: DirectoryPath
|
notebooks_root_path: DirectoryPath
|
||||||
|
|
@ -47,7 +47,6 @@ class Settings(BaseSettings):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
remote_public_keys_timeout: float = 0.5
|
remote_public_keys_timeout: float = 0.5
|
||||||
default_commit_message: str = "Update notebooks"
|
|
||||||
|
|
||||||
@field_validator("identity_paths", "recipient_paths", mode="before")
|
@field_validator("identity_paths", "recipient_paths", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -2,16 +2,14 @@ import sys
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
import pendulum
|
||||||
|
from pendulum.tz import local_timezone
|
||||||
from rich import print
|
from rich import print
|
||||||
from whenever import Instant
|
|
||||||
|
|
||||||
|
|
||||||
def now():
|
def now():
|
||||||
return Instant.now()
|
tz = local_timezone()
|
||||||
|
return pendulum.now(tz) # type: ignore[reportArgumentType]
|
||||||
|
|
||||||
def now_as_date():
|
|
||||||
return now().py_datetime().date()
|
|
||||||
|
|
||||||
|
|
||||||
def capture(fn: Callable):
|
def capture(fn: Callable):
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pendulum
|
||||||
import pytest as pytest
|
import pytest as pytest
|
||||||
|
|
||||||
from halig import utils
|
from halig import utils
|
||||||
|
|
@ -23,10 +24,10 @@ def notes(notebooks_path: Path):
|
||||||
dailies = work / "Dailies"
|
dailies = work / "Dailies"
|
||||||
dailies.mkdir()
|
dailies.mkdir()
|
||||||
|
|
||||||
dt = utils.now()
|
dt = pendulum.now()
|
||||||
for day_offset in range(10):
|
for day_offset in range(10):
|
||||||
dt = dt.subtract(hours=day_offset*24)
|
dt = dt.subtract(days=day_offset)
|
||||||
(dailies / f"{dt.py_datetime().date()}.age").touch()
|
(dailies / f"{dt.date()}.age").touch()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
|
@ -55,7 +56,7 @@ def notebooks_command(settings: Settings):
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def current_note(notes, settings, encryptor) -> Path:
|
def current_note(notes, settings, encryptor) -> Path:
|
||||||
note_path = settings.notebooks_root_path / f"{utils.now_as_date()}.age"
|
note_path = settings.notebooks_root_path / f"{utils.now().date()}.age"
|
||||||
note_path.touch()
|
note_path.touch()
|
||||||
data = encryptor.encrypt(b"foo")
|
data = encryptor.encrypt(b"foo")
|
||||||
with note_path.open("wb") as f:
|
with note_path.open("wb") as f:
|
||||||
|
|
@ -66,7 +67,7 @@ def current_note(notes, settings, encryptor) -> Path:
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def current_daily(notes, settings, encryptor) -> Path:
|
def current_daily(notes, settings, encryptor) -> Path:
|
||||||
note_path = (
|
note_path = (
|
||||||
settings.notebooks_root_path / "Work" / "Dailies" / f"{utils.now_as_date()}.age"
|
settings.notebooks_root_path / "Work" / "Dailies" / f"{utils.now().date()}.age"
|
||||||
)
|
)
|
||||||
data = encryptor.encrypt(b"foo")
|
data = encryptor.encrypt(b"foo")
|
||||||
with note_path.open("wb") as f:
|
with note_path.open("wb") as f:
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from halig.commands.git.commit import GitCommitCommand
|
|
||||||
from halig.settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def command(settings: Settings):
|
|
||||||
return GitCommitCommand(settings=settings)
|
|
||||||
|
|
||||||
|
|
||||||
def test_repo_is_not_initialized(settings):
|
|
||||||
"""Given that settings.notebooks_root_path is not a git repo, assert that the command
|
|
||||||
initializes the repo upon instantiation"""
|
|
||||||
|
|
||||||
assert not (settings.notebooks_root_path / ".git").is_dir()
|
|
||||||
GitCommitCommand(settings=settings)
|
|
||||||
assert (settings.notebooks_root_path / ".git").is_dir()
|
|
||||||
|
|
||||||
|
|
||||||
def test_repo_is_initialized(settings):
|
|
||||||
"""Manually initialize a repo in settings.notebooks_root_path and check that the command instantiation
|
|
||||||
is not reinitializing it"""
|
|
||||||
|
|
||||||
p = subprocess.Popen(["git", "init"], cwd=settings.notebooks_root_path)
|
|
||||||
p.wait()
|
|
||||||
assert (settings.notebooks_root_path / ".git").is_dir()
|
|
||||||
GitCommitCommand(settings=settings)
|
|
||||||
assert (settings.notebooks_root_path / ".git").is_dir()
|
|
||||||
|
|
||||||
|
|
||||||
def test_run(settings, command, faker):
|
|
||||||
"""Create a bunch of .age and non-.age files and assert that all .age files are added to git and that the commit
|
|
||||||
message is set"""
|
|
||||||
|
|
||||||
for _ in range(10):
|
|
||||||
random_file = settings.notebooks_root_path / f"{faker.word()}.txt"
|
|
||||||
random_file.touch()
|
|
||||||
|
|
||||||
for _ in range(10):
|
|
||||||
random_age_file = settings.notebooks_root_path / f"{faker.word()}.age"
|
|
||||||
random_age_file.touch()
|
|
||||||
|
|
||||||
command.run()
|
|
||||||
assert settings.notebooks_root_path / ".git" / "index"
|
|
||||||
assert settings.notebooks_root_path / ".git" / "index" / "stage"
|
|
||||||
|
|
||||||
assert command.message in command.repo.git.log("--pretty=oneline").splitlines()[0]
|
|
||||||
|
|
||||||
assert "nothing added to commit but untracked files present (use \"git add\" to track)" in command.repo.git.status()
|
|
||||||
assert ".age" not in command.repo.git.status()
|
|
||||||
|
|
||||||
|
|
||||||
def test_custom_commit_message(settings, command, faker):
|
|
||||||
command.message = faker.word()
|
|
||||||
command.run()
|
|
||||||
assert command.message in command.repo.git.log("--pretty=oneline").splitlines()[0]
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import shutil
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from git import Repo
|
|
||||||
|
|
||||||
from halig.commands.git.pull import GitPullCommand
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def command(settings, faker):
|
|
||||||
"""Configure a local remote for testing located at settings.notebooks_root_path/../remote, push some .age files to
|
|
||||||
that remote
|
|
||||||
"""
|
|
||||||
command = GitPullCommand(settings=settings)
|
|
||||||
|
|
||||||
new_path = shutil.copytree(settings.notebooks_root_path, settings.notebooks_root_path / "../remote")
|
|
||||||
new_path = new_path.resolve()
|
|
||||||
|
|
||||||
command.repo.create_remote("origin", str(new_path))
|
|
||||||
|
|
||||||
remote_repo = Repo(new_path)
|
|
||||||
for _ in range(10):
|
|
||||||
random_age_file = new_path / f"{faker.word()}.age"
|
|
||||||
random_age_file.touch()
|
|
||||||
remote_repo.index.add([str(random_age_file)])
|
|
||||||
remote_repo.index.commit("Update notebooks")
|
|
||||||
|
|
||||||
return command
|
|
||||||
|
|
||||||
def test_pull_from_origin(command):
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
def test_pull_from_custom_origin(settings, command):
|
|
||||||
remote_path = settings.notebooks_root_path / "../remote"
|
|
||||||
command.repo.create_remote("custom", str(remote_path.resolve()))
|
|
||||||
command.remotes = ["custom"]
|
|
||||||
command.run()
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import shutil
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from halig.commands.git.commit import GitCommitCommand
|
|
||||||
from halig.commands.git.push import GitPushCommand
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def command(settings, faker):
|
|
||||||
"""Configure a local remote for testing"""
|
|
||||||
commit_command = GitCommitCommand(settings=settings)
|
|
||||||
new_path = shutil.copytree(settings.notebooks_root_path, settings.notebooks_root_path / "../remote")
|
|
||||||
new_path = new_path.resolve()
|
|
||||||
for _ in range(10):
|
|
||||||
random_age_file = settings.notebooks_root_path / f"{faker.word()}.age"
|
|
||||||
random_age_file.touch()
|
|
||||||
commit_command.run()
|
|
||||||
|
|
||||||
push_command = GitPushCommand(settings=settings)
|
|
||||||
|
|
||||||
push_command.repo.create_remote("origin", str(new_path))
|
|
||||||
|
|
||||||
return push_command
|
|
||||||
|
|
||||||
|
|
||||||
def test_push_to_origin(command):
|
|
||||||
"""Test that the command pushes to the origin remote"""
|
|
||||||
command.run()
|
|
||||||
|
|
||||||
|
|
||||||
def test_push_to_custom_remote(settings, command):
|
|
||||||
"""Test that the command pushes to a custom remote"""
|
|
||||||
|
|
||||||
remote_path = settings.notebooks_root_path / "../remote"
|
|
||||||
command.repo.create_remote("custom", str(remote_path.resolve()))
|
|
||||||
command.remotes = ["custom"]
|
|
||||||
command.run()
|
|
||||||
|
|
@ -71,14 +71,19 @@ def notebooks_path(tmp_path) -> Path:
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def settings(notebooks_path: Path, halig_ssh_path) -> Settings:
|
def settings(notebooks_path: Path, halig_ssh_path) -> Settings:
|
||||||
return Settings(notebooks_root_path=notebooks_path,identity_paths=[halig_ssh_path / "id_ed25519"],recipient_paths=[halig_ssh_path / "id_ed25519.pub"])
|
return Settings(
|
||||||
|
notebooks_root_path=notebooks_path,
|
||||||
|
identity_paths=[halig_ssh_path / "id_ed25519"],
|
||||||
|
recipient_paths=[halig_ssh_path / "id_ed25519.pub"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def settings_file_path(settings, halig_config_path: Path, notebooks_path: Path) -> Path:
|
def settings_file_path(halig_config_path: Path, notebooks_path: Path) -> Path:
|
||||||
yaml_file = halig_config_path / "halig.yml"
|
yaml_file = halig_config_path / "halig.yml"
|
||||||
yaml_file.touch()
|
yaml_file.touch()
|
||||||
s = Settings(notebooks_root_path=notebooks_path, identity_paths=settings.identity_paths, recipient_paths=settings.recipient_paths)
|
s = Settings(notebooks_root_path=notebooks_path)
|
||||||
|
# `.dict()` doesn't serialize some fields that yaml doesn't understand
|
||||||
serialized = json.loads(s.model_dump_json())
|
serialized = json.loads(s.model_dump_json())
|
||||||
with yaml_file.open("w") as f:
|
with yaml_file.open("w") as f:
|
||||||
yaml.safe_dump(serialized, f)
|
yaml.safe_dump(serialized, f)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ from halig.settings import Settings, load_from_file
|
||||||
|
|
||||||
|
|
||||||
def test_settings_from_env(settings: Settings, notebooks_root_path_envvar):
|
def test_settings_from_env(settings: Settings, notebooks_root_path_envvar):
|
||||||
from_env_settings = Settings(recipient_paths=settings.recipient_paths,
|
from_env_settings = Settings() # type: ignore[call-arg]
|
||||||
identity_paths=settings.identity_paths) # type: ignore[call-arg]
|
|
||||||
assert from_env_settings.notebooks_root_path == settings.notebooks_root_path
|
assert from_env_settings.notebooks_root_path == settings.notebooks_root_path
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,10 +29,3 @@ def test_load_from_non_existing_file_path_raises_file_not_found_error(halig_conf
|
||||||
file = halig_config_path / "some_invalid_file.yml"
|
file = halig_config_path / "some_invalid_file.yml"
|
||||||
with pytest.raises(FileNotFoundError, match=f"File {file} does not exist"):
|
with pytest.raises(FileNotFoundError, match=f"File {file} does not exist"):
|
||||||
load_from_file(file)
|
load_from_file(file)
|
||||||
|
|
||||||
|
|
||||||
def test_settings_identity_paths_is_not_list_is_converted(settings):
|
|
||||||
s = Settings(identity_paths=settings.identity_paths[0], recipient_paths=settings.recipient_paths[0],
|
|
||||||
notebooks_root_path=settings.notebooks_root_path)
|
|
||||||
assert s.identity_paths == [settings.identity_paths[0]]
|
|
||||||
assert s.recipient_paths == [settings.recipient_paths[0]]
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue