From f3793d468cccb2e7c11b74dd4991c8cdd04cfad0 Mon Sep 17 00:00:00 2001 From: Anry Das Date: Tue, 20 Oct 2026 14:55:45 +0300 Subject: [PATCH] Incremental package builds and releases on v* tags (0.8.0) - build-packages.sh rebuilds a package only when its srcpkgs/ directory changed, its binary package is missing, or a dry-run install finds a shared library Void no longer provides; DASOS_REBUILD=1 rebuilds all. An unchanged `make packages` takes 20 s instead of 17 min on the CI host - ci/release.sh publishes both ISOs and SHA256SUMS as a Gitea release when the workflow runs for a v* tag matching VERSION - README and TODO updated Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yml | 12 +++++++++ Makefile | 2 +- README.md | 7 ++--- README_ua.md | 7 ++--- TODO.md | 7 ++--- VERSION | 2 +- ci/release.sh | 41 +++++++++++++++++++++++++++++ scripts/builder.sh | 1 + scripts/container/build-packages.sh | 31 ++++++++++++++++++++++ 9 files changed, 99 insertions(+), 11 deletions(-) create mode 100755 ci/release.sh diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index a88b5d0..724b44f 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,5 +1,6 @@ # 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. +# A v* tag also publishes the images as a release (ci/release.sh). name: build on: @@ -15,6 +16,8 @@ concurrency: jobs: build: runs-on: dasci + permissions: + contents: write # ci/release.sh creates releases steps: - uses: builtin:checkout @@ -25,3 +28,12 @@ jobs: - name: Result run: ls -lh "$DASOS_OUT/iso/"*-"$(cat VERSION)"-x86_64.iso + + # A v* tag publishes both images and their checksums as a release + - name: Release + if: startsWith(gitea.ref, 'refs/tags/v') + env: + GITEA_TOKEN: ${{ gitea.token }} + GITEA_SERVER_URL: ${{ gitea.server_url }} + GITEA_REPOSITORY: ${{ gitea.repository }} + run: ci/release.sh "${{ gitea.ref_name }}" "$DASOS_OUT/iso" diff --git a/Makefile b/Makefile index a7eed34..62c8fc0 100644 --- a/Makefile +++ b/Makefile @@ -40,5 +40,5 @@ test: # Build output may be owned by root when a build was interrupted, so remove it from the container clean: - scripts/builder.sh rm -rf /work/out/hostdir/binpkgs /work/out/iso + scripts/builder.sh rm -rf /work/out/hostdir/binpkgs /work/out/hostdir/stamps /work/out/iso rm -rf out/qemu diff --git a/README.md b/README.md index 0f835fc..e4380c8 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_ -[![Void Linux](https://img.shields.io/badge/Void_Linux-x86__64_glibc-green)](https://voidlinux.org) [![Xfce](https://img.shields.io/badge/Xfce-4.20-orange)](https://xfce.org) [![runit](https://img.shields.io/badge/init-runit-blue)](https://docs.voidlinux.org/config/services/) [![Go](https://img.shields.io/badge/Go-1.24+-blue)](https://go.dev) [![Docker](https://img.shields.io/badge/Docker-29-blue)](https://www.docker.com) [![Version](https://img.shields.io/badge/version-0.7.0-lightgrey)](#) +[![Void Linux](https://img.shields.io/badge/Void_Linux-x86__64_glibc-green)](https://voidlinux.org) [![Xfce](https://img.shields.io/badge/Xfce-4.20-orange)](https://xfce.org) [![runit](https://img.shields.io/badge/init-runit-blue)](https://docs.voidlinux.org/config/services/) [![Go](https://img.shields.io/badge/Go-1.24+-blue)](https://go.dev) [![Docker](https://img.shields.io/badge/Docker-29-blue)](https://www.docker.com) [![Version](https://img.shields.io/badge/version-0.8.0-lightgrey)](#) 🇺🇦 [Українська версія](README_ua.md) @@ -180,6 +180,7 @@ Void Linux is fast, simple and independent: runit instead of systemd, and its ow - `srcpkgs/das-server` is the server metapackage with the nftables, fail2ban, sshd and Docker defaults. `srcpkgs/das-helpers` contains `dsv`, `duser`, `dfw`, `dsys`, `dprofile` and the profiles. - `srcpkgs/calamares`, `srcpkgs/adw-gtk3` and `srcpkgs/font-jetbrains-mono` package upstream releases that Void does not have. - `make packages` lays the templates over a shallow clone of void-packages and builds them with `xbps-src` into `out/hostdir/binpkgs`. The ISO build installs them from there. +- A package is rebuilt only when its directory in `srcpkgs/` changed, its binary package is missing, or Void no longer provides a shared library it links to (checked with a dry-run install). `DASOS_REBUILD=1 make packages` rebuilds everything. #### Containerised build **_The whole build runs in a Docker container, so only Docker and QEMU are needed on the host._** @@ -193,7 +194,7 @@ Void Linux is fast, simple and independent: runit instead of systemd, and its ow - 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. +- A `v*` tag that matches `VERSION` also publishes both ISOs and `SHA256SUMS` as a release (`ci/release.sh`, with the job's own token): `git tag v0.8.0 && git push origin v0.8.0`. Other builds keep the ISOs on the CI host in `/var/lib/dasos-ci/out/iso`. ### 🚀 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. @@ -207,6 +208,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.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. +**Note:** Das OS is an early version (0.8.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 089b36c..95bafdb 100644 --- a/README_ua.md +++ b/README_ua.md @@ -1,7 +1,7 @@ ## Das OS ### _Легкий і дружній десктоп на базі Void Linux з Xfce та українським колоритом, а також консольна серверна версія_ -[![Void Linux](https://img.shields.io/badge/Void_Linux-x86__64_glibc-green)](https://voidlinux.org) [![Xfce](https://img.shields.io/badge/Xfce-4.20-orange)](https://xfce.org) [![runit](https://img.shields.io/badge/init-runit-blue)](https://docs.voidlinux.org/config/services/) [![Go](https://img.shields.io/badge/Go-1.24+-blue)](https://go.dev) [![Docker](https://img.shields.io/badge/Docker-29-blue)](https://www.docker.com) [![Version](https://img.shields.io/badge/version-0.7.0-lightgrey)](#) +[![Void Linux](https://img.shields.io/badge/Void_Linux-x86__64_glibc-green)](https://voidlinux.org) [![Xfce](https://img.shields.io/badge/Xfce-4.20-orange)](https://xfce.org) [![runit](https://img.shields.io/badge/init-runit-blue)](https://docs.voidlinux.org/config/services/) [![Go](https://img.shields.io/badge/Go-1.24+-blue)](https://go.dev) [![Docker](https://img.shields.io/badge/Docker-29-blue)](https://www.docker.com) [![Version](https://img.shields.io/badge/version-0.8.0-lightgrey)](#) 🇬🇧 [English version](README.md) @@ -180,6 +180,7 @@ Void Linux швидкий, простий і незалежний: runit зам - `srcpkgs/das-server` — серверний метапакет із налаштуваннями nftables, fail2ban, sshd і Docker. `srcpkgs/das-helpers` містить `dsv`, `duser`, `dfw`, `dsys`, `dprofile` і профілі. - `srcpkgs/calamares`, `srcpkgs/adw-gtk3` і `srcpkgs/font-jetbrains-mono` пакують релізи, яких немає у Void. - `make packages` накладає шаблони на неглибокий клон void-packages і збирає їх через `xbps-src` в `out/hostdir/binpkgs`. Збирання ISO встановлює їх звідти. +- Пакет перезбирається, лише коли змінився його каталог у `srcpkgs/`, зник бінарний пакет або Void більше не надає бібліотеку, з якою він зібраний (перевіряється пробним встановленням). `DASOS_REBUILD=1 make packages` перезбирає все. #### Збирання в контейнері **_Усе збирання відбувається в Docker-контейнері, тож на хості потрібні лише Docker і QEMU._** @@ -193,7 +194,7 @@ Void Linux швидкий, простий і незалежний: runit зам - 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` і поки не публікуються. +- Тег `v*`, що збігається з `VERSION`, ще й публікує обидва ISO та `SHA256SUMS` як реліз (`ci/release.sh`, з власним токеном завдання): `git tag v0.8.0 && git push origin v0.8.0`. Інші збирання залишають ISO на CI-хості в `/var/lib/dasos-ci/out/iso`. ### 🚀 Збирання застосунку Вимоги: Linux із Docker (користувач має бути в групі `docker`), QEMU та OVMF для тестування, Go 1.24+ і файли розробки GTK 3 для `make test`. Приблизно 10 ГБ вільного місця. @@ -207,6 +208,6 @@ make clean # видалити результати збиранн ``` ISO з'являються в `out/iso/dasos-<версія>-x86_64.iso` і `out/iso/dasos-server-<версія>-x86_64.iso`. Записати їх на флешку можна через `dd` або Ventoy. -**Примітка:** Das OS — рання версія (0.7.0). Пакети й ISO збираються в CI; підписаний репозиторій на хостингу заплановано на наступний етап. Поки репозиторій не розміщено на хостингу, встановлена система отримує оновлення лише з Void. Das OS базується на Void Linux, але не пов'язана з ним офіційно. +**Примітка:** Das OS — рання версія (0.8.0). Пакети й ISO збираються в CI; підписаний репозиторій на хостингу заплановано на наступний етап. Поки репозиторій не розміщено на хостингу, встановлена система отримує оновлення лише з Void. Das OS базується на Void Linux, але не пов'язана з ним офіційно. ###### _Made by -=:dAs:=-_ diff --git a/TODO.md b/TODO.md index bbf3b21..eb58a5e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # Das OS — що зробити далі -Стан на 2026-10-20: версія 0.7.0, етапи 1–8 готові (live ISO, власний репозиторій, оформлення, інсталятор Calamares зі знімками Btrfs, Go-утиліти `das-update` і `das-welcome`, серверний образ з консольним інсталятором, менші ISO, консольні помічники, профіль розробника), CI (етап 9: пакети й обидва ISO збираються на власному runner, ~45 хв). +Стан на 2026-10-20: версія 0.8.0, етапи 1–8 готові (live ISO, власний репозиторій, оформлення, інсталятор Calamares зі знімками Btrfs, Go-утиліти `das-update` і `das-welcome`, серверний образ з консольним інсталятором, менші ISO, консольні помічники, профіль розробника), CI (етап 9: пакети й обидва ISO збираються на власному runner, ~45 хв). ## Етап 5. Go-утиліти (готово, лишилися перевірки) - [x] `das-update`: вікно, значок у треї (лише коли є оновлення), консольний режим `--cli`, знімок snapper pre/post, встановлення через pkexec, підказка про відкат. @@ -66,7 +66,8 @@ Live ISO без графіки з консольним інсталятором: - [x] `DASOS_OUT` у `scripts/builder.sh`: кеш `out/` на CI-хості в `/var/lib/dasos-ci/out`. - [x] Runner зареєстровано, перший запуск у Gitea (run 1) успішний за 46 хв. - [x] Після перезавантаження Windows runner піднімається сам, без входу в систему (завдання S4U). -- [ ] Вирішити, куди публікувати ISO (Release на тег `v*` чи artifacts). +- [x] Тег `v*` публікує обидва ISO і `SHA256SUMS` як реліз Gitea (`ci/release.sh`). Ліміт вкладення релізу — 2 ГБ на файл (`[repository.release] FILE_MAX_SIZE`). +- [ ] Перевірити першу публікацію на справжньому тегу. ## Репозиторій пакетів - [ ] Підпис через `xbps-rindex --sign`, ключ поза репозиторієм. @@ -89,7 +90,7 @@ Live ISO без графіки з консольним інсталятором: - [ ] Прибрати значки томів live-носія («Том 6,6 ГБ») з робочого столу live-сесії. - [ ] Тема GRUB у встановленій системі (зараз стандартний чорний екран). - [ ] Перевірити меню Whisker вручну. -- [ ] Прискорити `make packages`: не перезбирати Calamares, якщо його версія не змінилася. +- [x] Прискорити `make packages`: пакет перезбирається лише після змін у `srcpkgs/<назва>`, без бінарного пакета або коли у Void зникла потрібна йому бібліотека (`xbps-install -n`). `DASOS_REBUILD=1` перезбирає все. ## Відкладено - [ ] ARM-образ для Raspberry Pi. diff --git a/VERSION b/VERSION index faef31a..a3df0a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.0 +0.8.0 diff --git a/ci/release.sh b/ci/release.sh new file mode 100755 index 0000000..f88c6ba --- /dev/null +++ b/ci/release.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Publishes the ISO images of the current version as a Gitea release: +# release.sh +# Needs GITEA_TOKEN, GITEA_SERVER_URL and GITEA_REPOSITORY (owner/name); in CI +# the job provides them. Running it again for the same tag replaces the files. +set -eu + +tag=$1 +dir=$2 +version=$(cat VERSION) +[ "$tag" = "v$version" ] || { echo "release: tag $tag does not match VERSION $version" >&2; exit 1; } + +api=$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY +auth="Authorization: token $GITEA_TOKEN" +json() { python3 -c "import json,sys; d=json.load(sys.stdin); $1"; } + +isos="dasos-$version-x86_64.iso dasos-server-$version-x86_64.iso" +cd "$dir" +sha256sum $isos >SHA256SUMS + +if ! id=$(curl -fsS -H "$auth" "$api/releases/tags/$tag" 2>/dev/null | json 'print(d["id"])' 2>/dev/null); then + id=$(curl -fsS -H "$auth" -H 'Content-Type: application/json' \ + -d "{\"tag_name\":\"$tag\",\"name\":\"Das OS $version\",\"body\":\"Desktop and server live ISO images. Check them with sha256sum -c SHA256SUMS.\"}" \ + "$api/releases" | json 'print(d["id"])') + echo "release: created $tag (id $id)" +fi + +# Files left from an earlier run for this tag are replaced +curl -fsS -H "$auth" "$api/releases/$id/assets" | + json '[print(a["id"], a["name"]) for a in d]' | + while read -r asset name; do + case " $isos SHA256SUMS " in + *" $name "*) curl -fsS -X DELETE -H "$auth" "$api/releases/$id/assets/$asset" ;; + esac + done + +for f in $isos SHA256SUMS; do + echo "release: uploading $f" + curl -fsS -o /dev/null -H "$auth" -F "attachment=@$f" "$api/releases/$id/assets?name=$f" +done +echo "release: $GITEA_SERVER_URL/$GITEA_REPOSITORY/releases/tag/$tag" diff --git a/scripts/builder.sh b/scripts/builder.sh index 411ce8b..ed8520f 100755 --- a/scripts/builder.sh +++ b/scripts/builder.sh @@ -18,4 +18,5 @@ exec docker run --rm --privileged \ --volume "$out/cache/xbps:/var/cache/xbps" \ --env HOST_UID="$(id -u)" \ --env HOST_GID="$(id -g)" \ + --env DASOS_REBUILD \ "$image" "$@" diff --git a/scripts/container/build-packages.sh b/scripts/container/build-packages.sh index 7f5702c..cbc2578 100755 --- a/scripts/container/build-packages.sh +++ b/scripts/container/build-packages.sh @@ -34,6 +34,30 @@ XBPS_CHROOT_CMD=uchroot XBPS_MAKEJOBS=$(nproc) CONF +# A package is rebuilt only when its directory in srcpkgs/ changed since the last +# build, its binary package is gone, or Void no longer provides a shared library +# it links to (a soname bump there needs a rebuild here). DASOS_REBUILD=1 rebuilds all. +stamps=$hostdir/stamps +mkdir -p "$stamps" +srchash() { + (cd "$vp/srcpkgs/$1" && find . -type f -print0 | sort -z | xargs -0 sha256sum) | sha256sum | cut -d' ' -f1 +} +# An empty root to dry-run installs into: xbps refuses unresolved shared libraries +checkroot=$(mktemp -d) +mkdir -p "$checkroot/var/db/xbps/keys" +cp -a /var/db/xbps/keys/. "$checkroot/var/db/xbps/keys/" +voidrepo=$(sed -n 's/^repository=//p' /usr/share/xbps.d/*-repository-main.conf | head -1) +xbps-install -S -r "$checkroot" -R "$voidrepo" >/dev/null +up_to_date() { + local info version revision + [[ ${DASOS_REBUILD:-} != 1 && -f $stamps/$1 && $(< "$stamps/$1") == "$(srchash "$1")" ]] || return 1 + info=$(as_builder ./xbps-src show "$1") + version=$(sed -n 's/^version:[[:space:]]*//p' <<<"$info") + revision=$(sed -n 's/^revision:[[:space:]]*//p' <<<"$info") + compgen -G "$hostdir/binpkgs/$1-${version}_$revision.*.xbps" >/dev/null || return 1 + xbps-install -n -y -r "$checkroot" -R "$hostdir/binpkgs" -R "$voidrepo" "$1" >/dev/null 2>&1 +} + names=() for dir in /work/srcpkgs/*/; do name=$(basename "$dir") @@ -59,9 +83,16 @@ fi # (also as a dependency of another one) would package the stale files rm -rf masterdir-x86_64/destdir/* for name in "${names[@]}"; do + if up_to_date "$name"; then + echo "==> $name is up to date" + continue + fi echo "==> Building $name" as_builder ./xbps-src -H "$hostdir" -f pkg "$name" + srchash "$name" >"$stamps/$name" done +rm -rf "$checkroot" +chown -R "$HOST_UID:$HOST_GID" "$stamps" echo "==> Repository: $hostdir/binpkgs" ls -1 "$hostdir"/binpkgs/*.xbps