Ship v0.1.0 with prebuilt TUI and complete package audit

This commit is contained in:
fi3w0 2026-10-03 06:34:30 +02:00
parent 1744a5ef3d
commit 46c5e56abe
24 changed files with 947 additions and 24 deletions

View file

@ -37,7 +37,7 @@ The existing installation has not been changed by creating this repo.
## Start ## Start
```sh ```sh
git clone https://git.fiwlabs.dev/fiwdev/Fiw-Gentoo-Dots.git git clone --branch v0.1.0 https://git.fiwlabs.dev/fiwdev/Fiw-Gentoo-Dots.git
cd Fiw-Gentoo-Dots cd Fiw-Gentoo-Dots
./install ./install
``` ```
@ -49,8 +49,10 @@ a final preview. Enter saves without applying. `r` runs the full restore;
`v` enables selected services and `b` deploys the selected bootloader. `v` enables selected services and `b` deploys the selected bootloader.
`u` opens config backups for preview and restoration. Apply actions finish `u` opens config backups for preview and restoration. Apply actions finish
with a [combined report](docs/reports.md) of outcomes and remaining setup. with a [combined report](docs/reports.md) of outcomes and remaining setup.
Go 1.24+ builds the TUI; Python 3.11+ runs the backend. Go dependencies are Linux amd64 includes a verified prebuilt TUI; Git and Python 3.11+ are enough.
recorded in go.mod/go.sum. Saved selections are reused on subsequent launches. Go 1.24+ is the source fallback for changed frontend code or other platforms.
Saved selections are reused on subsequent launches. See the
[v0.1.0 snapshot and build instructions](docs/releases.md).
For a preview without building the TUI: For a preview without building the TUI:
@ -96,6 +98,8 @@ in the TUI, grouped with apps and gaming respectively.
Limine and GRUB are optional alternatives; retaining the current loader is Limine and GRUB are optional alternatives; retaining the current loader is
the default. Kernel choice is independent of bootloader choice. Deployment the default. Kernel choice is independent of bootloader choice. Deployment
previews the target ESP and firmware choices; see [boot setup](docs/boot.md). previews the target ESP and firmware choices; see [boot setup](docs/boot.md).
The [package audit](docs/package-audit.md) accounts for all current explicit
selections. [TideWM](docs/tidewm.md) remains an optional source build.
## Configs ## Configs

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.1.0

412
assets/bin/LICENSES.txt Normal file
View file

@ -0,0 +1,412 @@
# Third-party licences for the bundled TUI
## github.com/aymanbagabas/go-osc52/v2 v2.0.1 / LICENSE
MIT License
Copyright (c) 2022 Ayman Bagabas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/bubbletea v1.3.10 / LICENSE
MIT License
Copyright (c) 2020-2025 Charmbracelet, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc / LICENSE
MIT License
Copyright (c) 2020-2024 Charmbracelet, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/lipgloss v1.1.0 / LICENSE
MIT License
Copyright (c) 2021-2023 Charmbracelet, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/x/ansi v0.10.1 / LICENSE
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd / LICENSE
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/charmbracelet/x/term v0.2.1 / LICENSE
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/lucasb-eyer/go-colorful v1.2.0 / LICENSE
Copyright (c) 2013 Lucas Beyer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## github.com/mattn/go-isatty v0.0.20 / LICENSE
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
MIT License (Expat)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## github.com/mattn/go-runewidth v0.0.16 / LICENSE
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 / LICENSE
MIT License
Copyright (c) 2021 Christian Muehlhaeuser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/muesli/cancelreader v0.2.2 / LICENSE
MIT License
Copyright (c) 2022 Erik Geiser and Christian Muehlhaeuser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/muesli/termenv v0.16.0 / LICENSE
MIT License
Copyright (c) 2019 Christian Muehlhaeuser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/rivo/uniseg v0.4.7 / LICENSE.txt
MIT License
Copyright (c) 2019 Oliver Kuederle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e / LICENSE
The MIT License (MIT)
Copyright (c) 2016 Anmol Sethi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## golang.org/x/sys v0.36.0 / LICENSE
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Go standard library and runtime
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1
assets/bin/SHA256SUMS Normal file
View file

@ -0,0 +1 @@
767c35da0b46eaa785fa20b2d008e8403aa64e46223e116228d6b4be91e6dfb5 fiw-dots-linux-amd64.gz

Binary file not shown.

12
assets/bin/tui.json Normal file
View file

@ -0,0 +1,12 @@
{
"version": "0.1.0",
"source_sha256": "df16d0f9960968f191147aa7c45ff1161b30616f7e545f7a28bae9dc038d026e",
"binaries": {
"linux-amd64": {
"file": "fiw-dots-linux-amd64.gz",
"sha256": "32e133786d350016085741f1e4f15fae174977cb7cd63eae7999f97d3715c59b",
"gzip_sha256": "767c35da0b46eaa785fa20b2d008e8403aa64e46223e116228d6b4be91e6dfb5",
"size": 3555490
}
}
}

