diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
new file mode 100644
index 0000000..a88b5d0
--- /dev/null
+++ b/.gitea/workflows/build.yml
@@ -0,0 +1,27 @@
+# Builds the Das OS packages and both ISO images on the CI host (see ci/runner).
+# Build output stays on the host in DASOS_OUT, set in the runner's config.
+name: build
+
+on:
+ push:
+ branches: [main, dev]
+ tags: ["v*"]
+ workflow_dispatch:
+
+concurrency:
+ group: build-${{ gitea.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ runs-on: dasci
+ steps:
+ - uses: builtin:checkout
+
+ # One make run builds the packages once for both images.
+ # Go tests run inside the das-tools and das-theme package builds.
+ - name: Packages and ISO images
+ run: make iso iso-server
+
+ - name: Result
+ run: ls -lh "$DASOS_OUT/iso/"*-"$(cat VERSION)"-x86_64.iso
diff --git a/README.md b/README.md
index 9829315..0f835fc 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
## Das OS
### _A light, friendly Void Linux–based desktop with Xfce and a Ukrainian touch, plus a console server edition_
-[](https://voidlinux.org) [](https://xfce.org) [](https://docs.voidlinux.org/config/services/) [](https://go.dev) [](https://www.docker.com) [](#)
+[](https://voidlinux.org) [](https://xfce.org) [](https://docs.voidlinux.org/config/services/) [](https://go.dev) [](https://www.docker.com) [](#)
🇺🇦 [Українська версія](README_ua.md)
@@ -21,6 +21,7 @@ Void Linux is fast, simple and independent: runit instead of systemd, and its ow
- [Desktop defaults](#Defaults) for Xfce that don't overwrite stock files
- [Own package repository](#Repository) built by xbps-src from in-tree templates
- [Containerised build](#Build) that runs on any Linux host with Docker, and QEMU scripts for testing
+- [CI build](#CI) of the packages and both ISOs on every push, on a self-hosted Gitea Actions runner
### ⭐ Advantages
- **Void underneath** – a fast runit boot, the xbps package manager and fresh software, with none of the manual setup.
@@ -186,6 +187,14 @@ Void Linux is fast, simple and independent: runit instead of systemd, and its ow
2. `make iso` builds the packages and then the ISO with `void-mklive`, using the profile in `live/` (package list, services, root file system overlay). `make iso-server` does the same for the server profile in `server/`.
3. `make qemu` boots the newest ISO with UEFI. `make qemu-bios` uses legacy BIOS, and `make qemu-disk` also attaches a 20 GB virtual disk to install on. `make qemu-server` boots the server ISO with the disk on a serial console in the terminal (Ctrl-A X quits).
+#### CI build
+**_Every push to `main` or `dev` and every `v*` tag builds the packages and both ISOs on git.das.org.ua._**
+- The workflow `.gitea/workflows/build.yml` runs `make iso iso-server` on a self-hosted runner with the `dasci` label.
+- The runner is a WSL2 distro on a Windows machine: Ubuntu 24.04 with Docker Engine and `gitea-runner` as a systemd service. A scheduled task starts it when Windows boots, no login needed.
+- `ci/runner/setup.sh` installs Docker, the runner and its `config.yaml`; `setup.sh register ` registers it, `autostart.ps1` creates the scheduled task.
+- `DASOS_OUT` moves `out/` out of the checkout, so void-packages, the bootstrapped masterdir and the download cache survive between builds.
+- The ISOs stay on the CI host in `/var/lib/dasos-ci/out/iso` and are not published yet.
+
### 🚀 Building the application
Requirements: Linux with Docker (the user must be in the `docker` group), QEMU and OVMF for testing, and Go 1.24+ with the GTK 3 development files for `make test`. About 10 GB of free disk space.
```shell
@@ -198,6 +207,6 @@ make clean # remove build output, keep the download cache
```
The ISOs appear in `out/iso/dasos--x86_64.iso` and `out/iso/dasos-server--x86_64.iso`. Write them to a USB stick with `dd` or Ventoy.
-**Note:** Das OS is an early version (0.6.0). Signed repository hosting and a CI build are planned for upcoming stages. Until the repository is hosted, an installed system gets updates from Void only. Das OS is based on Void Linux but is not affiliated with it.
+**Note:** Das OS is an early version (0.7.0). Packages and ISOs are built in CI; signed repository hosting is planned for an upcoming stage. Until the repository is hosted, an installed system gets updates from Void only. Das OS is based on Void Linux but is not affiliated with it.
###### _Made by -=:dAs:=-_
diff --git a/README_ua.md b/README_ua.md
index 3e9f53b..089b36c 100644
--- a/README_ua.md
+++ b/README_ua.md
@@ -1,7 +1,7 @@
## Das OS
### _Легкий і дружній десктоп на базі Void Linux з Xfce та українським колоритом, а також консольна серверна версія_
-[](https://voidlinux.org) [](https://xfce.org) [](https://docs.voidlinux.org/config/services/) [](https://go.dev) [](https://www.docker.com) [](#)
+[](https://voidlinux.org) [](https://xfce.org) [](https://docs.voidlinux.org/config/services/) [](https://go.dev) [](https://www.docker.com) [](#)
🇬🇧 [English version](README.md)
@@ -21,6 +21,7 @@ Void Linux швидкий, простий і незалежний: runit зам
- [Налаштування за замовчуванням](#Defaults) для Xfce, які не перезаписують стандартні файли
- [Власний репозиторій пакетів](#Repository), який xbps-src збирає з шаблонів у дереві проєкту
- [Збирання в контейнері](#Build), яке працює на будь-якому Linux із Docker, і скрипти QEMU для тестування
+- [Збирання в CI](#CI) пакетів і обох ISO після кожного push на власному runner Gitea Actions
### ⭐ Переваги
- **Void усередині** – швидке завантаження з runit, менеджер пакетів xbps і свіжі програми без ручного налаштування.
@@ -186,6 +187,14 @@ Void Linux швидкий, простий і незалежний: runit зам
2. `make iso` збирає пакети, а потім ISO через `void-mklive` за профілем у `live/` (список пакетів, сервіси, оверлей кореневої файлової системи). `make iso-server` робить те саме для серверного профілю в `server/`.
3. `make qemu` завантажує найновіший ISO з UEFI. `make qemu-bios` використовує старий BIOS, а `make qemu-disk` ще й підключає віртуальний диск на 20 ГБ для встановлення. `make qemu-server` завантажує серверний ISO з диском на послідовній консолі прямо в терміналі (вихід — Ctrl-A X).
+#### Збирання в CI
+**_Кожен push у `main` чи `dev` і кожен тег `v*` збирає пакети й обидва ISO на git.das.org.ua._**
+- Workflow `.gitea/workflows/build.yml` запускає `make iso iso-server` на власному runner з міткою `dasci`.
+- Runner — дистрибутив WSL2 на Windows-машині: Ubuntu 24.04 з Docker Engine і `gitea-runner` як службою systemd. Завдання планувальника запускає його під час завантаження Windows, вхід у систему не потрібен.
+- `ci/runner/setup.sh` встановлює Docker, runner і його `config.yaml`; `setup.sh register <токен>` реєструє його, `autostart.ps1` створює завдання планувальника.
+- `DASOS_OUT` виносить `out/` за межі робочої копії, тож void-packages, підготовлений masterdir і кеш завантажень зберігаються між збираннями.
+- ISO лишаються на CI-хості в `/var/lib/dasos-ci/out/iso` і поки не публікуються.
+
### 🚀 Збирання застосунку
Вимоги: Linux із Docker (користувач має бути в групі `docker`), QEMU та OVMF для тестування, Go 1.24+ і файли розробки GTK 3 для `make test`. Приблизно 10 ГБ вільного місця.
```shell
@@ -198,6 +207,6 @@ make clean # видалити результати збиранн
```
ISO з'являються в `out/iso/dasos-<версія>-x86_64.iso` і `out/iso/dasos-server-<версія>-x86_64.iso`. Записати їх на флешку можна через `dd` або Ventoy.
-**Примітка:** Das OS — рання версія (0.6.0). Підписаний репозиторій на хостингу та збирання в CI заплановані на наступні етапи. Поки репозиторій не розміщено на хостингу, встановлена система отримує оновлення лише з Void. Das OS базується на Void Linux, але не пов'язана з ним офіційно.
+**Примітка:** Das OS — рання версія (0.7.0). Пакети й ISO збираються в CI; підписаний репозиторій на хостингу заплановано на наступний етап. Поки репозиторій не розміщено на хостингу, встановлена система отримує оновлення лише з Void. Das OS базується на Void Linux, але не пов'язана з ним офіційно.
###### _Made by -=:dAs:=-_
diff --git a/TODO.md b/TODO.md
index 7e9661e..339732c 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,6 +1,6 @@
# Das OS — що зробити далі
-Стан на 2026-10-20: версія 0.6.0, етапи 1–8 готові (live ISO, власний репозиторій, оформлення, інсталятор Calamares зі знімками Btrfs, Go-утиліти `das-update` і `das-welcome`, серверний образ з консольним інсталятором, менші ISO, консольні помічники, профіль розробника). Далі: CI (етап 9).
+Стан на 2026-10-20: версія 0.7.0, етапи 1–8 готові (live ISO, власний репозиторій, оформлення, інсталятор Calamares зі знімками Btrfs, Go-утиліти `das-update` і `das-welcome`, серверний образ з консольним інсталятором, менші ISO, консольні помічники, профіль розробника), етап 9 (CI) налаштовано, чекає першого запуску в Gitea.
## Етап 5. Go-утиліти (готово, лишилися перевірки)
- [x] `das-update`: вікно, значок у треї (лише коли є оновлення), консольний режим `--cli`, знімок snapper pre/post, встановлення через pkexec, підказка про відкат.
@@ -61,7 +61,12 @@ Live ISO без графіки з консольним інсталятором:
- [x] Повідомлення наприкінці `das-server-install` згадує `dprofile install dev`.
## Етап 9. CI
-- [ ] Збирання пакетів і обох ISO (десктоп і сервер) в контейнері Void (Gitea Actions на git.das.org.ua або GitHub Actions).
+- [x] Workflow `.gitea/workflows/build.yml`: `make iso iso-server` на власному runner `dasci:host` (push у `main`/`dev`, теги `v*`, ручний запуск).
+- [x] CI-хост: Windows-машина (192.168.88.193), WSL2 Ubuntu 24.04 `dasci` у `D:\DasOS\wsl`, Docker Engine, `gitea-runner` 5.1.1 як служба systemd, автозапуск завданням планувальника (`ci/runner`).
+- [x] `DASOS_OUT` у `scripts/builder.sh`: кеш `out/` на CI-хості в `/var/lib/dasos-ci/out`.
+- [ ] Зареєструвати runner і перевірити перший запуск у Gitea.
+- [ ] Перевірити, що після перезавантаження Windows runner піднімається сам (завдання S4U).
+- [ ] Вирішити, куди публікувати ISO (Release на тег `v*` чи artifacts).
## Репозиторій пакетів
- [ ] Підпис через `xbps-rindex --sign`, ключ поза репозиторієм.
diff --git a/VERSION b/VERSION
index a918a2a..faef31a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.0
+0.7.0
diff --git a/ci/runner/autostart.ps1 b/ci/runner/autostart.ps1
new file mode 100644
index 0000000..8b7a396
--- /dev/null
+++ b/ci/runner/autostart.ps1
@@ -0,0 +1,12 @@
+# Starts the dasci WSL distro (Docker and gitea-runner inside) when Windows
+# boots, without anyone logging in. Run once in an elevated PowerShell.
+# The task keeps a process attached so WSL never stops the distro as idle.
+$wsl = 'C:\Program Files\WSL\wsl.exe'
+$action = New-ScheduledTaskAction -Execute $wsl -Argument '-d dasci --exec /bin/sleep infinity'
+$trigger = New-ScheduledTaskTrigger -AtStartup
+$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType S4U -RunLevel Highest
+$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -AllowStartIfOnBatteries `
+ -DontStopIfGoingOnBatteries -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1)
+Register-ScheduledTask -TaskName 'DasOS CI (WSL dasci)' -Action $action -Trigger $trigger `
+ -Principal $principal -Settings $settings -Force | Out-Null
+Get-ScheduledTask -TaskName 'DasOS CI (WSL dasci)' | Select-Object TaskName, State
diff --git a/ci/runner/config.yaml b/ci/runner/config.yaml
new file mode 100644
index 0000000..4e14ae7
--- /dev/null
+++ b/ci/runner/config.yaml
@@ -0,0 +1,23 @@
+# gitea-runner configuration for the Das OS CI host (WSL2 distro "dasci").
+# Jobs run directly on the host: scripts/builder.sh needs the host's Docker
+# and paths it can bind-mount into the privileged Void builder container.
+log:
+ level: info
+
+runner:
+ file: /var/lib/gitea-runner/.runner
+ # One build at a time: packages and ISOs share the cache in DASOS_OUT
+ capacity: 1
+ # Matches Gitea's default job timeout; the first build bootstraps void-packages
+ timeout: 3h
+ envs:
+ DASOS_OUT: /var/lib/dasos-ci/out
+ labels:
+ - "dasci:host"
+
+cache:
+ # Build caches live in DASOS_OUT, the workflow does not use actions/cache
+ enabled: false
+
+host:
+ workdir_parent: /var/lib/gitea-runner/work
diff --git a/ci/runner/gitea-runner.service b/ci/runner/gitea-runner.service
new file mode 100644
index 0000000..b44f8c0
--- /dev/null
+++ b/ci/runner/gitea-runner.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Gitea Actions runner for Das OS
+After=docker.service network-online.target
+Wants=network-online.target
+Requires=docker.service
+
+[Service]
+User=runner
+Group=runner
+WorkingDirectory=/var/lib/gitea-runner
+ExecStart=/usr/local/bin/gitea-runner -c /etc/gitea-runner/config.yaml daemon
+Restart=always
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
diff --git a/ci/runner/setup.sh b/ci/runner/setup.sh
new file mode 100644
index 0000000..5994487
--- /dev/null
+++ b/ci/runner/setup.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Sets up the Das OS CI host: Docker Engine, the runner user and gitea-runner.
+# Run as root inside a fresh Ubuntu 24.04 WSL2 distro with systemd enabled:
+# sh setup.sh install everything
+# sh setup.sh register register the runner with git.das.org.ua
+# Safe to run again.
+set -eu
+
+RUNNER_VERSION=5.1.1
+INSTANCE=https://git.das.org.ua
+here=$(cd "$(dirname "$0")" && pwd)
+
+if [ "${1:-}" = register ]; then
+ [ -n "${2:-}" ] || { echo "usage: setup.sh register " >&2; exit 1; }
+ sudo -u runner /usr/local/bin/gitea-runner -c /etc/gitea-runner/config.yaml \
+ register --no-interactive --instance "$INSTANCE" --token "$2" --name dasci
+ systemctl restart gitea-runner
+ exit 0
+fi
+
+export DEBIAN_FRONTEND=noninteractive
+apt-get update -q
+apt-get install -yq ca-certificates curl git make xz-utils
+
+# Docker Engine from Docker's repository (Ubuntu's docker.io lags behind)
+install -m 0755 -d /etc/apt/keyrings
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+. /etc/os-release
+echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $VERSION_CODENAME stable" \
+ >/etc/apt/sources.list.d/docker.list
+apt-get update -q
+apt-get install -yq docker-ce docker-ce-cli containerd.io docker-buildx-plugin
+systemctl enable --now docker
+
+# Jobs run as an unprivileged user: build-packages.sh creates its xbps-src user
+# with the caller's UID, which must not be root
+id runner >/dev/null 2>&1 || useradd --create-home --shell /bin/bash --groups docker runner
+install -d -o runner -g runner /var/lib/gitea-runner /var/lib/dasos-ci /var/lib/dasos-ci/out
+
+url=https://gitea.com/gitea/runner/releases/download/v$RUNNER_VERSION/gitea-runner-$RUNNER_VERSION-linux-amd64.xz
+tmp=$(mktemp -d)
+curl -fsSL -o "$tmp/gitea-runner.xz" "$url"
+curl -fsSL -o "$tmp/gitea-runner.xz.sha256" "$url.sha256"
+(cd "$tmp" && echo "$(cut -d' ' -f1 gitea-runner.xz.sha256) gitea-runner.xz" | sha256sum -c --quiet)
+xz -d "$tmp/gitea-runner.xz"
+install -m 0755 "$tmp/gitea-runner" /usr/local/bin/gitea-runner
+rm -rf "$tmp"
+
+install -D -m 0644 "$here/config.yaml" /etc/gitea-runner/config.yaml
+install -m 0644 "$here/gitea-runner.service" /etc/systemd/system/gitea-runner.service
+systemctl daemon-reload
+systemctl enable gitea-runner
+# The runner starts once it is registered
+if [ -f /var/lib/gitea-runner/.runner ]; then
+ systemctl restart gitea-runner
+else
+ echo "Now register: sh setup.sh register "
+fi
+gitea-runner --version
diff --git a/scripts/builder.sh b/scripts/builder.sh
index 84db00b..411ce8b 100755
--- a/scripts/builder.sh
+++ b/scripts/builder.sh
@@ -1,18 +1,21 @@
#!/usr/bin/env bash
# Runs a command inside the Void Linux builder container.
# The project is mounted at /work; downloaded packages are cached in out/cache/xbps.
+# DASOS_OUT puts out/ elsewhere (CI keeps it between checkouts).
set -euo pipefail
root=$(cd "$(dirname "$0")/.." && pwd)
+out=${DASOS_OUT:-$root/out}
image=dasos-builder
docker build --quiet --tag "$image" "$root/docker/builder" >/dev/null
-mkdir -p "$root/out/cache/xbps"
+mkdir -p "$out/cache/xbps"
# --privileged: mklive mounts /proc, /sys, /dev and a loop device for the root image
exec docker run --rm --privileged \
--volume "$root:/work" \
- --volume "$root/out/cache/xbps:/var/cache/xbps" \
+ ${DASOS_OUT:+--volume "$out:/work/out"} \
+ --volume "$out/cache/xbps:/var/cache/xbps" \
--env HOST_UID="$(id -u)" \
--env HOST_GID="$(id -g)" \
"$image" "$@"