diff --git a/README.md b/README.md index 2bd4131..c9845ed 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ The existing installation has not been changed by creating this repo. ## Start ```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 ./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. `u` opens config backups for preview and restoration. Apply actions finish 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 -recorded in go.mod/go.sum. Saved selections are reused on subsequent launches. +Linux amd64 includes a verified prebuilt TUI; Git and Python 3.11+ are enough. +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: @@ -96,6 +98,8 @@ in the TUI, grouped with apps and gaming respectively. Limine and GRUB are optional alternatives; retaining the current loader is the default. Kernel choice is independent of bootloader choice. Deployment 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 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/assets/bin/LICENSES.txt b/assets/bin/LICENSES.txt new file mode 100644 index 0000000..07ec6ee --- /dev/null +++ b/assets/bin/LICENSES.txt @@ -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 + +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. diff --git a/assets/bin/SHA256SUMS b/assets/bin/SHA256SUMS new file mode 100644 index 0000000..b07b927 --- /dev/null +++ b/assets/bin/SHA256SUMS @@ -0,0 +1 @@ +767c35da0b46eaa785fa20b2d008e8403aa64e46223e116228d6b4be91e6dfb5 fiw-dots-linux-amd64.gz diff --git a/assets/bin/fiw-dots-linux-amd64.gz b/assets/bin/fiw-dots-linux-amd64.gz new file mode 100644 index 0000000..08ae55c Binary files /dev/null and b/assets/bin/fiw-dots-linux-amd64.gz differ diff --git a/assets/bin/tui.json b/assets/bin/tui.json new file mode 100644 index 0000000..93ded05 --- /dev/null +++ b/assets/bin/tui.json @@ -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 + } + } +} diff --git a/cmd/dots/main.go b/cmd/dots/main.go index 092377c..07d04f7 100644 --- a/cmd/dots/main.go +++ b/cmd/dots/main.go @@ -15,6 +15,8 @@ import ( "github.com/charmbracelet/x/ansi" ) +var tuiVersion = "source" + type selection struct { Name string `json:"name"` Profile string `json:"profile"` @@ -492,6 +494,10 @@ func (m model) View() string { } func main() { + if len(os.Args) == 2 && os.Args[1] == "--version" { + fmt.Println("Fiw-Gentoo-Dots TUI " + tuiVersion) + return + } repo, err := os.Getwd() if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/docs/assets.md b/docs/assets.md index bd34171..5ae2de8 100644 --- a/docs/assets.md +++ b/docs/assets.md @@ -1,5 +1,10 @@ # 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: - Utterly-Round-Dark window decorations retain the original author metadata diff --git a/docs/package-audit.md b/docs/package-audit.md new file mode 100644 index 0000000..118234e --- /dev/null +++ b/docs/package-audit.md @@ -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. diff --git a/docs/releases.md b/docs/releases.md new file mode 100644 index 0000000..acc2fe5 --- /dev/null +++ b/docs/releases.md @@ -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. diff --git a/docs/scripts.md b/docs/scripts.md index 828cfbb..623991f 100644 --- a/docs/scripts.md +++ b/docs/scripts.md @@ -2,7 +2,8 @@ | 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/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 | @@ -12,6 +13,8 @@ | `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/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-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 | diff --git a/docs/setup.md b/docs/setup.md index 98d8c8b..611703b 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -1,7 +1,8 @@ # Restore the setup 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. ## 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 existing loader. -The optional TideWM live recipe still needs a refresh against its upstream -development branch. End-to-end package and boot execution on a fresh Gentoo +The optional [TideWM recipe](tidewm.md) follows the audited upstream master +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, staged Portage configuration and mocked system commands. diff --git a/docs/tidewm.md b/docs/tidewm.md new file mode 100644 index 0000000..d587191 --- /dev/null +++ b/docs/tidewm.md @@ -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). diff --git a/install b/install index c6c3947..a140d2b 100755 --- a/install +++ b/install @@ -5,10 +5,4 @@ if (($#)); then exec python3 "$repo/lib/rice.py" "$@" fi cd "$repo" -if ! command -v go >/dev/null; then - 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 +exec python3 "$repo/lib/launcher.py" diff --git a/lib/launcher.py b/lib/launcher.py new file mode 100644 index 0000000..2949c23 --- /dev/null +++ b/lib/launcher.py @@ -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) diff --git a/lib/rice.py b/lib/rice.py index c3194b4..760fe25 100644 --- a/lib/rice.py +++ b/lib/rice.py @@ -122,7 +122,7 @@ def manual_steps(selection): if 'prism' in selection['configs']: steps.append('Set up Prism accounts, Java and Minecraft instances on this device; see docs/app-configs.md.') 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 @@ -578,6 +578,7 @@ def check_packages(selection): def main(): 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('--profile', choices=['stock', 'fiw-ryzen'], default='stock') parser.add_argument('--home', type=Path, default=Path.home()) diff --git a/lib/setup.py b/lib/setup.py index 10560a7..136e15a 100644 --- a/lib/setup.py +++ b/lib/setup.py @@ -36,7 +36,7 @@ SERVICES = { def required_repositories(selection): 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') if 'gaming' in selection['groups']: names.append('steam-overlay') diff --git a/overlay/gui-wm/tidewm/tidewm-9999.ebuild b/overlay/gui-wm/tidewm/tidewm-9999.ebuild index 351d7f6..61dee57 100644 --- a/overlay/gui-wm/tidewm/tidewm-9999.ebuild +++ b/overlay/gui-wm/tidewm/tidewm-9999.ebuild @@ -3,19 +3,20 @@ 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 DESCRIPTION="Water-themed Wayland tiling compositor built on Smithay" HOMEPAGE="https://github.com/Fi3w0/TideWM" EGIT_REPO_URI="https://github.com/Fi3w0/TideWM.git" -# Development happens on this branch; master is behind it. -EGIT_BRANCH="claude/new-session-bppt7l" +# Follow the upstream default branch. Audited at f86db8bd (2026-10-03). +EGIT_BRANCH="master" LICENSE="GPL-3+" # 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" IUSE="+screencast accessibility" @@ -23,6 +24,7 @@ DEPEND=" dev-libs/expat dev-libs/libinput:= media-libs/mesa + dev-libs/wayland sys-auth/seatd:= virtual/libudev:= x11-libs/libdrm @@ -30,10 +32,14 @@ DEPEND=" screencast? ( media-video/pipewire:= ) " 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) -BDEPEND="screencast? ( llvm-core/clang )" +BDEPEND="virtual/pkgconfig + screencast? ( llvm-core/clang )" QA_FLAGS_IGNORED=".*" @@ -56,11 +62,12 @@ src_configure() { $(usev screencast) $(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() { - 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 doins share/wayland-sessions/tidewm.desktop diff --git a/packages/audit-exceptions.json b/packages/audit-exceptions.json new file mode 100644 index 0000000..fd1aa0b --- /dev/null +++ b/packages/audit-exceptions.json @@ -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." +} diff --git a/packages/cli.list b/packages/cli.list index c3b0170..63eda08 100644 --- a/packages/cli.list +++ b/packages/cli.list @@ -17,3 +17,4 @@ app-portage/portage-utils sys-apps/usbutils sys-apps/hdparm media-video/ffmpeg +x11-misc/ydotool diff --git a/tests/test_release.py b/tests/test_release.py new file mode 100644 index 0000000..c68a6fc --- /dev/null +++ b/tests/test_release.py @@ -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() diff --git a/tests/test_setup.py b/tests/test_setup.py index 44d4979..b2de262 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -23,6 +23,7 @@ boot = module('boot') class SetupTests(unittest.TestCase): 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': ['tidewm']}), ['guru']) self.assertEqual(setup.required_repositories({'groups': ['gaming']}), ['guru', 'steam-overlay']) with tempfile.TemporaryDirectory() as temp: root = Path(temp) diff --git a/tools/audit-packages.py b/tools/audit-packages.py new file mode 100644 index 0000000..9b7a307 --- /dev/null +++ b/tools/audit-packages.py @@ -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()) diff --git a/tools/build-release.py b/tools/build-release.py new file mode 100644 index 0000000..9697043 --- /dev/null +++ b/tools/build-release.py @@ -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()