Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
python-version: "3.13"
cache: pip

- name: Install ruff
run: pip install ruff==0.15.12
- name: Install deps
run: uv sync
Comment on lines +21 to +22

- name: Run ruff check
run: ruff check bootstrap/
run: uv run ruff check bootstrap/

- name: Run ruff format (check only)
run: ruff format --check bootstrap/
run: uv run ruff format --check bootstrap/
10 changes: 5 additions & 5 deletions .github/workflows/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
python-version: "3.13"
cache: pip

- name: Install jsonschema
run: pip install 'jsonschema>=4.14.0'
- name: Install deps
run: uv sync
Comment on lines +21 to +22

- name: Validate sample_config.json against bootstrap/schema.py
run: |
python - <<'PY'
uv run python - <<'PY'
import json
from bootstrap.schema import config_validate
with open("sample_config.json") as f:
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ on:
paths:
- 'tests/**'
- 'bootstrap/**'
- 'poetry.lock'
- 'uv.lock'
- 'pyproject.toml'
pull_request:
branches: [main]
paths:
- 'tests/**'
- 'bootstrap/**'
- 'poetry.lock'
- 'uv.lock'
- 'pyproject.toml'

jobs:
Expand All @@ -27,13 +27,13 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
python-version: "3.13"
cache: pip

- name: Install runtime deps
run: pip install click 'jsonschema>=4.14.0'
- name: Install deps
run: uv sync
Comment on lines +35 to +36

- name: Run smoke tests
run: python -m unittest discover -v tests
run: uv run python -m unittest discover -v tests
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.1] - 2026-06-05
- switch project tooling from Poetry to `uv`: PEP 621 `pyproject.toml`, `uv.lock`, hatchling build backend, `Makefile` and CI workflows driven by `uv sync`/`uv run`

## [1.3.0] - 2026-05-09

Windows support and the cross-platform refactors needed to make it land cleanly.
Expand Down
34 changes: 11 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,55 +1,43 @@
.PHONY: help install-dev test lint schema ci

# Python interpreter inside the project venv. Works on Windows GNU Make
# (forward slashes are fine) and on Linux/Mac. Check $(OS) == Windows_NT
# explicitly — testing for "non-empty" would mis-route Linux/Darwin shells
# that happen to export OS=Linux / OS=Darwin to the Windows path.
ifeq ($(OS),Windows_NT)
PY := .venv/Scripts/python
else
PY := .venv/bin/python
endif

# ---- Default: show targets ----
help:
@echo "Common targets:"
@echo " make test Run the same unittest command CI runs (.github/workflows/test.yml)"
@echo " make lint Run the same ruff checks CI runs (.github/workflows/lint.yml)"
@echo " make schema Validate sample_config.json (.github/workflows/schema.yml)"
@echo " make ci Full pre-push verification: lint + test + schema"
@echo " make install-dev Install runtime + dev deps (adds ruff) via Poetry"
@echo " make install-dev Sync runtime + dev deps (adds ruff) via uv"

# `poetry install` alone leaves out the dev group, so `make lint` would fail
# on a fresh clone. `--with dev` pulls in ruff at the CI-pinned version.
# The committed poetry.toml pins virtualenvs.in-project = true so Poetry
# creates the venv at .venv/ inside the project, which is where the $(PY)
# above looks for it; without that, Poetry would cache the venv elsewhere
# and the subsequent test/lint/schema targets would not find Python.
# `uv sync` creates the in-project .venv and installs runtime deps plus the
# `dev` dependency group (ruff at the CI-pinned version). The targets below use
# `uv run`, which syncs on demand, so this is only needed to pre-warm the venv.
install-dev:
poetry install --with dev
uv sync

# =============================================================================
# CI-equivalent targets. These mirror .github/workflows/*.yml exactly so a
# clean run locally means the GitHub run will also pass.
# clean run locally means the GitHub run will also pass. `uv run` resolves the
# project venv on every platform, so no OS-specific interpreter path is needed.
# =============================================================================

# Mirrors .github/workflows/test.yml — discovers and runs every unittest
# under tests/. The package itself imports cleanly on any OS (only the
# inner functions shell out to OS-specific tools), so the same command
# passes on the Linux/macOS/Windows runners CI matrixes over.
test:
$(PY) -m unittest discover -v tests
uv run python -m unittest discover -v tests

# Mirrors .github/workflows/lint.yml — ruff check + ruff format --check.
lint:
$(PY) -m ruff check bootstrap/
$(PY) -m ruff format --check bootstrap/
uv run ruff check bootstrap/
uv run ruff format --check bootstrap/