View file

@ -15,6 +15,8 @@ import (
"github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/ansi"
) )
var tuiVersion = "source"
type selection struct { type selection struct {
Name string `json:"name"` Name string `json:"name"`
Profile string `json:"profile"` Profile string `json:"profile"`
@ -492,6 +494,10 @@ func (m model) View() string {
} }
func main() { func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Println("Fiw-Gentoo-Dots TUI " + tuiVersion)
return
}
repo, err := os.Getwd() repo, err := os.Getwd()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View file

@ -1,5 +1,10 @@
# Styling assets # Styling assets
The bundled TUI at `assets/bin` is built from this project's MIT source.
Its third-party Go dependency and runtime notices are retained in
`assets/bin/LICENSES.txt`. Binary/archive fingerprints and the frontend
source fingerprint are recorded in `assets/bin/tui.json`.
The captured styling uses a small subset of the original installed assets: The captured styling uses a small subset of the original installed assets:
- Utterly-Round-Dark window decorations retain the original author metadata - Utterly-Round-Dark window decorations retain the original author metadata

29
docs/package-audit.md Normal file
View file

@ -0,0 +1,29 @@
# Explicit package audit
The 2026-10-03 audit compares Portage `world` and the selected `@fiw-*` sets
with the main lists, optional lists and kernel choices. It reads explicit
selections only; it does not expand package dependencies or copy an installed
package inventory.
Result: **111 explicit selections accounted for**. 110 have package-list or
kernel coverage. `app-admin/fiw-scripts` supplies the two legacy helpers
already retained under `optional/`; that exception is documented in
`packages/audit-exceptions.json`. There are no unexplained missing packages
or unresolved sets.
The audit added `x11-misc/ydotool` to `cli`. It does not enable its daemon or
change input permissions. Filelight, Btrfs tools, NVIDIA drivers and TideWM
remain in their separate optional selections. Dolphin, Ark, Gwenview and
Spectacle remain in `fiw-apps`.
Repeat the audit on a device:
```sh
python3 tools/audit-packages.py
python3 tools/audit-packages.py --json > local/package-audit.json
```
Version pins and old repository qualifiers are normalized for coverage;
explicit Java slots remain distinct. Cyclic or missing selected sets are
reported rather than treated as covered. The tool does not change package
lists, Portage configuration or the device's world selections.

65
docs/releases.md Normal file
View file

@ -0,0 +1,65 @@
# Release snapshots
## v0.1.0 — First restoration snapshot
This tag captures the current KDE shortcuts and appearance, categorized
packages, individually optional app preferences and restoration interface.
Use the snapshot when restoring another device:
```sh
git clone --branch v0.1.0 https://git.fiwlabs.dev/fiwdev/Fiw-Gentoo-Dots.git
cd Fiw-Gentoo-Dots
./install
```
The repository includes a compressed, static Linux amd64 TUI binary. The
launcher verifies its archive, executable and frontend source fingerprints
before use, then caches the executable in ignored `build/`. Git and Python
3.11+ are sufficient for the bundled TUI. The Python backend remains readable
and runs directly for CLI commands.
Downloads are available through the Forgejo
[tag](https://git.fiwlabs.dev/fiwdev/Fiw-Gentoo-Dots/src/tag/v0.1.0) and
[source archive](https://git.fiwlabs.dev/fiwdev/Fiw-Gentoo-Dots/archive/v0.1.0.tar.gz).
The archive includes the binary, checksums and upstream licence notices.
This is a Git-tagged snapshot; no separate Forgejo release attachment is needed.
Included in this snapshot:
- Exact personal KDE shortcut assignments and optional KDE/GTK styling.
- Stock and Fiw's Ryzen presets with categorized packages and binary preference.
- Independent app configs, user Flatpaks and optional service selections.
- Config requirements, conflict prompts, backup restoration and combined reports.
- Optional Limine/GRUB deployment with target preview and firmware choices.
- A refreshed optional TideWM recipe and an audit covering all current explicit selections.
The installer starts from an existing Gentoo system. Boot automation covers
unsigned amd64 UEFI and plain ext4/Btrfs roots. A full fresh-system package and
boot run remains unverified. TideWM is a live source package, separately
optional and subject to the compile-or-skip prompt.
## Source fallback and maintenance
Go 1.24+ is needed when the binary is absent, unsupported on the current
architecture, or does not match changed frontend source. To explicitly build:
```sh
FIW_DOTS_SOURCE=1 ./install
```
The source fallback does not download a newer Go toolchain automatically.
Config-only changes retain the frontend fingerprint, so recapturing app
preferences does not require rebuilding the binary.
Before tagging a new snapshot, update `VERSION` and rebuild the bundle:
```sh
python3 tools/build-release.py
python3 tools/check-private.py
```
The maintainer build uses `CGO_ENABLED=0`, generic amd64 instructions,
`-trimpath` and no Git build metadata. It records archive/executable/source
SHA256 fingerprints and retains notices for the Go dependencies used in
that binary and for the Go runtime. Review and commit the generated
`assets/bin/` files with the matching frontend source, then tag that commit.

View file

@ -2,7 +2,8 @@
| Helper | Purpose | How it is used | | Helper | Purpose | How it is used |
|---|---|---| |---|---|---|
| `install` | Builds/opens the Bubble Tea chooser, or forwards CLI options to the Python backend | Main entry point | | `install` | Opens the verified prebuilt chooser with a Go source fallback, or forwards CLI options to the Python backend | Main entry point |
| `lib/launcher.py` | Verifies frontend/binary fingerprints and extracts/caches the bundled TUI | Called by the entry point |
| `lib/rice.py` | Validates selections, previews plans, restores configs and installs Portage packages | Called by the entry point/TUI | | `lib/rice.py` | Validates selections, previews plans, restores configs and installs Portage packages | Called by the entry point/TUI |
| `lib/setup.py` | Stages missing overlays, installs selected user Flatpaks and enables selected service units | Called by the backend | | `lib/setup.py` | Stages missing overlays, installs selected user Flatpaks and enables selected service units | Called by the backend |
| `lib/boot.py` | Detects the target ESP/root, previews/deploys Limine or GRUB, refreshes kernel menus | Boot action; copied as `fiw-dots-boot` on deployment | | `lib/boot.py` | Detects the target ESP/root, previews/deploys Limine or GRUB, refreshes kernel menus | Boot action; copied as `fiw-dots-boot` on deployment |
@ -12,6 +13,8 @@
| `tools/capture.py` | Recaptures the explicit preference allowlist without copying full personal profiles | Maintenance command; review the diff | | `tools/capture.py` | Recaptures the explicit preference allowlist without copying full personal profiles | Maintenance command; review the diff |
| `tools/shortcuts.py` | Regenerates the readable shortcut reference from captured repo files | Run after changing/capturing shortcuts | | `tools/shortcuts.py` | Regenerates the readable shortcut reference from captured repo files | Run after changing/capturing shortcuts |
| `tools/check-private.py` | Scans publishable files for personal paths, identifiers and credentials; keeps fingerprinted upstream attribution | Run before committing | | `tools/check-private.py` | Scans publishable files for personal paths, identifiers and credentials; keeps fingerprinted upstream attribution | Run before committing |
| `tools/audit-packages.py` | Compares explicit world/selected sets with main/optional package lists and documented helper exceptions | Read-only package audit |
| `tools/build-release.py` | Builds the generic static amd64 TUI bundle, fingerprints and dependency licence notices | Maintainer command before tagging snapshots |
| `optional/fiw-cachy-patch` | Downloads matching vanilla/CachyOS kernel sources, generates a patch and adjusts the installed guard/name config | Explicit manual root operation; not part of ordinary restoration | | `optional/fiw-cachy-patch` | Downloads matching vanilla/CachyOS kernel sources, generates a patch and adjusts the installed guard/name config | Explicit manual root operation; not part of ordinary restoration |
| `optional/fiw-limine-splice` | Replaces the old Gentoo snippet block in an existing Limine menu and orders its entries | Explicit manual use with an ESP argument; specific to the earlier snippet design | | `optional/fiw-limine-splice` | Replaces the old Gentoo snippet block in an existing Limine menu and orders its entries | Explicit manual use with an ESP argument; specific to the earlier snippet design |
| `optional/spotify/spotify-patch` | Runs the retained SpotX/Spicetify customization workflow for the Gentoo Spotify client | Explicit optional user operation; stock Spotify is the default | | `optional/spotify/spotify-patch` | Runs the retained SpotX/Spicetify customization workflow for the Gentoo Spotify client | Explicit optional user operation; stock Spotify is the default |

View file

@ -1,7 +1,8 @@
# Restore the setup # Restore the setup
Start with an installed amd64 Gentoo system using systemd and a multilib Start with an installed amd64 Gentoo system using systemd and a multilib
Plasma profile, with Git and Python 3.11+ available. Go 1.24+ builds the TUI. Plasma profile, with Git and Python 3.11+ available. The snapshot includes a
prebuilt Linux amd64 TUI; Go 1.24+ is needed for its source fallback only.
This repo does not partition disks or install a stage3. This repo does not partition disks or install a stage3.
## Terminal interface ## Terminal interface
@ -130,8 +131,9 @@ or Btrfs roots, and unsigned boot. See [boot setup](boot.md) for previews,
firmware choices and kernel update integration. Other layouts can keep their firmware choices and kernel update integration. Other layouts can keep their
existing loader. existing loader.
The optional TideWM live recipe still needs a refresh against its upstream The optional [TideWM recipe](tidewm.md) follows the audited upstream master
development branch. End-to-end package and boot execution on a fresh Gentoo branch and uses the normal compile-or-skip prompt. End-to-end package and
boot execution on a fresh Gentoo
installation remains unverified; development checks used temporary homes, installation remains unverified; development checks used temporary homes,
staged Portage configuration and mocked system commands. staged Portage configuration and mocked system commands.

28
docs/tidewm.md Normal file
View file

@ -0,0 +1,28 @@
# Optional TideWM
The `tidewm` section remains unchecked in the shipped presets. It installs
TideWM and its selected Wayland tools without changing KDE config presets.
Its GURU tools trigger repository preparation even when `fiw-apps` and
`gaming` are unchecked.
The live recipe follows upstream `master`, audited at
[f86db8bd](https://github.com/Fi3w0/TideWM/tree/f86db8bd02a7a52de9028bb075fe845717f398f3).
It builds `TideWM`, `tidectl` and the `wavefmt` formatter, with optional
screencast/accessibility features. Rust 1.88 is required by the locked Lua
binding version; the recipe also declares Wayland, pkg-config and the GTK
portal fallback used by the upstream session.
The recipe fetches and vendors Cargo dependencies during unpack, then uses
frozen builds with no compilation-stage network resolution. Screencasting
is enabled by default, including the session-specific portal files.
This is a source build. The main installer shows it as an additional
compile-or-skip choice; it is not part of the deliberately small source list.
A live recipe follows future upstream changes, while the current audit SHA
documents the version reviewed for this snapshot. Compilation of the
refreshed tree on a fresh device remains unverified.
After installation, choose **TideWM** at login. It creates its own default
configuration on first launch. No existing TideWM settings are captured or
overwritten by this repo. Upstream documents its
[configuration format](https://github.com/Fi3w0/TideWM/blob/f86db8bd02a7a52de9028bb075fe845717f398f3/WAVE.md).

View file

@ -5,10 +5,4 @@ if (($#)); then
exec python3 "$repo/lib/rice.py" "$@" exec python3 "$repo/lib/rice.py" "$@"
fi fi
cd "$repo" cd "$repo"
if ! command -v go >/dev/null; then exec python3 "$repo/lib/launcher.py"
echo 'The TUI requires Go to build. Use ./install --plan for the Python preview.' >&2
exit 1
fi
mkdir -p build
go build -o build/fiw-dots ./cmd/dots
exec build/fiw-dots

93
lib/launcher.py Normal file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Run the matching bundled TUI, with a Go source fallback."""
import gzip
import hashlib
import io
import json
import os
import platform
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
def source_hash(repo=REPO):
paths = [repo / name for name in ('go.mod', 'go.sum', 'VERSION')]
for directory in ('cmd', 'internal'):
paths += [path for path in (repo / directory).rglob('*.go') if not path.name.endswith('_test.go')]
value = hashlib.sha256()
for path in sorted(paths):
value.update(path.relative_to(repo).as_posix().encode() + b'\0' + path.read_bytes() + b'\0')
return value.hexdigest()
def digest(data):
return hashlib.sha256(data).hexdigest()
def bundled(repo, cache):
if platform.system() != 'Linux' or platform.machine() not in ('x86_64', 'amd64'):
raise RuntimeError('The bundled TUI supports Linux amd64.')
root = repo / 'assets/bin'
metadata = json.loads((root / 'tui.json').read_text())
if metadata['source_sha256'] != source_hash(repo):
raise RuntimeError('The bundled TUI does not match the frontend source in this checkout.')
binary = metadata['binaries']['linux-amd64']
path = root / binary['file']
if path.parent.resolve() != root.resolve():
raise RuntimeError('Invalid bundled TUI path.')
if cache.is_file() and not cache.is_symlink() and digest(cache.read_bytes()) == binary['sha256']:
cache.chmod(0o755)
return cache
compressed = path.read_bytes()
if digest(compressed) != binary['gzip_sha256']:
raise RuntimeError('Bundled TUI archive checksum mismatch.')
size = binary['size']
if not isinstance(size, int) or not 0 < size <= 64 * 1024 * 1024:
raise RuntimeError('Invalid bundled TUI size.')
with gzip.GzipFile(fileobj=io.BytesIO(compressed)) as stream:
data = stream.read(size + 1)
if len(data) != size or digest(data) != binary['sha256']:
raise RuntimeError('Bundled TUI binary checksum mismatch.')
cache.parent.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(dir=cache.parent, prefix='.fiw-dots-', delete=False) as file:
temporary = Path(file.name)
file.write(data)
try:
temporary.chmod(0o755)
temporary.replace(cache)
finally:
temporary.unlink(missing_ok=True)
return cache
def launch(repo=REPO):
os.chdir(repo)
cache = repo / 'build/fiw-dots'
if os.environ.get('FIW_DOTS_SOURCE') != '1':
try:
return bundled(repo, cache)
except (KeyError, ValueError, OSError, RuntimeError) as error:
print('Prebuilt TUI unavailable: ' + str(error), file=sys.stderr)
if not shutil.which('go'):
raise RuntimeError('Use the v0.1.0 snapshot for its prebuilt Linux amd64 TUI, or install Go 1.24+ for source builds. Python CLI commands such as ./install --plan remain available.')
print('Building the TUI from source with Go.', file=sys.stderr)
cache.parent.mkdir(parents=True, exist_ok=True)
version = (repo / 'VERSION').read_text().strip()
env = dict(os.environ, CGO_ENABLED='0', GOTOOLCHAIN='local')
subprocess.run(['go', 'build', '-trimpath', '-buildvcs=false', '-ldflags=-s -w -X main.tuiVersion=' + version,
'-o', str(cache), './cmd/dots'], env=env, check=True)
return cache
if __name__ == '__main__':
try:
binary = launch()
os.execv(binary, [str(binary)] + sys.argv[1:])
except (ValueError, OSError, RuntimeError, subprocess.CalledProcessError) as error:
print('TUI launcher: ' + str(error), file=sys.stderr)
sys.exit(1)

View file

@ -122,7 +122,7 @@ def manual_steps(selection):
if 'prism' in selection['configs']: if 'prism' in selection['configs']:
steps.append('Set up Prism accounts, Java and Minecraft instances on this device; see docs/app-configs.md.') steps.append('Set up Prism accounts, Java and Minecraft instances on this device; see docs/app-configs.md.')
if 'tidewm' in selection['groups']: if 'tidewm' in selection['groups']:
steps.append('The optional TideWM live recipe needs an upstream refresh before use.') steps.append('Choose TideWM at login after installation; it creates its own default config (docs/tidewm.md).')
return steps return steps
@ -578,6 +578,7 @@ def check_packages(selection):
def main(): def main():
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--version', action='version', version='Fiw-Gentoo-Dots ' + (REPO / 'VERSION').read_text().strip())
parser.add_argument('--selection') parser.add_argument('--selection')
parser.add_argument('--profile', choices=['stock', 'fiw-ryzen'], default='stock') parser.add_argument('--profile', choices=['stock', 'fiw-ryzen'], default='stock')
parser.add_argument('--home', type=Path, default=Path.home()) parser.add_argument('--home', type=Path, default=Path.home())

View file

@ -36,7 +36,7 @@ SERVICES = {
def required_repositories(selection): def required_repositories(selection):
names = [] names = []
if any(g in selection['groups'] for g in ('fiw-apps', 'gaming')): if any(g in selection['groups'] for g in ('fiw-apps', 'gaming', 'tidewm')):
names.append('guru') names.append('guru')
if 'gaming' in selection['groups']: if 'gaming' in selection['groups']:
names.append('steam-overlay') names.append('steam-overlay')

View file

@ -3,19 +3,20 @@
EAPI=8 EAPI=8
RUST_MIN_VER="1.86.0" # Cargo.lock currently includes mlua 0.12, whose MSRV is Rust 1.88.
RUST_MIN_VER="1.88.0"
inherit cargo git-r3 xdg inherit cargo git-r3 xdg
DESCRIPTION="Water-themed Wayland tiling compositor built on Smithay" DESCRIPTION="Water-themed Wayland tiling compositor built on Smithay"
HOMEPAGE="https://github.com/Fi3w0/TideWM" HOMEPAGE="https://github.com/Fi3w0/TideWM"
EGIT_REPO_URI="https://github.com/Fi3w0/TideWM.git" EGIT_REPO_URI="https://github.com/Fi3w0/TideWM.git"
# Development happens on this branch; master is behind it. # Follow the upstream default branch. Audited at f86db8bd (2026-10-03).
EGIT_BRANCH="claude/new-session-bppt7l" EGIT_BRANCH="master"
LICENSE="GPL-3+" LICENSE="GPL-3+"
# Dependent crate licenses # Dependent crate licenses
LICENSE+=" Apache-2.0 BSD ISC MIT Unicode-3.0 ZLIB" LICENSE+=" Apache-2.0 BSD CC0-1.0 ISC MIT Unicode-3.0 ZLIB"
SLOT="0" SLOT="0"
IUSE="+screencast accessibility" IUSE="+screencast accessibility"
@ -23,6 +24,7 @@ DEPEND="
dev-libs/expat dev-libs/expat
dev-libs/libinput:= dev-libs/libinput:=
media-libs/mesa media-libs/mesa
dev-libs/wayland
sys-auth/seatd:= sys-auth/seatd:=
virtual/libudev:= virtual/libudev:=
x11-libs/libdrm x11-libs/libdrm
@ -30,10 +32,14 @@ DEPEND="
screencast? ( media-video/pipewire:= ) screencast? ( media-video/pipewire:= )
" "
RDEPEND="${DEPEND} RDEPEND="${DEPEND}
screencast? ( sys-apps/xdg-desktop-portal ) screencast? (
sys-apps/xdg-desktop-portal
sys-apps/xdg-desktop-portal-gtk
)
" "
# pipewire-sys generates its bindings with bindgen (libclang) # pipewire-sys generates its bindings with bindgen (libclang)
BDEPEND="screencast? ( llvm-core/clang )" BDEPEND="virtual/pkgconfig
screencast? ( llvm-core/clang )"
QA_FLAGS_IGNORED=".*" QA_FLAGS_IGNORED=".*"
@ -56,11 +62,12 @@ src_configure() {
$(usev screencast) $(usev screencast)
$(usev accessibility) $(usev accessibility)
) )
cargo_src_configure # Vendored Git crates are already fetched during src_unpack.
cargo_src_configure --frozen --bin TideWM --bin tidectl --bin wavefmt
} }
src_install() { src_install() {
dobin "$(cargo_target_dir)"/TideWM "$(cargo_target_dir)"/tidectl dobin "$(cargo_target_dir)"/TideWM "$(cargo_target_dir)"/tidectl "$(cargo_target_dir)"/wavefmt
insinto /usr/share/wayland-sessions insinto /usr/share/wayland-sessions
doins share/wayland-sessions/tidewm.desktop doins share/wayland-sessions/tidewm.desktop

View file

@ -0,0 +1,3 @@
{
"app-admin/fiw-scripts": "Its two legacy kernel/boot helpers are retained as optional/fiw-cachy-patch and optional/fiw-limine-splice for manual use. The current boot action has its own refresh helper."
}

View file

@ -17,3 +17,4 @@ app-portage/portage-utils
sys-apps/usbutils sys-apps/usbutils
sys-apps/hdparm sys-apps/hdparm
media-video/ffmpeg media-video/ffmpeg
x11-misc/ydotool

81
tests/test_release.py Normal file
View file

@ -0,0 +1,81 @@
import gzip
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
REPO = Path(__file__).resolve().parents[1]
def module(name, path):
spec = importlib.util.spec_from_file_location(name, path)
value = importlib.util.module_from_spec(spec)
spec.loader.exec_module(value)
return value
launcher = module('launcher', REPO / 'lib/launcher.py')
audit = module('audit', REPO / 'tools/audit-packages.py')
class ReleaseTests(unittest.TestCase):
def bundle(self, repo):
for name in ('go.mod', 'go.sum', 'VERSION'):
(repo / name).write_text('fixture')
root = repo / 'assets/bin'; root.mkdir(parents=True)
data = b'fixture-binary'
archive = gzip.compress(data, mtime=0)
(root / 'fiw-dots.gz').write_bytes(archive)
metadata = {'source_sha256': launcher.source_hash(repo), 'binaries': {
'linux-amd64': {'file': 'fiw-dots.gz', 'size': len(data),
'sha256': launcher.digest(data), 'gzip_sha256': launcher.digest(archive)}}}
(root / 'tui.json').write_text(json.dumps(metadata))
return data
def test_bundle_runs_without_go_and_replaces_a_damaged_cache(self):
with tempfile.TemporaryDirectory() as temp:
repo = Path(temp); data = self.bundle(repo)
cache = repo / 'build/fiw-dots'; cache.parent.mkdir(); cache.write_bytes(b'damaged')
with patch.object(launcher.platform, 'system', return_value='Linux'), patch.object(launcher.platform, 'machine', return_value='x86_64'), patch.object(launcher.shutil, 'which', return_value=None), patch.object(launcher.os, 'chdir'), patch.dict(launcher.os.environ, {'FIW_DOTS_SOURCE': '0'}), patch.object(launcher.subprocess, 'run') as run:
self.assertEqual(launcher.launch(repo), cache)
self.assertEqual(cache.read_bytes(), data)
self.assertTrue(cache.stat().st_mode & 0o111)
run.assert_not_called()
def test_changed_frontend_and_modified_archives_are_rejected(self):
with tempfile.TemporaryDirectory() as temp:
repo = Path(temp); self.bundle(repo)
cache = repo / 'build/fiw-dots'
with patch.object(launcher.platform, 'system', return_value='Linux'), patch.object(launcher.platform, 'machine', return_value='x86_64'):
archive = repo / 'assets/bin/fiw-dots.gz'
original = archive.read_bytes(); archive.write_bytes(original + b'modified')
with self.assertRaises(RuntimeError):
launcher.bundled(repo, cache)
archive.write_bytes(original)
(repo / 'cmd/dots').mkdir(parents=True)
(repo / 'cmd/dots/main.go').write_text('changed frontend')
with self.assertRaises(RuntimeError):
launcher.bundled(repo, cache)
self.assertFalse(cache.exists())
def test_audit_expands_only_selected_sets_and_preserves_java_slots(self):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); sets = root / 'sets'; sets.mkdir()
world, selected = root / 'world', root / 'world_sets'
world.write_text('=app-editors/vscode-1.0::old\n')
selected.write_text('@desktop\n')
(sets / 'desktop').write_text('app-editors/vscode\n@java\n')
(sets / 'java').write_text('dev-java/openjdk-bin:21\n')
(sets / 'not-selected').write_text('dev-libs/dependency-only\n')
result, unresolved = audit.explicit(world, selected, sets)
self.assertEqual(set(result), {'app-editors/vscode', 'dev-java/openjdk-bin:21'})
self.assertEqual(unresolved, [])
(sets / 'java').write_text('@desktop\n')
with self.assertRaises(ValueError):
audit.explicit(world, selected, sets)
if __name__ == '__main__':
unittest.main()

View file

@ -23,6 +23,7 @@ boot = module('boot')
class SetupTests(unittest.TestCase): class SetupTests(unittest.TestCase):
def test_only_selected_overlays_are_staged_and_existing_ones_are_preserved(self): def test_only_selected_overlays_are_staged_and_existing_ones_are_preserved(self):
self.assertEqual(setup.required_repositories({'groups': ['kde']}), []) self.assertEqual(setup.required_repositories({'groups': ['kde']}), [])
self.assertEqual(setup.required_repositories({'groups': ['tidewm']}), ['guru'])
self.assertEqual(setup.required_repositories({'groups': ['gaming']}), ['guru', 'steam-overlay']) self.assertEqual(setup.required_repositories({'groups': ['gaming']}), ['guru', 'steam-overlay'])
with tempfile.TemporaryDirectory() as temp: with tempfile.TemporaryDirectory() as temp:
root = Path(temp) root = Path(temp)

92
tools/audit-packages.py Normal file
View file

@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""Compare explicit world/set selections with main and optional package lists."""
import argparse
import json
import re
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
def lines(path):
if not path.is_file():
return []
return [line.split('#', 1)[0].strip() for line in path.read_text().splitlines()
if line.split('#', 1)[0].strip()]
def package(atom):
value = atom.split('::', 1)[0].split('[', 1)[0].lstrip('<>=~')
base, _, slot = value.partition(':')
base = re.sub(r'-[0-9].*$', '', base)
if not re.fullmatch(r'[A-Za-z0-9+_.-]+/[A-Za-z0-9+_.-]+', base):
raise ValueError('Invalid explicit package atom.')
return base + (':' + slot if slot else '')
def explicit(world, world_sets, sets):
selections, unresolved = {}, set()
def read(items, origin, stack=()):
for atom in items:
if atom.startswith('@'):
name = atom[1:]
if not re.fullmatch(r'[A-Za-z0-9+_.-]+', name):
raise ValueError('Invalid selected set name.')
if name in stack:
raise ValueError('Cyclic selected set: ' + name)
source = sets / name
if not source.is_file():
unresolved.add(name)
else:
read(lines(source), '@' + name, stack + (name,))
else:
selections.setdefault(package(atom), set()).add(origin)
read(lines(world), 'world')
read(lines(world_sets), 'world_sets')
return {atom: sorted(origins) for atom, origins in sorted(selections.items())}, sorted(unresolved)
def audit(world, world_sets, sets, repo=REPO):
selected, unresolved = explicit(world, world_sets, sets)
coverage = {}
for path in sorted((repo / 'packages').rglob('*.list')):
if path.name == 'source.list':
continue
category = path.relative_to(repo / 'packages').with_suffix('').as_posix()
for atom in lines(path):
coverage.setdefault(package(atom), set()).add(category)
for atom, category in [('sys-kernel/gentoo-kernel', 'kernel/custom'),
('sys-kernel/gentoo-kernel-bin', 'kernel/binary')]:
coverage.setdefault(atom, set()).add(category)
exceptions = json.loads((repo / 'packages/audit-exceptions.json').read_text())
matched = {atom: sorted(coverage[atom]) for atom in selected if atom in coverage}
manual = {atom: exceptions[atom] for atom in selected if atom in exceptions and atom not in coverage}
missing = sorted(set(selected) - set(matched) - set(manual))
return {'explicit_count': len(selected), 'covered_count': len(matched),
'coverage': matched, 'sources': selected, 'manual_helpers': manual,
'missing': missing, 'unresolved_sets': unresolved}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--world', type=Path, default=Path('/var/lib/portage/world'))
parser.add_argument('--world-sets', type=Path, default=Path('/var/lib/portage/world_sets'))
parser.add_argument('--sets', type=Path, default=Path('/etc/portage/sets'))
parser.add_argument('--json', action='store_true')
args = parser.parse_args()
if not args.world.is_file() and not args.world_sets.is_file():
parser.error('No Portage explicit selection files found.')
result = audit(args.world, args.world_sets, args.sets)
if args.json:
print(json.dumps(result, indent=2))
else:
print(str(result['covered_count']) + '/' + str(result['explicit_count']) + ' explicit selections covered by package lists/kernel choices.')
for atom, reason in result['manual_helpers'].items():
print('Manual helper: ' + atom + ' — ' + reason)
print('Missing main packages: ' + (', '.join(result['missing']) or 'none'))
print('Unresolved selected sets: ' + (', '.join(result['unresolved_sets']) or 'none'))
return 1 if result['missing'] or result['unresolved_sets'] else 0
if __name__ == '__main__':
raise SystemExit(main())

82
tools/build-release.py Normal file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Build the portable Linux amd64 TUI bundle and retain dependency licences."""
import gzip
import json
import os
import re
import subprocess
import sys
import tempfile
import urllib.request
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / 'lib'))
from launcher import digest, source_hash
def json_stream(text):
decoder = json.JSONDecoder()
while text.strip():
item, end = decoder.raw_decode(text.lstrip())
yield item
text = text.lstrip()[end:]
def build():
version = (REPO / 'VERSION').read_text().strip()
if not re.fullmatch(r'\d+\.\d+\.\d+', version):
raise ValueError('VERSION must contain a numeric release version.')
env = dict(os.environ, CGO_ENABLED='0', GOOS='linux', GOARCH='amd64', GOAMD64='v1',
GOEXPERIMENT='', GOFLAGS='-mod=readonly', GOTOOLCHAIN='local')
output = REPO / 'assets/bin'
output.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix='fiw-dots-release-') as temp:
executable = Path(temp) / 'fiw-dots'
subprocess.run(['go', 'build', '-trimpath', '-buildvcs=false',
'-ldflags=-s -w -X main.tuiVersion=' + version,
'-o', str(executable), './cmd/dots'], cwd=REPO, env=env, check=True)
data = executable.read_bytes()
archive = gzip.compress(data, mtime=0)
name = 'fiw-dots-linux-amd64.gz'
metadata = {'version': version, 'source_sha256': source_hash(),
'binaries': {'linux-amd64': {'file': name, 'sha256': digest(data),
'gzip_sha256': digest(archive), 'size': len(data)}}}
packages = subprocess.run(['go', 'list', '-deps', '-json', './cmd/dots'], cwd=REPO, env=env,
text=True, capture_output=True, check=True)
modules = {package['Module']['Path']: package['Module'] for package in json_stream(packages.stdout)
if package.get('Module') and not package['Module'].get('Main')}
notices = ['# Third-party licences for the bundled TUI\n']
for _, module in sorted(modules.items()):
directory = Path(module['Dir'])
licences = sorted(path for path in directory.iterdir() if path.is_file()
and path.name.lower().startswith(('license', 'copying', 'notice')))
if not licences:
raise RuntimeError('Missing licence notice for ' + module['Path'])
for path in licences:
notices += ['\n## ' + module['Path'] + ' ' + module['Version'] + ' / ' + path.name + '\n', path.read_text()]
goroot = subprocess.run(['go', 'env', 'GOROOT'], env=env, text=True, capture_output=True, check=True).stdout.strip()
license_path = Path(goroot) / 'LICENSE'
if license_path.is_file():
go_license = license_path.read_text()
else:
# Gentoo omits the upstream licence file from GOROOT. Obtain that
# exact toolchain's notice from the official Go source repository.
goversion = subprocess.run(['go', 'env', 'GOVERSION'], env=env, text=True, capture_output=True, check=True).stdout.strip()
match = re.match(r'go\d+\.\d+(?:\.\d+)?', goversion)
if not match:
raise RuntimeError('Cannot identify the Go toolchain licence source.')
url = 'https://raw.githubusercontent.com/golang/go/' + match.group() + '/LICENSE'
with urllib.request.urlopen(url, timeout=20) as response:
go_license = response.read().decode()
notices += ['\n## Go standard library and runtime\n', go_license]
(output / name).write_bytes(archive)
(output / 'tui.json').write_text(json.dumps(metadata, indent=2) + '\n')
(output / 'SHA256SUMS').write_text(digest(archive) + ' ' + name + '\n')
(output / 'LICENSES.txt').write_text('\n'.join(notices))
print('Built v' + version + ': ' + str(len(data)) + ' bytes; compressed ' + str(len(archive)) + ' bytes.')
print('Source and binary fingerprints: assets/bin/tui.json')
if __name__ == '__main__':
build()