diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..abc477d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +# Changelog + +All notable changes to Homebase will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, release 0, alpha stage). + +## [Unreleased] + +## [26.0-alpha] - 2026-04-13 + +First tagged snapshot. Pre-alpha — the installer does not yet boot, but the design is locked and the prototype components are shaped. + +### Added + +- **Installer webapp prototype** (`webinstaller/`) — Flask app with 3-step wizard (hostname → drive → overview), serves drive list via `drives.py::list_scored_devices()`. +- **Drive scoring module** (`webinstaller/drives.py`) — scores attached disks by type (NVMe/SSD/HDD), SMART health (`smartctl -H`), and size. Consumed by the installer and usable as a CLI. +- **Base archinstall configuration** (`archinstall/user_configuration.json`) — systemd-boot, ext4, Docker + Compose preinstalled, server profile, SSH. Credentials template at `archinstall/user_credentials.example.json` (real credentials gitignored). +- **Installer wireframes** (`docs/installer-wireframes.md`) — Robert's hand-drawn 4-screen UX sketches. +- **Competitor analysis** (`docs/competitors.md`) — CasaOS, Umbrel, YunoHost reviewed across install flow, hardware detection, app store UX, reverse proxy/SSL, user complaints. Key finding: device-aware wizard + managed gateway are uncontested differentiators. +- **Wizard flow spec** (`docs/wizard-flow.md`) — 8-screen first-boot flow extending Robert's wireframe with YunoHost-style domain/SSL/diagnostic/confirm screens. Locked tech picks with rejected alternatives. +- **Project README** — vision, principles, architecture, key decisions, landscape, roadmap. + +### Decisions locked + +- **Reverse proxy:** Caddy (auto Let's Encrypt, simplest config) +- **Identity provider:** Authentik (bundled SSO, every app auto-wired at install) +- **Managed gateway DNS:** NS delegation to `ns1.homebase.cloud` / `ns2.homebase.cloud` (wildcard cert via Let's Encrypt DNS-01) +- **Local HTTPS:** Local CA installed by user once (no browser warnings on `*.proksi.local`) +- **Base OS:** Arch (rolling, Debian remains fallback) +- **Containers:** Docker + Compose +- **License:** AGPL-3.0 + +[Unreleased]: https://forgejo.sourcegate.online/daniel/homebase/compare/26.0-alpha...HEAD +[26.0-alpha]: https://forgejo.sourcegate.online/daniel/homebase/releases/tag/26.0-alpha diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..174815e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# Contributing to Homebase + +Two-person project right now (Daniel + Robert). This doc exists so the conventions don't get lost as the team grows. + +## Dev setup + +```bash +git clone https://forgejo.sourcegate.online/daniel/homebase.git +cd homebase + +python -m venv .venv +source .venv/bin/activate + +# Flask installer +pip install -r webinstaller/requirements.txt +python webinstaller/app.py # serves on http://localhost:5000 + +# Dev tooling (lint + tests) +pip install -e ".[dev]" +ruff check . +pytest +``` + +Required system tools (used by `webinstaller/drives.py`): + +- `lsblk` (util-linux) +- `smartctl` (smartmontools) — `sudo pacman -S smartmontools` / `sudo apt install smartmontools` + +## Commit messages + +We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). Every commit subject line starts with a type: + +| Type | When | +|------|------| +| `feat:` | New user-visible feature | +| `fix:` | Bug fix | +| `docs:` | Documentation only | +| `ci:` | CI config, Forgejo Actions, runner setup | +| `build:` | Packaging, `pyproject.toml`, dependency changes | +| `refactor:` | Code restructuring with no behavior change | +| `test:` | Adding or fixing tests | +| `chore:` | Everything else (e.g. `.gitignore`, version bumps) | + +Examples: + +``` +feat: add S5 domain picker to installer wizard +fix: handle empty lsblk output in drives.parse_size_gb +docs: document NS delegation flow for managed gateway +ci: add ruff format check to Forgejo Actions workflow +``` + +Keep the subject line under 72 characters. Use the body to explain *why* — the code already says *what*. + +## Code style + +- **Python:** ruff handles lint + format. Config in `pyproject.toml`. Run `ruff check . && ruff format .` before committing. +- **Markdown:** wrap at a sensible width (no hard line-length rule). Every external link you add to a doc must resolve (the markdown-link-check CI job will catch it if it doesn't). +- **No comments that explain *what*** — the code says that. Only comment the *why* when it's non-obvious. + +## PR / push workflow + +While the team is two people, pushing to `main` is fine for small changes. For anything larger than ~50 lines or that touches architecture, open a PR and get the other person to look at it. + +## Tests + +Tests live in `tests/`. Pure functions get unit tests; anything that shells out to `lsblk` / `smartctl` is out of scope (those need hands-on testing on real hardware). + +```bash +pytest # all tests +pytest tests/test_drives.py -v +``` + +## Releases + +See [RELEASING.md](RELEASING.md). + +## License + +AGPL-3.0 (see `LICENSE`). By contributing you agree your contributions are licensed the same way. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..7fb884a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,73 @@ +# Releasing + +Homebase uses calendar versioning: **`YY.N-stage`** — e.g. `26.0-alpha` is 2026, release 0, alpha stage. No `v` prefix. + +- `YY` — last two digits of the current year +- `N` — incrementing release number within the year, starting at 0 (next one in 2026 is `26.1-alpha`, then `26.2-alpha`…) +- `-alpha` — drop it when the installer boots end-to-end and wipe-and-reinstall is safe + +When the year rolls over, the next release becomes `27.0-alpha` regardless of how many `26.x` releases shipped. + +## Cadence + +Tag per meaningful milestone, not on a calendar. A milestone is: ISO boots, a wizard screen works end-to-end, managed gateway serves its first real domain, etc. If a week goes by with no tag, that's fine — no tag is better than a noisy one. + +## Release steps + +1. **Move `[Unreleased]` in `CHANGELOG.md` to a new version heading.** + ```markdown + ## [Unreleased] + + ## [26.1-alpha] - 2026-05-20 + ### Added + - ... + ``` + Add a `[26.1-alpha]` link definition at the bottom: + ```markdown + [26.1-alpha]: https://forgejo.sourcegate.online/daniel/homebase/releases/tag/26.1-alpha + ``` + Update the `[Unreleased]` compare link to point at the new tag. + +2. **Commit the changelog.** + ```bash + git add CHANGELOG.md + git commit -m "chore: release 26.1-alpha" + ``` + +3. **Tag the commit.** + ```bash + git tag -a 26.1-alpha -m "Release 26.1-alpha" + ``` + +4. **Push the tag and main.** + ```bash + git push origin main + git push origin 26.1-alpha + ``` + +5. **Create a Forgejo Release** at `https://forgejo.sourcegate.online/daniel/homebase/releases/new`: + - Tag: `26.1-alpha` (already exists) + - Title: `26.1-alpha` + - Body: paste the changelog section for this version + - Tick **Pre-release** for anything still `-alpha` or `-beta` + +6. **Verify CI passed on the tag.** The Forgejo Actions run against the tagged commit should be green before you announce the release anywhere. + +## First-time: find the current version + +```bash +git describe --tags --abbrev=0 +``` + +If the project is fresh and `git describe` fails, the next release is `26.0-alpha`. + +## If the tag is wrong + +Don't move published tags. Delete the release + tag, fix the problem, bump to the next number. + +```bash +git tag -d 26.1-alpha +git push origin :refs/tags/26.1-alpha +# ... fix ... +git tag -a 26.2-alpha ... +``` diff --git a/docs/runner-setup.md b/docs/runner-setup.md new file mode 100644 index 0000000..f93a45b --- /dev/null +++ b/docs/runner-setup.md @@ -0,0 +1,113 @@ +# Forgejo Runner Setup + +How to stand up a `forgejo-runner` so the CI workflow in `.forgejo/workflows/ci.yml` actually executes on every push. + +The runner is a long-running daemon that polls the Forgejo instance for queued jobs and runs them in Docker containers. + +## Choosing a host + +| Option | Good for | Trade-off | +|--------|----------|-----------| +| **Dedicated VPS** | Production-ish CI that runs even when you're offline | Costs a few €/month; one more machine to maintain | +| **Home server / NAS** | Free; plenty of capacity | CI blocked if home network / power drops | +| **Local dev machine** | Quick to set up, fast runs | CI only works while the machine is on | + +Recommendation for now: **home server or a cheap VPS**. Don't use a laptop that suspends. + +## Install + +Pick either the binary or the Docker container path. Docker is easier to upgrade. + +### Path A: Docker Compose (recommended) + +On the chosen host, create `~/forgejo-runner/compose.yml`: + +```yaml +services: + runner: + image: code.forgejo.org/forgejo/runner:6 + container_name: forgejo-runner + restart: unless-stopped + environment: + - CONFIG_FILE=/data/config.yml + volumes: + - ./data:/data + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - docker-in-docker + command: /bin/sh -c "sleep 5; forgejo-runner daemon" + + docker-in-docker: + image: docker:dind + container_name: forgejo-runner-dind + restart: unless-stopped + privileged: true + environment: + - DOCKER_TLS_CERTDIR= + command: dockerd -H tcp://0.0.0.0:2375 --tls=false +``` + +### Path B: Binary + +Download the latest release from https://code.forgejo.org/forgejo/runner/releases and drop it somewhere in `$PATH`: + +```bash +wget https://code.forgejo.org/forgejo/runner/releases/download/v6.0.0/forgejo-runner-6.0.0-linux-amd64 +chmod +x forgejo-runner-6.0.0-linux-amd64 +sudo mv forgejo-runner-6.0.0-linux-amd64 /usr/local/bin/forgejo-runner +``` + +## Register + +1. In the Forgejo web UI: go to **Site Administration → Actions → Runners → Create new Runner**. Copy the registration token. (For a repo-scoped runner instead, use **Repo Settings → Actions → Runners**.) + +2. Register from the runner host: + + ```bash + forgejo-runner register \ + --instance https://forgejo.sourcegate.online \ + --token \ + --name homebase-runner-1 \ + --labels docker,ubuntu-latest,self-hosted \ + --no-interactive + ``` + + This writes `.runner` (registration file) and `config.yml` into the current directory. + +3. Start the daemon (Docker: `docker compose up -d`; binary: `forgejo-runner daemon`). + +4. Verify the runner shows up as **Idle** in Forgejo's admin Runners page. + +## First CI run + +Push any commit; the Actions tab on the repo should show the workflow running. If nothing happens: + +- Confirm the runner is online (Forgejo admin → Actions → Runners). +- Check the workflow has labels that match the runner (`runs-on: ubuntu-latest` needs a runner registered with that label). +- Check the runner logs: `docker logs forgejo-runner` or the systemd journal. + +## Systemd unit (for the binary path) + +```ini +[Unit] +Description=Forgejo Actions Runner +After=docker.service +Requires=docker.service + +[Service] +ExecStart=/usr/local/bin/forgejo-runner daemon +WorkingDirectory=/var/lib/forgejo-runner +User=forgejo-runner +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Save as `/etc/systemd/system/forgejo-runner.service`, then `sudo systemctl enable --now forgejo-runner`. + +## Security notes + +- The runner mounts `/var/run/docker.sock` which gives it root-equivalent access to the host. Run it on a machine you trust and nothing sensitive. +- Registration tokens are one-shot; a stolen token can't re-register after the runner is live. +- Prefer repo-scoped runners over instance-wide if you're sharing the runner with other repos you don't control.