# Mirrors .github/workflows/schema.yml — loads sample_config.json and runs
# it through bootstrap.schema.config_validate. Catches regressions where a
# schema change makes the documented sample config invalid.
schema:
$(PY) -c "import json; from bootstrap.schema import config_validate; config_validate(json.load(open('sample_config.json'))); print('sample_config.json is valid')"
uv run python -c "import json; from bootstrap.schema import config_validate; config_validate(json.load(open('sample_config.json'))); print('sample_config.json is valid')"

# Full pre-push verification: everything CI runs, in one shot.
ci: lint test schema
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ There are probably many. However, the well known ones; Puppet, Chef, Ansible, et
There are a lot of "dotfiles" repos on github; this depends on that --- it requires you to have a `~/dotfiles/` repo that can be symlinked against, but it goes a lot further (than dotfiles).

# Prerequisites
1. python3 (3.10+ required; I am using 3.11) and `poetry`
1. python3 (3.10+ required; I am using 3.11) and `uv`
2. if on mac, `homebrew`; if on Arch, `yay`; if on Windows, `winget` (ships with Windows 11) and/or `scoop`
3. If on mac, XCode command line tools (can't even build `gcc` from `homebrew` without this): `sudo xcode-select --install`
4. If on Windows, enable Developer Mode (Settings → System → For developers → Developer Mode). This lets `os.symlink` work without admin.
Expand Down Expand Up @@ -46,7 +46,7 @@ Supported package managers:

Note: For Ubuntu, `ppa` entries are automatically processed before `apt` regardless of order in the config. Similarly on Windows, `scoop_bucket` is processed before `scoop` so buckets are available when packages install.

6. `prereq_packages`: packages that provide language toolchains (rust/cargo, go, poetry) needed before other packages can be installed. These are installed before `packages`. Structure is the same as `packages` but keyed by OS only (no `all` section).
6. `prereq_packages`: packages that provide language toolchains (rust/cargo, go, uv) needed before other packages can be installed. These are installed before `packages`. Structure is the same as `packages` but keyed by OS only (no `all` section).
7. `file_associations` (Windows-only): per-user Windows file extension → app launcher. Each entry has required `ext` (`.yaml`), `progid` (`Neovim.YAML`), and `cmd` (`wt -- nvim "%1"`), plus optional `name` (friendly app name shown in the "Open with" picker), `icon` (Explorer icon — a `.ico` path or `<path>,<index>` to pull a PE resource), and `label` (human-readable name for logs). Writes `HKCU\Software\Classes` so no admin is needed; it also clears `UserChoice` (only when it points at a different app) so Explorer double-click respects your mapping. Runs after `packages` so the target apps exist.
8. `post_commands`: same shape as `commands`, but runs *after* packages and `file_associations`. Use this for steps that depend on packages already being installed — e.g. registering a font file that scoop installed but didn't put in the user font registry.

Expand All @@ -61,9 +61,9 @@ The extra config files are optional and use the same schema. They're processed a

# Install and Running

poetry install
poetry run runboot --loctype work # work machines
poetry run runboot --loctype private # personal machines
uv sync
uv run runboot --loctype work # work machines
uv run runboot --loctype private # personal machines

The systype (mac, arch, ubuntu, windows) is detected from `sys.platform` and `/etc/os-release`. The script is idempotent — safe to run repeatedly. Add packages to `bootstrap_config.json` and re-run.

Expand Down
2 changes: 1 addition & 1 deletion bootstrap/boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def boot_config(cfg: dict, systype, loctype, run_prereqs=False):
4. os-specific softlinks (links.mac/arch/ubuntu/windows)
5. generic commands
6. system specific commands
7. prereq packages (rust/cargo, go, poetry) - only on first config
7. prereq packages (rust/cargo, go, uv) - only on first config
8. system specific packages (installs npm, go, etc via pacman/brew/apt/winget)
9. generic packages (uses pip, npm, go_install, cargo, fisher)
10. file associations (windows-only, runs after packages so target apps exist)
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"additionalProperties": False,
},
"prereq_packages": {
"description": "packages that provide language toolchains (rust/cargo, go, poetry) needed before other packages can be installed. These are installed before `packages`.",
"description": "packages that provide language toolchains (rust/cargo, go, uv) needed before other packages can be installed. These are installed before `packages`.",
"type": "object",
"properties": {
"mac": {"$ref": "#/definitions/package_section"},
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def _install_packages(inner, label):


def prereq_packages(config, systype):
"""install prerequisite packages (rust/cargo, go, poetry) for a given systype"""
"""install prerequisite packages (rust/cargo, go, uv) for a given systype"""
if "prereq_packages" not in config or systype not in config["prereq_packages"]:
log.skip(f"No prereq_packages defined for systype {systype}")
return
Expand Down
Loading
Loading