dotsman 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dotsman-1.0.0/.github/dependabot.yml +19 -0
- dotsman-1.0.0/.github/workflows/ci.yml +40 -0
- dotsman-1.0.0/.github/workflows/publish.yml +67 -0
- dotsman-1.0.0/.gitignore +152 -0
- dotsman-1.0.0/.pre-commit-config.yaml +24 -0
- dotsman-1.0.0/LICENSE +27 -0
- dotsman-1.0.0/PKG-INFO +181 -0
- dotsman-1.0.0/README.md +166 -0
- dotsman-1.0.0/dots/__init__.py +1 -0
- dotsman-1.0.0/dots/cli.py +174 -0
- dotsman-1.0.0/dots/crypto.py +96 -0
- dotsman-1.0.0/dots/errors.py +80 -0
- dotsman-1.0.0/dots/fs.py +111 -0
- dotsman-1.0.0/dots/repo.py +519 -0
- dotsman-1.0.0/dots/ui.py +103 -0
- dotsman-1.0.0/dots.example.conf +17 -0
- dotsman-1.0.0/pyproject.toml +60 -0
- dotsman-1.0.0/tests/__init__.py +0 -0
- dotsman-1.0.0/tests/conftest.py +74 -0
- dotsman-1.0.0/tests/test_config.py +114 -0
- dotsman-1.0.0/tests/test_crypto.py +212 -0
- dotsman-1.0.0/tests/test_fs.py +327 -0
- dotsman-1.0.0/tests/test_repo.py +360 -0
- dotsman-1.0.0/tests/test_repo_encrypted.py +593 -0
- dotsman-1.0.0/uv.lock +391 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "daily"
|
|
7
|
+
groups:
|
|
8
|
+
python-dependencies:
|
|
9
|
+
patterns:
|
|
10
|
+
- "*"
|
|
11
|
+
|
|
12
|
+
- package-ecosystem: "github-actions"
|
|
13
|
+
directory: "/"
|
|
14
|
+
schedule:
|
|
15
|
+
interval: "daily"
|
|
16
|
+
groups:
|
|
17
|
+
github-actions:
|
|
18
|
+
patterns:
|
|
19
|
+
- "*"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- master
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- master
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
quality:
|
|
17
|
+
name: Quality Checks
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
21
|
+
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
cache-dependency-glob: "uv.lock"
|
|
25
|
+
- run: uv sync --frozen
|
|
26
|
+
- run: uv run ruff format --check dots/ tests/
|
|
27
|
+
- run: uv run ruff check dots/ tests/
|
|
28
|
+
- run: uv run pyright
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
name: Unit Tests
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
35
|
+
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
|
36
|
+
with:
|
|
37
|
+
enable-cache: true
|
|
38
|
+
cache-dependency-glob: "uv.lock"
|
|
39
|
+
- run: uv sync --frozen
|
|
40
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
ci:
|
|
13
|
+
name: CI
|
|
14
|
+
uses: ./.github/workflows/ci.yml
|
|
15
|
+
|
|
16
|
+
check-version:
|
|
17
|
+
name: Version Check
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
21
|
+
- name: Verify tag matches package version
|
|
22
|
+
env:
|
|
23
|
+
TAG: ${{ github.ref_name }}
|
|
24
|
+
run: |
|
|
25
|
+
TAG_VERSION="${TAG#v}"
|
|
26
|
+
PKG_VERSION=$(python3 -c "
|
|
27
|
+
import re
|
|
28
|
+
with open('pyproject.toml') as f:
|
|
29
|
+
for line in f:
|
|
30
|
+
m = re.match(r'^version\s*=\s*\"(.+)\"', line)
|
|
31
|
+
if m:
|
|
32
|
+
print(m.group(1))
|
|
33
|
+
break
|
|
34
|
+
")
|
|
35
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
36
|
+
echo "::error::Tag version ($TAG_VERSION) does not match package version ($PKG_VERSION)"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
echo "Version check passed: $TAG_VERSION"
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
name: Publish
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
needs:
|
|
45
|
+
- ci
|
|
46
|
+
- check-version
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
contents: write
|
|
50
|
+
environment:
|
|
51
|
+
name: pypi
|
|
52
|
+
url: https://pypi.org/p/dotsman
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
55
|
+
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
cache-dependency-glob: "uv.lock"
|
|
59
|
+
- name: Build package
|
|
60
|
+
run: uv build
|
|
61
|
+
- name: Publish to PyPI
|
|
62
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
|
63
|
+
- name: Create GitHub Release
|
|
64
|
+
env:
|
|
65
|
+
GH_TOKEN: ${{ github.token }}
|
|
66
|
+
TAG: ${{ github.ref_name }}
|
|
67
|
+
run: gh release create "$TAG" dist/* --generate-notes
|
dotsman-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Created by .ignore support plugin (hsz.mobi)
|
|
2
|
+
### JetBrains template
|
|
3
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
|
4
|
+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
5
|
+
|
|
6
|
+
# User-specific stuff:
|
|
7
|
+
.idea/**/workspace.xml
|
|
8
|
+
.idea/**/tasks.xml
|
|
9
|
+
.idea/dictionaries
|
|
10
|
+
.idea/shelf
|
|
11
|
+
|
|
12
|
+
# Sensitive or high-churn files:
|
|
13
|
+
.idea/**/dataSources/
|
|
14
|
+
.idea/**/dataSources.ids
|
|
15
|
+
.idea/**/dataSources.xml
|
|
16
|
+
.idea/**/dataSources.local.xml
|
|
17
|
+
.idea/**/sqlDataSources.xml
|
|
18
|
+
.idea/**/dynamic.xml
|
|
19
|
+
.idea/**/uiDesigner.xml
|
|
20
|
+
|
|
21
|
+
# Gradle:
|
|
22
|
+
.idea/**/gradle.xml
|
|
23
|
+
.idea/**/libraries
|
|
24
|
+
|
|
25
|
+
# CMake
|
|
26
|
+
cmake-build-debug/
|
|
27
|
+
|
|
28
|
+
# Mongo Explorer plugin:
|
|
29
|
+
.idea/**/mongoSettings.xml
|
|
30
|
+
|
|
31
|
+
## File-based project format:
|
|
32
|
+
*.iws
|
|
33
|
+
|
|
34
|
+
## Plugin-specific files:
|
|
35
|
+
|
|
36
|
+
# IntelliJ
|
|
37
|
+
out/
|
|
38
|
+
|
|
39
|
+
# mpeltonen/sbt-idea plugin
|
|
40
|
+
.idea_modules/
|
|
41
|
+
|
|
42
|
+
# JIRA plugin
|
|
43
|
+
atlassian-ide-plugin.xml
|
|
44
|
+
|
|
45
|
+
# Cursive Clojure plugin
|
|
46
|
+
.idea/replstate.xml
|
|
47
|
+
|
|
48
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
49
|
+
com_crashlytics_export_strings.xml
|
|
50
|
+
crashlytics.properties
|
|
51
|
+
crashlytics-build.properties
|
|
52
|
+
fabric.properties
|
|
53
|
+
### Python template
|
|
54
|
+
# Byte-compiled / optimized / DLL files
|
|
55
|
+
__pycache__/
|
|
56
|
+
*.py[cod]
|
|
57
|
+
*$py.class
|
|
58
|
+
|
|
59
|
+
# C extensions
|
|
60
|
+
*.so
|
|
61
|
+
|
|
62
|
+
# Distribution / packaging
|
|
63
|
+
.Python
|
|
64
|
+
build/
|
|
65
|
+
develop-eggs/
|
|
66
|
+
dist/
|
|
67
|
+
downloads/
|
|
68
|
+
eggs/
|
|
69
|
+
.eggs/
|
|
70
|
+
lib/
|
|
71
|
+
lib64/
|
|
72
|
+
parts/
|
|
73
|
+
sdist/
|
|
74
|
+
var/
|
|
75
|
+
wheels/
|
|
76
|
+
*.egg-info/
|
|
77
|
+
.installed.cfg
|
|
78
|
+
*.egg
|
|
79
|
+
|
|
80
|
+
# PyInstaller
|
|
81
|
+
# Usually these files are written by a python script from a template
|
|
82
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
83
|
+
*.manifest
|
|
84
|
+
*.spec
|
|
85
|
+
|
|
86
|
+
# Installer logs
|
|
87
|
+
pip-log.txt
|
|
88
|
+
pip-delete-this-directory.txt
|
|
89
|
+
|
|
90
|
+
# Unit test / coverage reports
|
|
91
|
+
htmlcov/
|
|
92
|
+
.tox/
|
|
93
|
+
.coverage
|
|
94
|
+
.coverage.*
|
|
95
|
+
.cache
|
|
96
|
+
nosetests.xml
|
|
97
|
+
coverage.xml
|
|
98
|
+
*.cover
|
|
99
|
+
.hypothesis/
|
|
100
|
+
|
|
101
|
+
# Translations
|
|
102
|
+
*.mo
|
|
103
|
+
*.pot
|
|
104
|
+
|
|
105
|
+
# Django stuff:
|
|
106
|
+
*.log
|
|
107
|
+
local_settings.py
|
|
108
|
+
|
|
109
|
+
# Flask stuff:
|
|
110
|
+
instance/
|
|
111
|
+
.webassets-cache
|
|
112
|
+
|
|
113
|
+
# Scrapy stuff:
|
|
114
|
+
.scrapy
|
|
115
|
+
|
|
116
|
+
# Sphinx documentation
|
|
117
|
+
docs/_build/
|
|
118
|
+
|
|
119
|
+
# PyBuilder
|
|
120
|
+
target/
|
|
121
|
+
|
|
122
|
+
# Jupyter Notebook
|
|
123
|
+
.ipynb_checkpoints
|
|
124
|
+
|
|
125
|
+
# pyenv
|
|
126
|
+
.python-version
|
|
127
|
+
|
|
128
|
+
# celery beat schedule file
|
|
129
|
+
celerybeat-schedule
|
|
130
|
+
|
|
131
|
+
# SageMath parsed files
|
|
132
|
+
*.sage.py
|
|
133
|
+
|
|
134
|
+
# Environments
|
|
135
|
+
.env
|
|
136
|
+
.venv
|
|
137
|
+
env/
|
|
138
|
+
venv/
|
|
139
|
+
ENV/
|
|
140
|
+
|
|
141
|
+
# Spyder project settings
|
|
142
|
+
.spyderproject
|
|
143
|
+
.spyproject
|
|
144
|
+
|
|
145
|
+
# Rope project settings
|
|
146
|
+
.ropeproject
|
|
147
|
+
|
|
148
|
+
# mkdocs documentation
|
|
149
|
+
/site
|
|
150
|
+
|
|
151
|
+
# mypy
|
|
152
|
+
.mypy_cache/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: ruff-format
|
|
5
|
+
name: ruff format
|
|
6
|
+
entry: uv run ruff format --check
|
|
7
|
+
language: system
|
|
8
|
+
types:
|
|
9
|
+
- python
|
|
10
|
+
|
|
11
|
+
- id: ruff-check
|
|
12
|
+
name: ruff check
|
|
13
|
+
entry: uv run ruff check
|
|
14
|
+
language: system
|
|
15
|
+
types:
|
|
16
|
+
- python
|
|
17
|
+
|
|
18
|
+
- id: pyright
|
|
19
|
+
name: pyright
|
|
20
|
+
entry: uv run pyright
|
|
21
|
+
language: system
|
|
22
|
+
types:
|
|
23
|
+
- python
|
|
24
|
+
pass_filenames: false
|
dotsman-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2017 Mathieu Deous
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
* Neither the name of Mathieu Deous nor the names of the project contributors
|
|
15
|
+
may be used to endorse or promote products derived from this software
|
|
16
|
+
without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL MATHIEU DEOUS BE LIABLE FOR ANY
|
|
22
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
25
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
dotsman-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dotsman
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Configuration files management tool
|
|
5
|
+
Project-URL: Repository, https://github.com/mdeous/dots
|
|
6
|
+
Author-email: Mathieu Deous <mat.deous@gmail.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Requires-Dist: gitpython>=3.1.46
|
|
11
|
+
Requires-Dist: pyrage>=1.3.0
|
|
12
|
+
Requires-Dist: rich>=15.0.0
|
|
13
|
+
Requires-Dist: typer>=0.24.1
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# dots
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+

|
|
20
|
+
[](https://github.com/mdeous/dots/actions/workflows/ci.yml)
|
|
21
|
+
[](https://github.com/mdeous/dots/actions/workflows/github-code-scanning/codeql)
|
|
22
|
+
|
|
23
|
+
Yet another dotfiles management tool.
|
|
24
|
+
|
|
25
|
+
## :sparkles: Features
|
|
26
|
+
|
|
27
|
+
- :link: Dotfiles stored in a central folder and symlinked to their real location
|
|
28
|
+
- :repeat: Automatic git versioning on every change
|
|
29
|
+
- :computer: Per-machine configuration via hostname-based config sections
|
|
30
|
+
- :warning: Conflict detection and resolution during sync
|
|
31
|
+
- :lock: Encryption for sensitive files
|
|
32
|
+
|
|
33
|
+
## :clipboard: Requirements
|
|
34
|
+
|
|
35
|
+
- Python 3.12+
|
|
36
|
+
- git
|
|
37
|
+
|
|
38
|
+
## :package: Installation
|
|
39
|
+
|
|
40
|
+
Clone the repository and install with [uv](https://docs.astral.sh/uv/):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/mdeous/dots.git
|
|
44
|
+
cd dots
|
|
45
|
+
uv sync
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or with pip:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/mdeous/dots.git
|
|
52
|
+
cd dots
|
|
53
|
+
pip install .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## :gear: Configuration
|
|
57
|
+
|
|
58
|
+
The configuration file should be located at `~/.dots.conf`.
|
|
59
|
+
|
|
60
|
+
Settings are organized in sections named after the machine's hostname. The `[DEFAULT]` section provides fallback values used when no hostname-specific section exists.
|
|
61
|
+
|
|
62
|
+
### Available settings
|
|
63
|
+
|
|
64
|
+
| Key | Description | Default |
|
|
65
|
+
| --------------- | ------------------------------------------------------------------------- | -------- |
|
|
66
|
+
| `repo_dir` | Path to the dotfiles repository | `~/dots` |
|
|
67
|
+
| `age_identity` | Path to an [age](https://age-encryption.org) identity file for encryption | _none_ |
|
|
68
|
+
| `ignored_files` | Comma-separated list of glob patterns to skip during sync | _none_ |
|
|
69
|
+
|
|
70
|
+
### Example
|
|
71
|
+
|
|
72
|
+
```ini
|
|
73
|
+
[DEFAULT]
|
|
74
|
+
repo_dir = ~/dots
|
|
75
|
+
|
|
76
|
+
[work-laptop]
|
|
77
|
+
repo_dir = ~/dotfiles
|
|
78
|
+
ignored_files = .bashrc, .config/personal/*
|
|
79
|
+
|
|
80
|
+
[home-desktop]
|
|
81
|
+
age_identity = ~/.age/dots.key
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## :rocket: Usage
|
|
85
|
+
|
|
86
|
+
### Global options
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
dots [--config PATH] [--verbose] [--version] COMMAND
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Option | Short | Description |
|
|
93
|
+
| ----------- | ----- | --------------------------------------------- |
|
|
94
|
+
| `--config` | `-c` | Path to config file (default: `~/.dots.conf`) |
|
|
95
|
+
| `--verbose` | `-v` | Display debug information |
|
|
96
|
+
| `--version` | `-V` | Display version and exit |
|
|
97
|
+
|
|
98
|
+
### `dots add <file>`
|
|
99
|
+
|
|
100
|
+
Add a file to the repository. The file is copied into the repo and replaced with a symlink.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
dots add ~/.bashrc
|
|
104
|
+
dots add ~/.config/git/config
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Option | Short | Description |
|
|
108
|
+
| ----------- | ----- | ------------------------------------------------ |
|
|
109
|
+
| `--encrypt` | `-e` | Encrypt the file with age before storing in repo |
|
|
110
|
+
|
|
111
|
+
When `--encrypt` is used, the file is stored as a `.age` file in the repo (encrypted at rest), with a decrypted working copy in a gitignored `.decrypted/` directory. The symlink points to the decrypted copy so the file remains usable. Requires `age_identity` to be set in the config.
|
|
112
|
+
|
|
113
|
+
### `dots remove <file>`
|
|
114
|
+
|
|
115
|
+
Remove a file from the repository. The symlink is replaced with the original file.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
dots remove ~/.bashrc
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `dots list`
|
|
122
|
+
|
|
123
|
+
List all files in the repository and their sync status.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
dots list
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `dots sync`
|
|
130
|
+
|
|
131
|
+
Synchronize the repository with the filesystem. Creates missing symlinks and detects conflicts.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
dots sync
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| Option | Short | Description |
|
|
138
|
+
| ---------------- | ----- | ---------------------------------------------- |
|
|
139
|
+
| `--force-relink` | `-r` | Overwrite links that point to the wrong target |
|
|
140
|
+
| `--force-add` | `-a` | Overwrite the repo version with the local file |
|
|
141
|
+
| `--force-link` | `-l` | Overwrite the local file with the repo version |
|
|
142
|
+
|
|
143
|
+
`--force-add` and `--force-link` are mutually exclusive. Without force flags, `dots` prompts interactively when conflicts are found.
|
|
144
|
+
|
|
145
|
+
## :lock: Encryption
|
|
146
|
+
|
|
147
|
+
Sensitive dotfiles can be encrypted at rest using [age](https://age-encryption.org). Encrypted files are safe to push to public repositories.
|
|
148
|
+
|
|
149
|
+
### Setup
|
|
150
|
+
|
|
151
|
+
1. Generate an age identity:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
age-keygen -o ~/.age/dots.key
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
2. Add the identity path to your config (`~/.dots.conf`):
|
|
158
|
+
|
|
159
|
+
```ini
|
|
160
|
+
[DEFAULT]
|
|
161
|
+
age_identity = ~/.age/dots.key
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
3. Add files with encryption:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
dots add --encrypt ~/.config/secrets.env
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### How it works
|
|
171
|
+
|
|
172
|
+
- Encrypted files are stored in the repo with a `.age` extension (e.g., `secrets.env.age`)
|
|
173
|
+
- Decrypted working copies live in a gitignored `.decrypted/` directory inside the repo
|
|
174
|
+
- Symlinks point to the decrypted copies, so files are usable as normal
|
|
175
|
+
- `dots sync` detects changes to decrypted files and re-encrypts them automatically
|
|
176
|
+
- On a new machine, `dots sync` decrypts all `.age` files and creates symlinks
|
|
177
|
+
- If the age identity is not configured, encrypted files are skipped with a warning during sync
|
|
178
|
+
|
|
179
|
+
## :scroll: License
|
|
180
|
+
|
|
181
|
+
BSD 3-Clause. See [LICENSE](LICENSE) for details.
|
dotsman-1.0.0/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# dots
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
[](https://github.com/mdeous/dots/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/mdeous/dots/actions/workflows/github-code-scanning/codeql)
|
|
7
|
+
|
|
8
|
+
Yet another dotfiles management tool.
|
|
9
|
+
|
|
10
|
+
## :sparkles: Features
|
|
11
|
+
|
|
12
|
+
- :link: Dotfiles stored in a central folder and symlinked to their real location
|
|
13
|
+
- :repeat: Automatic git versioning on every change
|
|
14
|
+
- :computer: Per-machine configuration via hostname-based config sections
|
|
15
|
+
- :warning: Conflict detection and resolution during sync
|
|
16
|
+
- :lock: Encryption for sensitive files
|
|
17
|
+
|
|
18
|
+
## :clipboard: Requirements
|
|
19
|
+
|
|
20
|
+
- Python 3.12+
|
|
21
|
+
- git
|
|
22
|
+
|
|
23
|
+
## :package: Installation
|
|
24
|
+
|
|
25
|
+
Clone the repository and install with [uv](https://docs.astral.sh/uv/):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/mdeous/dots.git
|
|
29
|
+
cd dots
|
|
30
|
+
uv sync
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or with pip:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/mdeous/dots.git
|
|
37
|
+
cd dots
|
|
38
|
+
pip install .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## :gear: Configuration
|
|
42
|
+
|
|
43
|
+
The configuration file should be located at `~/.dots.conf`.
|
|
44
|
+
|
|
45
|
+
Settings are organized in sections named after the machine's hostname. The `[DEFAULT]` section provides fallback values used when no hostname-specific section exists.
|
|
46
|
+
|
|
47
|
+
### Available settings
|
|
48
|
+
|
|
49
|
+
| Key | Description | Default |
|
|
50
|
+
| --------------- | ------------------------------------------------------------------------- | -------- |
|
|
51
|
+
| `repo_dir` | Path to the dotfiles repository | `~/dots` |
|
|
52
|
+
| `age_identity` | Path to an [age](https://age-encryption.org) identity file for encryption | _none_ |
|
|
53
|
+
| `ignored_files` | Comma-separated list of glob patterns to skip during sync | _none_ |
|
|
54
|
+
|
|
55
|
+
### Example
|
|
56
|
+
|
|
57
|
+
```ini
|
|
58
|
+
[DEFAULT]
|
|
59
|
+
repo_dir = ~/dots
|
|
60
|
+
|
|
61
|
+
[work-laptop]
|
|
62
|
+
repo_dir = ~/dotfiles
|
|
63
|
+
ignored_files = .bashrc, .config/personal/*
|
|
64
|
+
|
|
65
|
+
[home-desktop]
|
|
66
|
+
age_identity = ~/.age/dots.key
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## :rocket: Usage
|
|
70
|
+
|
|
71
|
+
### Global options
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
dots [--config PATH] [--verbose] [--version] COMMAND
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Option | Short | Description |
|
|
78
|
+
| ----------- | ----- | --------------------------------------------- |
|
|
79
|
+
| `--config` | `-c` | Path to config file (default: `~/.dots.conf`) |
|
|
80
|
+
| `--verbose` | `-v` | Display debug information |
|
|
81
|
+
| `--version` | `-V` | Display version and exit |
|
|
82
|
+
|
|
83
|
+
### `dots add <file>`
|
|
84
|
+
|
|
85
|
+
Add a file to the repository. The file is copied into the repo and replaced with a symlink.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
dots add ~/.bashrc
|
|
89
|
+
dots add ~/.config/git/config
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Option | Short | Description |
|
|
93
|
+
| ----------- | ----- | ------------------------------------------------ |
|
|
94
|
+
| `--encrypt` | `-e` | Encrypt the file with age before storing in repo |
|
|
95
|
+
|
|
96
|
+
When `--encrypt` is used, the file is stored as a `.age` file in the repo (encrypted at rest), with a decrypted working copy in a gitignored `.decrypted/` directory. The symlink points to the decrypted copy so the file remains usable. Requires `age_identity` to be set in the config.
|
|
97
|
+
|
|
98
|
+
### `dots remove <file>`
|
|
99
|
+
|
|
100
|
+
Remove a file from the repository. The symlink is replaced with the original file.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
dots remove ~/.bashrc
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `dots list`
|
|
107
|
+
|
|
108
|
+
List all files in the repository and their sync status.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
dots list
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `dots sync`
|
|
115
|
+
|
|
116
|
+
Synchronize the repository with the filesystem. Creates missing symlinks and detects conflicts.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
dots sync
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Option | Short | Description |
|
|
123
|
+
| ---------------- | ----- | ---------------------------------------------- |
|
|
124
|
+
| `--force-relink` | `-r` | Overwrite links that point to the wrong target |
|
|
125
|
+
| `--force-add` | `-a` | Overwrite the repo version with the local file |
|
|
126
|
+
| `--force-link` | `-l` | Overwrite the local file with the repo version |
|
|
127
|
+
|
|
128
|
+
`--force-add` and `--force-link` are mutually exclusive. Without force flags, `dots` prompts interactively when conflicts are found.
|
|
129
|
+
|
|
130
|
+
## :lock: Encryption
|
|
131
|
+
|
|
132
|
+
Sensitive dotfiles can be encrypted at rest using [age](https://age-encryption.org). Encrypted files are safe to push to public repositories.
|
|
133
|
+
|
|
134
|
+
### Setup
|
|
135
|
+
|
|
136
|
+
1. Generate an age identity:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
age-keygen -o ~/.age/dots.key
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
2. Add the identity path to your config (`~/.dots.conf`):
|
|
143
|
+
|
|
144
|
+
```ini
|
|
145
|
+
[DEFAULT]
|
|
146
|
+
age_identity = ~/.age/dots.key
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
3. Add files with encryption:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
dots add --encrypt ~/.config/secrets.env
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### How it works
|
|
156
|
+
|
|
157
|
+
- Encrypted files are stored in the repo with a `.age` extension (e.g., `secrets.env.age`)
|
|
158
|
+
- Decrypted working copies live in a gitignored `.decrypted/` directory inside the repo
|
|
159
|
+
- Symlinks point to the decrypted copies, so files are usable as normal
|
|
160
|
+
- `dots sync` detects changes to decrypted files and re-encrypts them automatically
|
|
161
|
+
- On a new machine, `dots sync` decrypts all `.age` files and creates symlinks
|
|
162
|
+
- If the age identity is not configured, encrypted files are skipped with a warning during sync
|
|
163
|
+
|
|
164
|
+
## :scroll: License
|
|
165
|
+
|
|
166
|
+
BSD 3-Clause. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "1.0.0"
|