docs: update README for new architecture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
243
README.md
243
README.md
@@ -1,15 +1,6 @@
|
||||
# flow
|
||||
|
||||
`flow` is a CLI for managing development instances, containers, dotfiles, and host bootstrap.
|
||||
|
||||
## What is implemented
|
||||
|
||||
- Instance access via `flow enter`
|
||||
- Container lifecycle under `flow dev`
|
||||
- Dotfiles repo management (`flow dotfiles`)
|
||||
- Bootstrap provisioning (`flow bootstrap`)
|
||||
- Package installs from unified manifest definitions (`flow package`)
|
||||
- Project sync checks (`flow sync`)
|
||||
`flow` is a CLI for managing development environments: dotfiles, packages, containers, remote targets, and system bootstrap.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -20,88 +11,111 @@ make install-local
|
||||
|
||||
This installs `flow` to `~/.local/bin/flow`.
|
||||
|
||||
## Core behavior
|
||||
## Architecture
|
||||
|
||||
### Security model
|
||||
Four-layer design: **core** (runtime, config, errors) -> **domain** (pure functions, frozen dataclasses) -> **services** (I/O orchestration) -> **commands** (thin CLI adapters).
|
||||
|
||||
- `flow` must run as a regular user (root/sudo invocation is rejected).
|
||||
- At startup, `flow` refreshes sudo credentials once (`sudo -v`) for privileged steps.
|
||||
- Package `post-install` hooks run without sudo by default.
|
||||
- A package hook can use sudo only when `allow_sudo: true` is explicitly set.
|
||||
- Domain layer is pure: no I/O, no side effects, fully testable
|
||||
- Services perform all I/O and delegate logic to domain functions
|
||||
- Commands are trivial dispatchers from argparse to services
|
||||
|
||||
### Config location and merge rules
|
||||
## Commands
|
||||
|
||||
`flow` loads all YAML files from:
|
||||
```bash
|
||||
# Dotfiles
|
||||
flow dotfiles link [--profile NAME] [--dry-run] [--skip PKG...]
|
||||
flow dotfiles unlink [PACKAGES...] [--dry-run]
|
||||
flow dotfiles status
|
||||
flow dotfiles sync
|
||||
|
||||
1. `~/.local/share/flow/dotfiles/_shared/flow/.config/flow/` (self-hosted, if present)
|
||||
2. `~/.config/flow/` (local fallback)
|
||||
# Packages
|
||||
flow packages install [NAMES...] [--profile NAME] [--dry-run]
|
||||
flow packages remove NAMES... [--dry-run]
|
||||
flow packages list
|
||||
|
||||
Files are read alphabetically (`*.yaml` and `*.yml`) and merged at top level.
|
||||
If the same top-level key appears in multiple files, the later filename wins.
|
||||
# Bootstrap
|
||||
flow setup run PROFILE [--dry-run]
|
||||
flow setup show PROFILE
|
||||
flow setup list
|
||||
|
||||
`repository.pull-before-edit` controls whether `flow dotfiles edit` runs `git pull --rebase` first (default: `true`).
|
||||
When pull brings new changes, flow shows an info message and waits for Enter before opening the editor.
|
||||
# Remote targets
|
||||
flow remote enter NAMESPACE@PLATFORM [--dry-run]
|
||||
flow remote list
|
||||
|
||||
### Dotfiles layout (flat with reserved dirs)
|
||||
# Dev containers
|
||||
flow dev create IMAGE [--namespace NS] [--dry-run]
|
||||
flow dev enter NAME [--shell PATH]
|
||||
flow dev stop NAME
|
||||
flow dev remove NAME
|
||||
flow dev list
|
||||
|
||||
Inside your dotfiles repo root:
|
||||
# Projects
|
||||
flow projects check [--fetch]
|
||||
|
||||
```text
|
||||
_shared/
|
||||
flow/
|
||||
.config/flow/
|
||||
config.yaml
|
||||
packages.yaml
|
||||
profiles.yaml
|
||||
dnsmasq/
|
||||
.user_hosts
|
||||
_root/
|
||||
opt/homebrew/etc/dnsmasq.conf
|
||||
git/
|
||||
.gitconfig
|
||||
linux-auto/
|
||||
nvim/
|
||||
.config/nvim/init.lua
|
||||
# Other
|
||||
flow --version
|
||||
flow --quiet
|
||||
flow completion
|
||||
```
|
||||
|
||||
- `_shared/`: linked for all profiles
|
||||
- `_root/` is a marker inside a package for absolute paths (via sudo), e.g. `dnsmasq/_root/etc/hostname -> /etc/hostname`
|
||||
- every other directory at this level is a profile name
|
||||
- any target conflict fails (including `_shared` vs profile)
|
||||
## Configuration
|
||||
|
||||
### External module packages
|
||||
Config is loaded from `~/.config/flow/config.yaml` and merged with self-hosted config from the dotfiles repo at `~/.local/share/flow/dotfiles/_shared/flow/.config/flow/`.
|
||||
|
||||
Any directory inside a package can be backed by an external git repository using `_module.yaml`:
|
||||
```yaml
|
||||
repository:
|
||||
url: git@github.com:you/dotfiles.git
|
||||
branch: main
|
||||
|
||||
```text
|
||||
_shared/
|
||||
paths:
|
||||
projects: ~/projects
|
||||
|
||||
defaults:
|
||||
container-registry: registry.example.com
|
||||
tmux-session: main
|
||||
|
||||
targets:
|
||||
personal@orb: personal.orb
|
||||
work@ec2:
|
||||
host: work.ec2.internal
|
||||
identity: ~/.ssh/id_work
|
||||
```
|
||||
|
||||
## Dotfiles layout
|
||||
|
||||
```
|
||||
_shared/ # Linked for all profiles
|
||||
zsh/
|
||||
.zshrc
|
||||
nvim/
|
||||
.config/nvim/
|
||||
_module.yaml
|
||||
_module.yaml # External git module
|
||||
.local/bin/
|
||||
nvim-wrapper
|
||||
dns/
|
||||
_root/ # Absolute path marker (needs sudo)
|
||||
etc/hosts
|
||||
|
||||
linux-work/ # Profile-specific layer
|
||||
i3/
|
||||
.config/i3/config
|
||||
```
|
||||
|
||||
### External modules
|
||||
|
||||
A `_module.yaml` inside a package mounts an external git repo at that location:
|
||||
|
||||
```yaml
|
||||
source: github:org/nvim-config
|
||||
ref:
|
||||
branch: main
|
||||
```
|
||||
|
||||
- Flow mounts the module repo root at the directory containing `_module.yaml` (e.g. the example mounts into `~/.config/nvim/`).
|
||||
- Local files under that directory are ignored (shown only in `--verbose`).
|
||||
- Only one `_module.yaml` per package is supported.
|
||||
- Modules are refreshed on `flow dotfiles init` and `flow dotfiles sync` (not on `link`).
|
||||
Module files are linked from the clone cache, while local files outside the mount path are linked from the dotfiles repo.
|
||||
|
||||
## Manifest model
|
||||
## Manifest
|
||||
|
||||
Top-level keys:
|
||||
|
||||
- `profiles`
|
||||
- `packages`
|
||||
- optional global settings like `repository`, `paths`, `defaults`, `targets`
|
||||
|
||||
`environments` is not supported.
|
||||
|
||||
### Packages (unified)
|
||||
Packages and profiles are defined in YAML files under the flow config directory.
|
||||
|
||||
```yaml
|
||||
packages:
|
||||
@@ -109,103 +123,40 @@ packages:
|
||||
type: pkg
|
||||
sources:
|
||||
apt: fd-find
|
||||
dnf: fd-find
|
||||
brew: fd
|
||||
|
||||
- name: wezterm
|
||||
type: cask
|
||||
sources:
|
||||
brew: wezterm
|
||||
|
||||
- name: neovim
|
||||
type: binary
|
||||
source: github:neovim/neovim
|
||||
version: "0.10.4"
|
||||
asset-pattern: "nvim-{{os}}-{{arch}}.tar.gz"
|
||||
platform-map:
|
||||
linux-x64: { os: linux, arch: x64 }
|
||||
linux-arm64: { os: linux, arch: arm64 }
|
||||
darwin-arm64: { os: macos, arch: arm64 }
|
||||
extract-dir: "nvim-{{os}}64"
|
||||
install:
|
||||
bin: [bin/nvim]
|
||||
share: [share/nvim]
|
||||
man: [share/man/man1/nvim.1]
|
||||
lib: [lib/libnvim.so]
|
||||
```
|
||||
linux-x64: nvim-linux-x86_64.tar.gz
|
||||
|
||||
### Profile package syntaxes
|
||||
|
||||
All are supported in one profile list:
|
||||
|
||||
```yaml
|
||||
profiles:
|
||||
macos-dev:
|
||||
os: macos
|
||||
linux-work:
|
||||
os: linux
|
||||
hostname: dev-box
|
||||
locale: en_US.UTF-8
|
||||
shell: zsh
|
||||
packages:
|
||||
- git
|
||||
- cask/wezterm
|
||||
- fd
|
||||
- binary/neovim
|
||||
- name: docker
|
||||
allow_sudo: true
|
||||
post-install: |
|
||||
sudo groupadd docker || true
|
||||
sudo usermod -aG docker $USER
|
||||
ssh-keys:
|
||||
- path: ~/.ssh/id_ed25519
|
||||
type: ed25519
|
||||
runcmd:
|
||||
- echo "setup complete"
|
||||
```
|
||||
|
||||
### Templates
|
||||
## Security
|
||||
|
||||
- `{{ env.VAR_NAME }}`
|
||||
- `{{ version }}`
|
||||
- `{{ os }}`
|
||||
- `{{ arch }}`
|
||||
|
||||
### Bootstrap profile features
|
||||
|
||||
- `os` is required (`linux` or `macos`)
|
||||
- `package-manager` optional (auto-detected if omitted)
|
||||
- default locale is `en_US.UTF-8`
|
||||
- shell auto-install + `chsh` when `shell:` is declared and missing
|
||||
- `requires` validation for required env vars
|
||||
- `ssh-keygen` definitions
|
||||
- `runcmd` (runs after package installation)
|
||||
- automatic config linking (`_shared` + profile, including package-local `_root` markers)
|
||||
- `post-link` hook (runs after symlink phase)
|
||||
- config skip patterns:
|
||||
- package names (e.g. `nvim`)
|
||||
- `_shared`
|
||||
- `_profile`
|
||||
- `_root`
|
||||
|
||||
## Command overview
|
||||
|
||||
```bash
|
||||
flow enter personal@orb
|
||||
flow dev create api -i tm0/node -p ~/projects/api
|
||||
|
||||
flow dotfiles init --repo git@github.com:you/dotfiles.git
|
||||
flow dotfiles link --profile linux-auto
|
||||
flow dotfiles undo
|
||||
flow dotfiles status
|
||||
flow dotfiles modules list
|
||||
flow dotfiles modules sync
|
||||
|
||||
flow bootstrap list
|
||||
flow bootstrap show linux-auto
|
||||
flow bootstrap run --profile linux-auto --var USER_EMAIL=you@example.com
|
||||
|
||||
flow package install neovim
|
||||
flow package list --all
|
||||
|
||||
flow sync check
|
||||
flow completion install-zsh
|
||||
```
|
||||
- `flow` must run as a regular user (root invocation is rejected)
|
||||
- `_root/` files require sudo for linking
|
||||
- Package post-install hooks run without sudo by default
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -e ".[dev]"
|
||||
python3 -m pytest
|
||||
FLOW_RUN_E2E_CONTAINER=1 .venv/bin/pytest -q tests/test_dotfiles_e2e_container.py
|
||||
make deps
|
||||
.venv/bin/python -m pytest tests/ -v
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user