mirror of
https://git.launchpad.net/livecd-rootfs
synced 2026-02-14 05:53:29 +00:00
Compare commits
101 Commits
25.10.18
...
ubuntu/mas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3645bdf230 | ||
|
|
c3671c739d | ||
|
|
733ad14e33 | ||
|
|
e26de340e2 | ||
|
|
7f1c505f20 | ||
|
|
6d954c975d | ||
|
|
73035c0b19 | ||
|
|
84760de4da | ||
|
|
2c2f7d5e5c | ||
|
|
45aa1e4550 | ||
|
|
c1edc22c24 | ||
|
|
9add6d4ab8 | ||
|
|
acd63ee3e4 | ||
|
|
ab2b82e3c2 | ||
|
|
9a9ca07a76 | ||
|
|
4d8cfd89b8 | ||
|
|
ce809612c4 | ||
|
|
b3fdc4e615 | ||
|
|
3112c5f175 | ||
|
|
8e26b08f59 | ||
|
|
7cbabf55d5 | ||
|
|
ddbf8bf828 | ||
|
|
74f5986230 | ||
|
|
563d142029 | ||
|
|
755f0b0d15 | ||
|
|
d756afd205 | ||
|
|
9c5d326e56 | ||
|
|
383a1206cc | ||
|
|
2f918331fb | ||
|
|
e6558e2541 | ||
|
|
01c80d8d0a | ||
|
|
72511a0381 | ||
|
|
c147c15291 | ||
|
|
856f14edee | ||
|
|
caf4f1030a | ||
|
|
49e1ab15cd | ||
|
|
5cbea9f677 | ||
|
|
a19f30b9d6 | ||
|
|
44c14b799f | ||
|
|
9fdbaf8d6d | ||
|
|
ae1e5005aa | ||
|
|
c327ab7bd7 | ||
|
|
ab943acf44 | ||
|
|
827d87bd7f | ||
|
|
562e589cd1 | ||
|
|
65dad6ccc0 | ||
|
|
0fc035c8ba | ||
|
|
e5ef47f7dd | ||
|
|
69ee041674 | ||
|
|
e78505a5f3 | ||
|
|
544aa0299e | ||
|
|
98c75ef41b | ||
|
|
5e00e3ecb2 | ||
|
|
d280d58a7d | ||
|
|
e2c8b4b1ad | ||
|
|
b3ddf6a78a | ||
|
|
218ad9af27 | ||
|
|
befd8ddadf | ||
|
|
21372df0f9 | ||
|
|
001aed3b3b | ||
|
|
9164e58d83 | ||
|
|
18c3471930 | ||
|
|
466d9683c5 | ||
|
|
ec74e75597 | ||
|
|
432785a2fe | ||
|
|
596d6d8464 | ||
|
|
1fca197379 | ||
|
|
f6e3d2aedd | ||
|
|
918bc13a3a | ||
|
|
92471dcb76 | ||
|
|
a53da7e27b | ||
|
|
922faa0d12 | ||
|
|
e0f4fd8109 | ||
|
|
78d502951b | ||
|
|
287bf91450 | ||
|
|
ff6b3824d8 | ||
|
|
ddff3faba3 | ||
|
|
9172378dae | ||
|
|
e35eb63edc | ||
|
|
82a239e39c | ||
|
|
b8520530c9 | ||
|
|
7957008902 | ||
|
|
4b4d3de818 | ||
|
|
355f6d5b26 | ||
|
|
39e1066593 | ||
|
|
12545fb878 | ||
|
|
f7e0f39a1d | ||
|
|
f7ed2d271e | ||
|
|
ae0be803f1 | ||
|
|
ab658bce7d | ||
|
|
1501b3776c | ||
|
|
8de7b2eb10 | ||
|
|
2fd6cb1609 | ||
|
|
6dbce04781 | ||
|
|
785c4c53d3 | ||
|
|
762108eaef | ||
|
|
fddd7b7595 | ||
|
|
2aeaaf1815 | ||
|
|
ecaaf04844 | ||
|
|
eda621a927 | ||
|
|
12c92b2cbf |
12
.launchpad.yaml
Normal file
12
.launchpad.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pipeline:
|
||||||
|
- [lint]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
series: noble
|
||||||
|
architectures: amd64
|
||||||
|
packages:
|
||||||
|
- black
|
||||||
|
- mypy
|
||||||
|
- python3-flake8
|
||||||
|
run: ./check-lint
|
||||||
236
README.parameters
Normal file
236
README.parameters
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
Understanding the parameters used by livecd-rootfs
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
livecd-rootfs is a confusing codebase. One of the confusing things is
|
||||||
|
how information flows into and around the image build process. There
|
||||||
|
is IMAGEFORMAT and IMAGE_TARGETS and PROJECT and many other
|
||||||
|
variables. It is not obvious when looking at the code if a given
|
||||||
|
variable is something passed as a parameter or something derived from
|
||||||
|
it.
|
||||||
|
|
||||||
|
All (or almost all) production use of livecd-rootfs is via
|
||||||
|
launchpad-buildd so the set of potential parameters is limited by the
|
||||||
|
set of environment variables launchpad-build can set in response to
|
||||||
|
the build request.
|
||||||
|
|
||||||
|
The process from build request to environment live-build is run is a
|
||||||
|
little convoluted. The build request takes:
|
||||||
|
|
||||||
|
an archive -- where to get livecd-rootfs from
|
||||||
|
a distro_arch_series -- the series to get livecd-rootfs and build
|
||||||
|
a pocket -- pocket to get livecd-rootfs from, also influences if proposed is
|
||||||
|
used as a package source for the image being built
|
||||||
|
unique_key -- you cannot have more than one pending livefs build with the same
|
||||||
|
unique_key. does not affect the build at all.
|
||||||
|
version -- optional version string, see below. often a serial like 20250525.1
|
||||||
|
metadata_override -- combined with the metadata on the livefs itself to make
|
||||||
|
the metadata for this build.
|
||||||
|
|
||||||
|
(ref: https://launchpad.net/+apidoc/devel.html#livefs-requestBuild)
|
||||||
|
|
||||||
|
These parameters are stored on the livefsbuild object (ref:
|
||||||
|
https://git.launchpad.net/launchpad/tree/lib/lp/soyuz/model/livefsbuild.py#n372)
|
||||||
|
and converted into a set of args passed to launchpad-build by the
|
||||||
|
LiveFSBuildBehaviour class (ref:
|
||||||
|
https://git.launchpad.net/launchpad/tree/lib/lp/soyuz/model/livefsbuildbehaviour.py#n99).
|
||||||
|
|
||||||
|
Inside launchpad-build, these arguments are inspected by the
|
||||||
|
LiveFilesystemBuildManager.initiate method (ref:
|
||||||
|
https://git.launchpad.net/launchpad-buildd/tree/lpbuildd/livefs.py#n24)
|
||||||
|
which turns them into arguments for the BuildLiveFS lpbuild
|
||||||
|
"operation" which is what creates the environment live-build runs in
|
||||||
|
(ref:
|
||||||
|
https://git.launchpad.net/launchpad-buildd/tree/lpbuildd/target/build_livefs.py#n167).
|
||||||
|
|
||||||
|
These variables can be set for both lb config and lb build:
|
||||||
|
|
||||||
|
PROJECT (mandatory, comes from "project" in the metadata)
|
||||||
|
ARCH (set to the abi tag of the distroarchseries being built for)
|
||||||
|
SUBPROJECT (optional, comes from "subproject" in the metadata)
|
||||||
|
SUBARCH (optional, comes from "subarch" in the metadata)
|
||||||
|
CHANNEL (optional, comes from "subarch" in the metadata)
|
||||||
|
IMAGE_TARGETS (optional, comes from "image_targets" in the metadata
|
||||||
|
"image_targets" is a list. IMAGE_TARGETS is set to " ".join(image_targets))
|
||||||
|
REPO_SNAPSHOT_STAMP
|
||||||
|
(optional, comes from "repo_snapshot_stamp" in the metadata)
|
||||||
|
SNAPSHOT_SERVICE_TIMESTAMP
|
||||||
|
(optional, comes from "snapshot_snapshot_stamp" in the metadata)
|
||||||
|
COHORT_KEY
|
||||||
|
(optional, comes from "cohort-key" in the metadata)
|
||||||
|
|
||||||
|
launchpad-buildd also contains code to set http_proxy / HTTP_PROXY /
|
||||||
|
LB_APT_HTTP_PROXY but there does not appear to be any way to trigger
|
||||||
|
this when requesting a build.
|
||||||
|
|
||||||
|
In addition the following variables can be set for lb config only (why
|
||||||
|
are some things set for lb config only? no idea):
|
||||||
|
|
||||||
|
SUITE (set to the name of the distroarchseries being built for)
|
||||||
|
NOW (set to value of the 'version' argument to the build request,
|
||||||
|
defaults to strftime("%Y%m%d-%H%M%S"))
|
||||||
|
IMAGEFORMAT (optional, comes from "image_format" in the metadata)
|
||||||
|
PROPOSED (set to "1" if the pocket passed to the build request is proposed)
|
||||||
|
EXTRA_PPAS (optional, comes from "extra_ppas" in the metadata
|
||||||
|
"extra_ppas" is a list. EXTRA_PPAS is set to " ".join(extra_ppas))
|
||||||
|
EXTRA_SNAPS (optional, comes from "extra_snaps" in the metadata
|
||||||
|
"extra_snaps" is a list. EXTRA_SNAPS is set to " ".join(extra_snaps))
|
||||||
|
|
||||||
|
Here is an opinionated and slightly angry attempt to describe what
|
||||||
|
each of these is for:
|
||||||
|
|
||||||
|
PROJECT
|
||||||
|
-------
|
||||||
|
|
||||||
|
This is the big one, the main variable that defines what is being
|
||||||
|
built. It can be ubuntu, ubuntu-server, xubuntu, ubuntu-mini-iso, that
|
||||||
|
sort of thing. Generally PROJECT determines the set of packages
|
||||||
|
installed but it (unfortunately?) has a bit more impact than that.
|
||||||
|
|
||||||
|
It's unarguable that we need a parameter like this.
|
||||||
|
|
||||||
|
ARCH
|
||||||
|
----
|
||||||
|
|
||||||
|
The architecture being built for. This is always the same as `dpkg
|
||||||
|
--print-architecture` for us, we don't do any cross builds.
|
||||||
|
|
||||||
|
It's kind of redundant but it's not really a problem that this exists.
|
||||||
|
|
||||||
|
SUBPROJECT
|
||||||
|
----------
|
||||||
|
|
||||||
|
This is used for some builds to build a different sort of build of the
|
||||||
|
project. It can be set to:
|
||||||
|
|
||||||
|
* "minimized" for ubuntu-cpc builds to make a minimal cloud image
|
||||||
|
* "minimal" for xubuntu builds to make a smaller ISO
|
||||||
|
* "desktop-preinstalled" for ubuntu builds to make a preinstalled
|
||||||
|
image instead of the parts for an installer.
|
||||||
|
* "buildd" for images to be used as build images by craft tools, and also
|
||||||
|
buildd chroots used on launchpad builders?
|
||||||
|
* "live" for ubuntu-server builds, historically to distinguish d-i
|
||||||
|
style installers from subiquity style installers
|
||||||
|
* "desktop" for ubuntu-core-installer builds, to influence which
|
||||||
|
model is use to build the ubuntu core system that will be
|
||||||
|
installed.
|
||||||
|
|
||||||
|
_This_ parameter is a total mess. The desktop-preinstalled use feels
|
||||||
|
particularly egregious.
|
||||||
|
|
||||||
|
SUBARCH
|
||||||
|
-------
|
||||||
|
|
||||||
|
This identifies the target machine more specifically than ARCH,
|
||||||
|
e.g. "tegra-jetson" or "licheerv". Used mostly but not exclusively for
|
||||||
|
preinstalled builds.
|
||||||
|
|
||||||
|
We probably do need something like this.
|
||||||
|
|
||||||
|
CHANNEL
|
||||||
|
-------
|
||||||
|
|
||||||
|
Influences which channel snaps included in the build are taken from
|
||||||
|
(via a few different mechanisms).
|
||||||
|
|
||||||
|
IMAGE_TARGETS
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Passed for CPC (and ubuntu-oem, for some reason) builds to
|
||||||
|
`config/hooks.d/make-hooks` which uses it to select which binary hooks
|
||||||
|
to run (and so determines which artifacts get produced).
|
||||||
|
|
||||||
|
It is probably reasonable that this exists.
|
||||||
|
|
||||||
|
REPO_SNAPSHOT_STAMP
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Currently unused.
|
||||||
|
|
||||||
|
SNAPSHOT_SERVICE_TIMESTAMP
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Also currently unused, and unclear how it differs from
|
||||||
|
REPO_SNAPSHOT_STAMP.
|
||||||
|
|
||||||
|
COHORT_KEY
|
||||||
|
----------
|
||||||
|
|
||||||
|
Used to make sure that different builds run at the same time don't get
|
||||||
|
different versions of snaps due to phasing differences.
|
||||||
|
|
||||||
|
This is a totally valid thing to need to supply.
|
||||||
|
|
||||||
|
http_proxy / HTTP_PROXY / LB_APT_HTTP_PROXY
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Nothing complex here!
|
||||||
|
|
||||||
|
SUITE
|
||||||
|
-----
|
||||||
|
|
||||||
|
This is the series being built (e.g. noble, questing). It is misnamed
|
||||||
|
really -- a suite is usually a combination of a series and a pocket
|
||||||
|
(noble-proposed, questing-security).
|
||||||
|
|
||||||
|
As with ARCH this is sort of redundant as we do builds in a chroot of
|
||||||
|
the series being built but OTOH it is definitely information the
|
||||||
|
build needs to know!
|
||||||
|
|
||||||
|
NOW
|
||||||
|
---
|
||||||
|
|
||||||
|
The serial for the build, e.g. 20250519 or 20240418.4.
|
||||||
|
|
||||||
|
It is a totally reasonable parameter.
|
||||||
|
|
||||||
|
IMAGEFORMAT
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This is one of the more incoherently handled parameters. In rough
|
||||||
|
outline it is the filesystem of the image we produce.
|
||||||
|
|
||||||
|
Installer builds do not produce raw images, so this ends up being set
|
||||||
|
to 'plain' (which causes live-build to just leave the rootfs as a
|
||||||
|
directory tree) or 'none' (which causes live-build to do roughly the
|
||||||
|
same thing but in a different way?).
|
||||||
|
|
||||||
|
Image builds that use ubuntu-image set it to "ubuntu-image". These
|
||||||
|
builds do not call 'lb build' or 'lb binary'.
|
||||||
|
|
||||||
|
Other preinstalled images (mostly cpc images) set it to ext4 (but then
|
||||||
|
use live-build/ubuntu-cpc/hooks.d/remove-implicit-artifacts to remove
|
||||||
|
the output file that this causes live-build to produce...). Some
|
||||||
|
projects rely on this being set via metadata when building the project
|
||||||
|
it seems.
|
||||||
|
|
||||||
|
It can be set when starting an image build, but most builds do not and
|
||||||
|
the behavior when it is not set explicitly is pretty confusing.
|
||||||
|
|
||||||
|
This place is not a place of honor.
|
||||||
|
|
||||||
|
PROPOSED
|
||||||
|
--------
|
||||||
|
|
||||||
|
Should packages from proposed by included?
|
||||||
|
|
||||||
|
This is not really as useful as it used to be for a bunch of reasons
|
||||||
|
but it conceptually makes sense.
|
||||||
|
|
||||||
|
EXTRA_PPAS
|
||||||
|
----------
|
||||||
|
|
||||||
|
Extra archives to get packages from.
|
||||||
|
|
||||||
|
This is a space separated list by the time it gets to
|
||||||
|
livecd-rootfs. Each element of the list is of the form USER/NAME[:PIN]
|
||||||
|
where user is a Launchpad user/team name, NAME is the name of the ppa
|
||||||
|
to add and the optional colon-PIN at the end is the value to pin (in
|
||||||
|
the "man 5 apt_preferences: sense) packages from this PPA at.
|
||||||
|
|
||||||
|
Production builds shouldn't really use this but it's definitely useful
|
||||||
|
for development.
|
||||||
|
|
||||||
|
EXTRA_SNAPS
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Extra snaps to include (but only for ubuntu-image based builds).
|
||||||
11
check-lint
Executable file
11
check-lint
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
export MYPYPATH=live-build
|
||||||
|
mypy live-build/isobuilder live-build/isobuild
|
||||||
|
mypy live-build/gen-iso-ids
|
||||||
|
|
||||||
|
black --check live-build/isobuilder live-build/isobuild live-build/gen-iso-ids
|
||||||
|
|
||||||
|
python3 -m flake8 --max-line-length 88 --ignore E203 live-build/isobuilder live-build/isobuild live-build/gen-iso-ids
|
||||||
208
debian/changelog
vendored
208
debian/changelog
vendored
@ -1,3 +1,211 @@
|
|||||||
|
livecd-rootfs (26.04.17) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: build the stable ISO using the stable model - essentially
|
||||||
|
reverting all the hacks.
|
||||||
|
* desktop: update the stable model to the latest. It has:
|
||||||
|
- components defined for the 6.19 kernel (nvidia 580 series)
|
||||||
|
- no core26: for TPM/FDE recovery testing, please install the core26 snap
|
||||||
|
from edge.
|
||||||
|
|
||||||
|
-- Olivier Gayot <olivier.gayot@canonical.com> Thu, 12 Feb 2026 10:25:15 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.16) resolute; urgency=medium
|
||||||
|
|
||||||
|
* Rename ISO_STATUS to BUILD_TYPE for image builds.
|
||||||
|
|
||||||
|
-- Utkarsh Gupta <utkarsh@debian.org> Thu, 12 Feb 2026 01:41:11 +0530
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.15) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Matthew Hagemann ]
|
||||||
|
* desktop: delay display manager starting until snapd seeding completes
|
||||||
|
|
||||||
|
[ Michael Hudson-Doyle ]
|
||||||
|
* Make an ISO in the livefs build when building an installer.
|
||||||
|
|
||||||
|
-- Michael Hudson-Doyle <michael.hudson@ubuntu.com> Wed, 11 Feb 2026 10:04:37 +1300
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.14) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Olivier Gayot ]
|
||||||
|
* desktop: build stable image with snapd from beta. Snapd 2.74 has just been
|
||||||
|
uploaded to beta. Let's stop using the version declared in the dangerous model.
|
||||||
|
|
||||||
|
[ Didier Roche-Tolomelli ]
|
||||||
|
* desktop: add (commented out) config to force reexecution of snapd snap version
|
||||||
|
|
||||||
|
-- Olivier Gayot <olivier.gayot@canonical.com> Thu, 22 Jan 2026 10:13:36 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.13) resolute; urgency=medium
|
||||||
|
|
||||||
|
* Bootstrap and install variant packages if ARCH_VARIANT is set.
|
||||||
|
|
||||||
|
-- Michael Hudson-Doyle <michael.hudson@ubuntu.com> Tue, 06 Jan 2026 22:03:15 +1300
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.12) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: add variables pointing to the different models (stable & dangerous).
|
||||||
|
* desktop: fix snap components taken from original model when overriding a
|
||||||
|
snap with another model.
|
||||||
|
- if we decide to override the definition of a snap (i.e., by taking in
|
||||||
|
from a different model), we also need to override the definition of its
|
||||||
|
components.
|
||||||
|
* desktop: refactor how we filter the snaps when overriding
|
||||||
|
* desktop: update the dangerous model so that it includes core26 and the 6.17
|
||||||
|
kernel and components.
|
||||||
|
|
||||||
|
-- Olivier Gayot <olivier.gayot@canonical.com> Tue, 16 Dec 2025 14:54:17 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.11) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Valentin Haudiquet ]
|
||||||
|
* refactor: added a function to generate grub config for netboot
|
||||||
|
* riscv/server: add grub efi bootloader in netboot tarballs
|
||||||
|
|
||||||
|
[ Olivier Gayot ]
|
||||||
|
* desktop: build with optional components included
|
||||||
|
* desktop: don't build the stable image with pc-kernel from 26.04/beta
|
||||||
|
- This was needed before because there was pc-kernel in 26.04/stable but
|
||||||
|
now there is one and it matches the components definition from the model.
|
||||||
|
|
||||||
|
-- Valentin Haudiquet <valentin.haudiquet@canonical.com> Thu, 11 Dec 2025 09:28:37 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.10) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Olivier Gayot ]
|
||||||
|
* desktop: fix a comment typo
|
||||||
|
|
||||||
|
[ Michael Hudson-Doyle ]
|
||||||
|
* Build Ubuntu Server images with the 'restricted' component enabled.
|
||||||
|
|
||||||
|
-- Dan Bungert <daniel.bungert@canonical.com> Tue, 09 Dec 2025 21:07:54 +1300
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.9) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: Add mesa to the hybrid model required by core24 apps.
|
||||||
|
|
||||||
|
-- Didier Roche-Tolomelli <didrocks@ubuntu.com> Wed, 26 Nov 2025 08:51:24 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.8) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: update TPM/FDE ubuntu model to use a GNOME platform snap
|
||||||
|
compatible with core24.
|
||||||
|
|
||||||
|
-- Didier Roche-Tolomelli <didrocks@ubuntu.com> Tue, 25 Nov 2025 11:38:59 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.7) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: build both ISOs with snapd, firmware-updater and
|
||||||
|
desktop-security-center from edge for TPM/FDE.
|
||||||
|
|
||||||
|
-- Olivier Gayot <olivier.gayot@canonical.com> Wed, 19 Nov 2025 10:41:17 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.6) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: build classic ISO with "grade: dangerous" and pull pc-kernel from
|
||||||
|
edge to workaround unavailability of pc-kernel in the stable 26.04 channel.
|
||||||
|
* desktop: refresh models to 26.04
|
||||||
|
|
||||||
|
-- Olivier Gayot <olivier.gayot@canonical.com> Tue, 18 Nov 2025 17:01:47 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.5) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Dan Bungert ]
|
||||||
|
* desktop: fix build error when SUBPROJECT is unset
|
||||||
|
|
||||||
|
-- Sebastien Bacher <seb128@ubuntu.com> Mon, 17 Nov 2025 12:05:26 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.4) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: update dangerous model for 26.04
|
||||||
|
|
||||||
|
-- Dan Bungert <daniel.bungert@canonical.com> Fri, 31 Oct 2025 11:13:53 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.3) resolute; urgency=medium
|
||||||
|
|
||||||
|
* desktop: use dangerous model for TPMFDE bits until snaps are available on
|
||||||
|
stable channels.
|
||||||
|
* desktop: use snapd from edge.
|
||||||
|
|
||||||
|
-- Dan Bungert <daniel.bungert@canonical.com> Wed, 29 Oct 2025 10:58:00 +0100
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.2) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Gauthier Jolly ]
|
||||||
|
* ubuntu-cpc:
|
||||||
|
- Use the right specific UUID type for the root filesystem partition.
|
||||||
|
- Set a PARTLABEL (cloudimg-rootfs) on the root filesystem partition.
|
||||||
|
|
||||||
|
-- Gauthier Jolly <gauthier.jolly@canonical.com> Thu, 23 Oct 2025 12:50:07 +1300
|
||||||
|
|
||||||
|
livecd-rootfs (26.04.1) resolute; urgency=medium
|
||||||
|
|
||||||
|
[ Heinrich Schuchardt ]
|
||||||
|
* Remove unused riscv64 SUBARCHs
|
||||||
|
|
||||||
|
[ Chad Smith ]
|
||||||
|
* Refresh cloud-init service override for updated service netcat invocation
|
||||||
|
to cloud-init 25.3. (LP: #2128887)
|
||||||
|
|
||||||
|
-- Chad Smith <chad.smith@canonical.com> Mon, 20 Oct 2025 16:32:36 -0600
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.24) questing; urgency=medium
|
||||||
|
|
||||||
|
[ Chad Smith ]
|
||||||
|
* Limit permissions for /etc/netplan/01-network-manager.yaml to
|
||||||
|
root read-write. (LP: #2119020)
|
||||||
|
|
||||||
|
[ Chloé 'kajiya' Smith ]
|
||||||
|
* Increase CPC disk-image base imagesize to 2.5GB
|
||||||
|
* In the ubuntu-cpc disk-image binary we need to avail of the ever increasing size
|
||||||
|
of packages. 2.2GB is now just a bit too small leading to `No space
|
||||||
|
left on device` errors when the binary hits `grub-install`. This change
|
||||||
|
increases $imagesize to 2.5GB (in the binary as an override, initially
|
||||||
|
implemented in ecaaf0484 by dlalaj). Also now run `df` just after the
|
||||||
|
grub-pc && grub2-common installs to make for easier debugging in the future.
|
||||||
|
(LP: #2115811)
|
||||||
|
|
||||||
|
-- Dan Bungert <daniel.bungert@canonical.com> Fri, 19 Sep 2025 13:47:20 -0600
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.23) questing; urgency=medium
|
||||||
|
|
||||||
|
* Add 6.17 kernel apparmor features' preseeds.
|
||||||
|
|
||||||
|
-- Thomas Bechtold <thomasbechtold@jpberlin.de> Thu, 18 Sep 2025 13:29:42 +0200
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.22) questing; urgency=medium
|
||||||
|
|
||||||
|
* Disable apparmor_restrict_unprivileged_userns in the live layers.
|
||||||
|
(LP: #2122675)
|
||||||
|
|
||||||
|
-- Michael Hudson-Doyle <michael.hudson@ubuntu.com> Tue, 16 Sep 2025 08:51:02 +1200
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.21) questing; urgency=medium
|
||||||
|
|
||||||
|
* Fix daily-dangerous builds:
|
||||||
|
- Copy hooks.
|
||||||
|
- Mangle the channel of seeded snaps to use the edge risk of whichever
|
||||||
|
track they are taken from.
|
||||||
|
- Update the dangerous model to reference tracks that actually exist.
|
||||||
|
- Include providers of content plugs when seeding snaps and creating
|
||||||
|
TPMFDE system.
|
||||||
|
- Do not attempt to build an UEFI boot image or hyperv desktop image for
|
||||||
|
this project/subproject combination.
|
||||||
|
|
||||||
|
-- Michael Hudson-Doyle <michael.hudson@ubuntu.com> Mon, 15 Sep 2025 12:16:08 +1200
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.20) questing; urgency=medium
|
||||||
|
|
||||||
|
* edubuntu: use dracut
|
||||||
|
|
||||||
|
-- Dan Bungert <daniel.bungert@canonical.com> Wed, 10 Sep 2025 17:15:45 -0600
|
||||||
|
|
||||||
|
livecd-rootfs (25.10.19) questing; urgency=medium
|
||||||
|
|
||||||
|
* Increase default image size for buildd.
|
||||||
|
|
||||||
|
-- Denis Lalaj <denis.lalaj@canonical.com> Tue, 09 Sep 2025 16:53:52 -0600
|
||||||
|
|
||||||
livecd-rootfs (25.10.18) questing; urgency=medium
|
livecd-rootfs (25.10.18) questing; urgency=medium
|
||||||
|
|
||||||
* desktop: use dracut
|
* desktop: use dracut
|
||||||
|
|||||||
2
debian/control
vendored
2
debian/control
vendored
@ -25,6 +25,7 @@ Depends: ${misc:Depends},
|
|||||||
git,
|
git,
|
||||||
gnupg,
|
gnupg,
|
||||||
grep-dctrl,
|
grep-dctrl,
|
||||||
|
jq,
|
||||||
kpartx,
|
kpartx,
|
||||||
live-build (>= 3.0~a57-1ubuntu31~),
|
live-build (>= 3.0~a57-1ubuntu31~),
|
||||||
lsb-release,
|
lsb-release,
|
||||||
@ -36,6 +37,7 @@ Depends: ${misc:Depends},
|
|||||||
procps,
|
procps,
|
||||||
python3,
|
python3,
|
||||||
python3-apt,
|
python3-apt,
|
||||||
|
python3-click,
|
||||||
python3-launchpadlib [!i386],
|
python3-launchpadlib [!i386],
|
||||||
python3-yaml,
|
python3-yaml,
|
||||||
qemu-utils [!i386],
|
qemu-utils [!i386],
|
||||||
|
|||||||
1
live-build/apparmor/6.17/capability
Normal file
1
live-build/apparmor/6.17/capability
Normal file
@ -0,0 +1 @@
|
|||||||
|
0xffffff
|
||||||
1
live-build/apparmor/6.17/caps/extended
Normal file
1
live-build/apparmor/6.17/caps/extended
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/caps/mask
Normal file
1
live-build/apparmor/6.17/caps/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf checkpoint_restore
|
||||||
1
live-build/apparmor/6.17/dbus/mask
Normal file
1
live-build/apparmor/6.17/dbus/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
acquire send receive
|
||||||
1
live-build/apparmor/6.17/domain/attach_conditions/xattr
Normal file
1
live-build/apparmor/6.17/domain/attach_conditions/xattr
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/change_hat
Normal file
1
live-build/apparmor/6.17/domain/change_hat
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/change_hatv
Normal file
1
live-build/apparmor/6.17/domain/change_hatv
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/change_onexec
Normal file
1
live-build/apparmor/6.17/domain/change_onexec
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/change_profile
Normal file
1
live-build/apparmor/6.17/domain/change_profile
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/computed_longest_left
Normal file
1
live-build/apparmor/6.17/domain/computed_longest_left
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/disconnected.ipc
Normal file
1
live-build/apparmor/6.17/domain/disconnected.ipc
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/disconnected.path
Normal file
1
live-build/apparmor/6.17/domain/disconnected.path
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/fix_binfmt_elf_mmap
Normal file
1
live-build/apparmor/6.17/domain/fix_binfmt_elf_mmap
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/interruptible
Normal file
1
live-build/apparmor/6.17/domain/interruptible
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/kill.signal
Normal file
1
live-build/apparmor/6.17/domain/kill.signal
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/post_nnp_subset
Normal file
1
live-build/apparmor/6.17/domain/post_nnp_subset
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/stack
Normal file
1
live-build/apparmor/6.17/domain/stack
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/domain/version
Normal file
1
live-build/apparmor/6.17/domain/version
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.2
|
||||||
1
live-build/apparmor/6.17/file/mask
Normal file
1
live-build/apparmor/6.17/file/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
create read write exec append mmap_exec link lock
|
||||||
1
live-build/apparmor/6.17/io_uring/mask
Normal file
1
live-build/apparmor/6.17/io_uring/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
sqpoll override_creds
|
||||||
1
live-build/apparmor/6.17/ipc/posix_mqueue
Normal file
1
live-build/apparmor/6.17/ipc/posix_mqueue
Normal file
@ -0,0 +1 @@
|
|||||||
|
create read write open delete setattr getattr label
|
||||||
1
live-build/apparmor/6.17/mount/mask
Normal file
1
live-build/apparmor/6.17/mount/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
mount umount pivot_root
|
||||||
1
live-build/apparmor/6.17/mount/move_mount
Normal file
1
live-build/apparmor/6.17/mount/move_mount
Normal file
@ -0,0 +1 @@
|
|||||||
|
detached
|
||||||
1
live-build/apparmor/6.17/namespaces/mask
Normal file
1
live-build/apparmor/6.17/namespaces/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
userns_create
|
||||||
1
live-build/apparmor/6.17/namespaces/pivot_root
Normal file
1
live-build/apparmor/6.17/namespaces/pivot_root
Normal file
@ -0,0 +1 @@
|
|||||||
|
no
|
||||||
1
live-build/apparmor/6.17/namespaces/profile
Normal file
1
live-build/apparmor/6.17/namespaces/profile
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/namespaces/userns_create
Normal file
1
live-build/apparmor/6.17/namespaces/userns_create
Normal file
@ -0,0 +1 @@
|
|||||||
|
pciu&
|
||||||
1
live-build/apparmor/6.17/network/af_mask
Normal file
1
live-build/apparmor/6.17/network/af_mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp mctp
|
||||||
1
live-build/apparmor/6.17/network/af_unix
Normal file
1
live-build/apparmor/6.17/network/af_unix
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/network_v8/af_inet
Normal file
1
live-build/apparmor/6.17/network_v8/af_inet
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/network_v8/af_mask
Normal file
1
live-build/apparmor/6.17/network_v8/af_mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp mctp
|
||||||
1
live-build/apparmor/6.17/network_v9/af_mask
Normal file
1
live-build/apparmor/6.17/network_v9/af_mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp mctp
|
||||||
1
live-build/apparmor/6.17/network_v9/af_unix
Normal file
1
live-build/apparmor/6.17/network_v9/af_unix
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/metadata_tagging_version
Normal file
1
live-build/apparmor/6.17/policy/metadata_tagging_version
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x000001
|
||||||
1
live-build/apparmor/6.17/policy/notify/user
Normal file
1
live-build/apparmor/6.17/policy/notify/user
Normal file
@ -0,0 +1 @@
|
|||||||
|
file tags
|
||||||
1
live-build/apparmor/6.17/policy/notify_versions/v3
Normal file
1
live-build/apparmor/6.17/policy/notify_versions/v3
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/notify_versions/v5
Normal file
1
live-build/apparmor/6.17/policy/notify_versions/v5
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/outofband
Normal file
1
live-build/apparmor/6.17/policy/outofband
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x000001
|
||||||
1
live-build/apparmor/6.17/policy/permstable32
Normal file
1
live-build/apparmor/6.17/policy/permstable32
Normal file
@ -0,0 +1 @@
|
|||||||
|
allow deny subtree cond kill complain prompt audit quiet hide xindex tag label
|
||||||
1
live-build/apparmor/6.17/policy/permstable32_version
Normal file
1
live-build/apparmor/6.17/policy/permstable32_version
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x000003
|
||||||
1
live-build/apparmor/6.17/policy/set_load
Normal file
1
live-build/apparmor/6.17/policy/set_load
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/state32
Normal file
1
live-build/apparmor/6.17/policy/state32
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x000001
|
||||||
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
1
live-build/apparmor/6.17/policy/versions/v5
Normal file
1
live-build/apparmor/6.17/policy/versions/v5
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/versions/v6
Normal file
1
live-build/apparmor/6.17/policy/versions/v6
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/versions/v7
Normal file
1
live-build/apparmor/6.17/policy/versions/v7
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/versions/v8
Normal file
1
live-build/apparmor/6.17/policy/versions/v8
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/policy/versions/v9
Normal file
1
live-build/apparmor/6.17/policy/versions/v9
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/ptrace/mask
Normal file
1
live-build/apparmor/6.17/ptrace/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
read trace
|
||||||
1
live-build/apparmor/6.17/query/label/data
Normal file
1
live-build/apparmor/6.17/query/label/data
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/query/label/multi_transaction
Normal file
1
live-build/apparmor/6.17/query/label/multi_transaction
Normal file
@ -0,0 +1 @@
|
|||||||
|
yes
|
||||||
1
live-build/apparmor/6.17/query/label/perms
Normal file
1
live-build/apparmor/6.17/query/label/perms
Normal file
@ -0,0 +1 @@
|
|||||||
|
allow deny audit quiet
|
||||||
1
live-build/apparmor/6.17/rlimit/mask
Normal file
1
live-build/apparmor/6.17/rlimit/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime
|
||||||
1
live-build/apparmor/6.17/signal/mask
Normal file
1
live-build/apparmor/6.17/signal/mask
Normal file
@ -0,0 +1 @@
|
|||||||
|
hup int quit ill trap abrt bus fpe kill usr1 segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg xcpu xfsz vtalrm prof winch io pwr sys emt lost
|
||||||
@ -119,6 +119,12 @@ Expire-Date: 0
|
|||||||
exit $ec
|
exit $ec
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "${ARCH_VARIANT:-}" ]; then
|
||||||
|
cat > chroot/etc/apt/apt.conf.d/90arch-variant <<EOF
|
||||||
|
APT::Architecture-Variants "${ARCH_VARIANT}";
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
# Set locale to C.UTF-8 by default. This may be overridden later.
|
# Set locale to C.UTF-8 by default. This may be overridden later.
|
||||||
echo "LANG=C.UTF-8" > chroot/etc/default/locale
|
echo "LANG=C.UTF-8" > chroot/etc/default/locale
|
||||||
|
|
||||||
@ -202,6 +208,22 @@ EOF
|
|||||||
undivert_update_initramfs
|
undivert_update_initramfs
|
||||||
undivert_grub chroot
|
undivert_grub chroot
|
||||||
fi
|
fi
|
||||||
|
if [ "${MAKE_ISO}" = yes ]; then
|
||||||
|
isobuild init --disk-info "$(cat config/iso-ids/disk-info)" --series "${LB_DISTRIBUTION}" --arch "${ARCH}"
|
||||||
|
# Determine which chroot directory has the apt configuration to use.
|
||||||
|
# Layered builds (PASSES set) create overlay directories named
|
||||||
|
# "overlay.base", "overlay.live", etc. - we use the first one (base).
|
||||||
|
# Single-pass builds use the "chroot" directory directly.
|
||||||
|
if [ "${PASSES}" ]; then
|
||||||
|
CHROOT="overlay.$(set -- $PASSES; echo $1)"
|
||||||
|
else
|
||||||
|
CHROOT=chroot
|
||||||
|
fi
|
||||||
|
isobuild setup-apt --chroot $CHROOT
|
||||||
|
if [ -n "${POOL_SEED_NAME}" ]; then
|
||||||
|
isobuild generate-pool --package-list-file "config/germinate-output/${POOL_SEED_NAME}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d chroot/etc/apt/preferences.d.save ]; then
|
if [ -d chroot/etc/apt/preferences.d.save ]; then
|
||||||
# https://mastodon.social/@scream@botsin.space
|
# https://mastodon.social/@scream@botsin.space
|
||||||
@ -421,13 +443,6 @@ if [ -e config/manifest-minimal-remove ]; then
|
|||||||
cp config/manifest-minimal-remove "$PREFIX.manifest-minimal-remove"
|
cp config/manifest-minimal-remove "$PREFIX.manifest-minimal-remove"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for ISO in binary.iso binary.hybrid.iso; do
|
|
||||||
[ -e "$ISO" ] || continue
|
|
||||||
ln "$ISO" "$PREFIX.iso"
|
|
||||||
chmod 644 "$PREFIX.iso"
|
|
||||||
break
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -e "binary/$INITFS/filesystem.dir" ]; then
|
if [ -e "binary/$INITFS/filesystem.dir" ]; then
|
||||||
(cd "binary/$INITFS/filesystem.dir/" && tar -c --sort=name --xattrs *) | \
|
(cd "binary/$INITFS/filesystem.dir/" && tar -c --sort=name --xattrs *) | \
|
||||||
gzip -9 --rsyncable > "$PREFIX.rootfs.tar.gz"
|
gzip -9 --rsyncable > "$PREFIX.rootfs.tar.gz"
|
||||||
@ -552,3 +567,28 @@ case $PROJECT in
|
|||||||
ubuntu-cpc)
|
ubuntu-cpc)
|
||||||
config/hooks.d/remove-implicit-artifacts
|
config/hooks.d/remove-implicit-artifacts
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
if [ "${MAKE_ISO}" = "yes" ]; then
|
||||||
|
# Link build artifacts with "for-iso." prefix for isobuild to consume.
|
||||||
|
# Layered builds create squashfs via lb_binary_layered (which already
|
||||||
|
# creates for-iso.*.squashfs files). Single-pass builds only have
|
||||||
|
# ${PREFIX}.squashfs, which does not contain cdrom.sources, so we
|
||||||
|
# create a for-iso.filesystem.squashfs that does.
|
||||||
|
if [ -z "$PASSES" ]; then
|
||||||
|
isobuild generate-sources --mountpoint=/cdrom > chroot/etc/apt/sources.list.d/cdrom.sources
|
||||||
|
create_squashfs chroot for-iso.filesystem.squashfs
|
||||||
|
fi
|
||||||
|
# Link kernel and initrd files. The ${thing#${PREFIX}} expansion strips
|
||||||
|
# the PREFIX, so "livecd.ubuntu-server.kernel-generic" becomes
|
||||||
|
# "for-iso.kernel-generic".
|
||||||
|
for thing in ${PREFIX}.kernel-* ${PREFIX}.initrd-*; do
|
||||||
|
for_iso_path=for-iso${thing#${PREFIX}}
|
||||||
|
if [ ! -f $for_iso_path ]; then
|
||||||
|
ln -v $thing $for_iso_path
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
isobuild add-live-filesystem --artifact-prefix for-iso.
|
||||||
|
isobuild make-bootable --project "${PROJECT}" --capproject "$(cat config/iso-ids/capproject)" \
|
||||||
|
${SUBARCH:+--subarch "${SUBARCH}"}
|
||||||
|
isobuild make-iso --volid "$(cat config/iso-ids/vol-id)" --dest ${PREFIX}.iso
|
||||||
|
fi
|
||||||
|
|||||||
@ -9,9 +9,7 @@ case $ARCH:$SUBARCH in
|
|||||||
armhf:|\
|
armhf:|\
|
||||||
i386:|\
|
i386:|\
|
||||||
ppc64el:|\
|
ppc64el:|\
|
||||||
riscv64:|riscv64:generic|riscv64:icicle|riscv64:jh7110|riscv64:licheerv|\
|
riscv64:|riscv64:generic|\
|
||||||
riscv64:milkvmars|riscv64:nezha|riscv64:pic64gx|riscv64:unmatched|\
|
|
||||||
riscv64:visionfive|riscv64:visionfive2|\
|
|
||||||
s390x:|\
|
s390x:|\
|
||||||
*appliance*)
|
*appliance*)
|
||||||
;;
|
;;
|
||||||
@ -52,6 +50,7 @@ mkdir -p config
|
|||||||
cp -af /usr/share/livecd-rootfs/live-build/functions config/functions
|
cp -af /usr/share/livecd-rootfs/live-build/functions config/functions
|
||||||
cp -af /usr/share/livecd-rootfs/live-build/lb_*_layered config/
|
cp -af /usr/share/livecd-rootfs/live-build/lb_*_layered config/
|
||||||
cp -af /usr/share/livecd-rootfs/live-build/snap-seed-parse.py config/snap-seed-parse
|
cp -af /usr/share/livecd-rootfs/live-build/snap-seed-parse.py config/snap-seed-parse
|
||||||
|
cp -af /usr/share/livecd-rootfs/live-build/snap-seed-missing-providers.py config/snap-seed-missing-providers
|
||||||
cp -af /usr/share/livecd-rootfs/live-build/expand-task config/expand-task
|
cp -af /usr/share/livecd-rootfs/live-build/expand-task config/expand-task
|
||||||
cp -af /usr/share/livecd-rootfs/live-build/squashfs-exclude-files config/
|
cp -af /usr/share/livecd-rootfs/live-build/squashfs-exclude-files config/
|
||||||
|
|
||||||
@ -401,15 +400,6 @@ fi
|
|||||||
# one also must request disk1-img-xz image format
|
# one also must request disk1-img-xz image format
|
||||||
if [ "$IMAGEFORMAT" = "ext4" ] && [ "$PROJECT" = "ubuntu-cpc" ]; then
|
if [ "$IMAGEFORMAT" = "ext4" ] && [ "$PROJECT" = "ubuntu-cpc" ]; then
|
||||||
case $ARCH:$SUBARCH in
|
case $ARCH:$SUBARCH in
|
||||||
riscv64:icicle | \
|
|
||||||
riscv64:jh7110 | \
|
|
||||||
riscv64:licheerv | \
|
|
||||||
riscv64:milkvmars | \
|
|
||||||
riscv64:nezha | \
|
|
||||||
riscv64:pic64gx | \
|
|
||||||
riscv64:unmatched | \
|
|
||||||
riscv64:visionfive | \
|
|
||||||
riscv64:visionfive2 | \
|
|
||||||
*:generic)
|
*:generic)
|
||||||
IMAGE_HAS_HARDCODED_PASSWORD=1
|
IMAGE_HAS_HARDCODED_PASSWORD=1
|
||||||
if [ -z "${IMAGE_TARGETS:-}" ]; then
|
if [ -z "${IMAGE_TARGETS:-}" ]; then
|
||||||
@ -646,7 +636,7 @@ case $PROJECT in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
case $PROJECT in
|
case $PROJECT in
|
||||||
ubuntu-server|ubuntu-mini-iso)
|
ubuntu-mini-iso)
|
||||||
COMPONENTS='main'
|
COMPONENTS='main'
|
||||||
;;
|
;;
|
||||||
edubuntu|ubuntu-budgie|ubuntucinnamon|ubuntukylin)
|
edubuntu|ubuntu-budgie|ubuntucinnamon|ubuntukylin)
|
||||||
@ -670,13 +660,26 @@ if ! [ -e config/germinate-output/structure ]; then
|
|||||||
fi
|
fi
|
||||||
(cd config/germinate-output && germinate --no-rdepends --no-installer \
|
(cd config/germinate-output && germinate --no-rdepends --no-installer \
|
||||||
-S $SEEDMIRROR -m $MIRROR -d $SUITE,$SUITE-updates \
|
-S $SEEDMIRROR -m $MIRROR -d $SUITE,$SUITE-updates \
|
||||||
-s $FLAVOUR.$SUITE $GERMINATE_ARG -a $ARCH)
|
-s $FLAVOUR.$SUITE $GERMINATE_ARG -a ${ARCH_VARIANT:-$ARCH})
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ISO build configuration. These defaults are overridden per-project below.
|
||||||
|
#
|
||||||
|
# MAKE_ISO: Set to "yes" to generate an installer ISO at the end of the build.
|
||||||
|
# This triggers isobuild to run in auto/build.
|
||||||
|
MAKE_ISO=no
|
||||||
|
# POOL_SEED_NAME: The germinate output file defining packages for the ISO's
|
||||||
|
# package pool (repository). Different flavors use different seeds:
|
||||||
|
# - "ship-live" for most desktop images
|
||||||
|
# - "server-ship-live" for Ubuntu Server (includes server-specific packages)
|
||||||
|
# - "" (empty) for images without a pool, like Ubuntu Core Installer
|
||||||
|
POOL_SEED_NAME=ship-live
|
||||||
|
|
||||||
# Common functionality for layered desktop images
|
# Common functionality for layered desktop images
|
||||||
common_layered_desktop_image() {
|
common_layered_desktop_image() {
|
||||||
touch config/universe-enabled
|
touch config/universe-enabled
|
||||||
PASSES_TO_LAYERS="true"
|
PASSES_TO_LAYERS="true"
|
||||||
|
MAKE_ISO=yes
|
||||||
|
|
||||||
if [ -n "$HAS_MINIMAL" ]; then
|
if [ -n "$HAS_MINIMAL" ]; then
|
||||||
if [ -z "$MINIMAL_TASKS" ]; then
|
if [ -z "$MINIMAL_TASKS" ]; then
|
||||||
@ -907,6 +910,7 @@ case $PROJECT in
|
|||||||
add_task install minimal standard
|
add_task install minimal standard
|
||||||
add_task install kubuntu-desktop
|
add_task install kubuntu-desktop
|
||||||
LIVE_TASK='kubuntu-live'
|
LIVE_TASK='kubuntu-live'
|
||||||
|
MAKE_ISO=yes
|
||||||
add_chroot_hook remove-gnome-icon-cache
|
add_chroot_hook remove-gnome-icon-cache
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -917,6 +921,7 @@ case $PROJECT in
|
|||||||
MINIMAL_DESC="A minimal installation of the Edubuntu Desktop."
|
MINIMAL_DESC="A minimal installation of the Edubuntu Desktop."
|
||||||
STANDARD_TASKS=edubuntu-desktop-gnome
|
STANDARD_TASKS=edubuntu-desktop-gnome
|
||||||
KERNEL_FLAVOURS=generic
|
KERNEL_FLAVOURS=generic
|
||||||
|
NEEDS_DRACUT=yes
|
||||||
do_layered_desktop_image
|
do_layered_desktop_image
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -932,6 +937,7 @@ case $PROJECT in
|
|||||||
ubuntu-unity)
|
ubuntu-unity)
|
||||||
add_task install minimal standard ${PROJECT}-desktop
|
add_task install minimal standard ${PROJECT}-desktop
|
||||||
LIVE_TASK=${PROJECT}-live
|
LIVE_TASK=${PROJECT}-live
|
||||||
|
MAKE_ISO=yes
|
||||||
;;
|
;;
|
||||||
|
|
||||||
lubuntu)
|
lubuntu)
|
||||||
@ -1006,6 +1012,8 @@ case $PROJECT in
|
|||||||
live)
|
live)
|
||||||
OPTS="${OPTS:+$OPTS }--bootstrap-flavour=minimal"
|
OPTS="${OPTS:+$OPTS }--bootstrap-flavour=minimal"
|
||||||
PASSES_TO_LAYERS=true
|
PASSES_TO_LAYERS=true
|
||||||
|
MAKE_ISO=yes
|
||||||
|
POOL_SEED_NAME=server-ship-live
|
||||||
add_task ubuntu-server-minimal server-minimal
|
add_task ubuntu-server-minimal server-minimal
|
||||||
add_package ubuntu-server-minimal lxd-installer
|
add_package ubuntu-server-minimal lxd-installer
|
||||||
add_task ubuntu-server-minimal.ubuntu-server minimal standard server
|
add_task ubuntu-server-minimal.ubuntu-server minimal standard server
|
||||||
@ -1106,6 +1114,9 @@ case $PROJECT in
|
|||||||
arm64)
|
arm64)
|
||||||
add_package ubuntu-server-minimal.ubuntu-server.installer.$flavor.netboot shim-signed
|
add_package ubuntu-server-minimal.ubuntu-server.installer.$flavor.netboot shim-signed
|
||||||
;;
|
;;
|
||||||
|
riscv64)
|
||||||
|
add_package ubuntu-server-minimal.ubuntu-server.installer.$flavor.netboot grub-efi-riscv64 grub-efi-riscv64-unsigned
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
add_package ubuntu-server-minimal.ubuntu-server.installer.$flavor.netboot
|
add_package ubuntu-server-minimal.ubuntu-server.installer.$flavor.netboot
|
||||||
;;
|
;;
|
||||||
@ -1135,6 +1146,8 @@ case $PROJECT in
|
|||||||
fi
|
fi
|
||||||
OPTS="${OPTS:+$OPTS }--bootstrap-flavour=minimal"
|
OPTS="${OPTS:+$OPTS }--bootstrap-flavour=minimal"
|
||||||
PASSES_TO_LAYERS=true
|
PASSES_TO_LAYERS=true
|
||||||
|
MAKE_ISO=yes
|
||||||
|
POOL_SEED_NAME=
|
||||||
add_task base server-minimal server
|
add_task base server-minimal server
|
||||||
add_task base.live server-live
|
add_task base.live server-live
|
||||||
add_package base.live linux-image-generic
|
add_package base.live linux-image-generic
|
||||||
@ -1218,14 +1231,7 @@ case $PROJECT in
|
|||||||
;;
|
;;
|
||||||
riscv64*)
|
riscv64*)
|
||||||
if [ -n "$SUBARCH" ]; then
|
if [ -n "$SUBARCH" ]; then
|
||||||
case "${SUBARCH:-}" in
|
KERNEL_FLAVOURS=generic
|
||||||
visionfive)
|
|
||||||
KERNEL_FLAVOURS=starfive
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
KERNEL_FLAVOURS=generic
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -1378,6 +1384,7 @@ lb config noauto \
|
|||||||
--checksums none \
|
--checksums none \
|
||||||
--cache false \
|
--cache false \
|
||||||
${BOOTAPPEND_LIVE:+--bootappend-live "$BOOTAPPEND_LIVE"} \
|
${BOOTAPPEND_LIVE:+--bootappend-live "$BOOTAPPEND_LIVE"} \
|
||||||
|
${ARCH_VARIANT:+--bootstrap-arch "$ARCH_VARIANT"} \
|
||||||
$OPTS \
|
$OPTS \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
@ -1396,6 +1403,8 @@ echo "IMAGEFORMAT=\"$IMAGEFORMAT\"" >> config/chroot
|
|||||||
if [ -n "$PASSES" ]; then
|
if [ -n "$PASSES" ]; then
|
||||||
echo "PASSES=\"$PASSES\"" >> config/common
|
echo "PASSES=\"$PASSES\"" >> config/common
|
||||||
fi
|
fi
|
||||||
|
echo "MAKE_ISO=\"$MAKE_ISO\"" >> config/common
|
||||||
|
echo "POOL_SEED_NAME=\"$POOL_SEED_NAME\"" >> config/common
|
||||||
if [ -n "$NO_SQUASHFS_PASSES" ]; then
|
if [ -n "$NO_SQUASHFS_PASSES" ]; then
|
||||||
echo "NO_SQUASHFS_PASSES=\"$NO_SQUASHFS_PASSES\"" >> config/common
|
echo "NO_SQUASHFS_PASSES=\"$NO_SQUASHFS_PASSES\"" >> config/common
|
||||||
fi
|
fi
|
||||||
@ -1519,7 +1528,7 @@ fi
|
|||||||
|
|
||||||
case $PROJECT:${SUBPROJECT:-} in
|
case $PROJECT:${SUBPROJECT:-} in
|
||||||
ubuntu-cpc:*|ubuntu-server:live|ubuntu:desktop-preinstalled| \
|
ubuntu-cpc:*|ubuntu-server:live|ubuntu:desktop-preinstalled| \
|
||||||
ubuntu-wsl:*|ubuntu-mini-iso:*|ubuntu:|ubuntu-oem:*| \
|
ubuntu-wsl:*|ubuntu-mini-iso:*|ubuntu:|ubuntu:dangerous|ubuntu-oem:*| \
|
||||||
ubuntustudio:*|edubuntu:*|ubuntu-budgie:*|ubuntucinnamon:*|xubuntu:*| \
|
ubuntustudio:*|edubuntu:*|ubuntu-budgie:*|ubuntucinnamon:*|xubuntu:*| \
|
||||||
ubuntukylin:*|ubuntu-mate:*|ubuntu-core-installer:*|lubuntu:*)
|
ubuntukylin:*|ubuntu-mate:*|ubuntu-core-installer:*|lubuntu:*)
|
||||||
# Ensure that most things e.g. includes.chroot are copied as is
|
# Ensure that most things e.g. includes.chroot are copied as is
|
||||||
@ -1689,3 +1698,11 @@ apt-get -y download $PREINSTALL_POOL
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${MAKE_ISO}" = "yes" ]; then
|
||||||
|
# XXX should pass --build-type here.
|
||||||
|
/usr/share/livecd-rootfs/live-build/gen-iso-ids \
|
||||||
|
--project $PROJECT ${SUBPROJECT:+--subproject $SUBPROJECT} \
|
||||||
|
--arch $ARCH ${SUBARCH:+--subarch $SUBARCH} ${NOW+--serial $NOW} \
|
||||||
|
--output-dir config/iso-ids/
|
||||||
|
fi
|
||||||
|
|||||||
@ -137,7 +137,9 @@ EOF
|
|||||||
|
|
||||||
disk_image=binary/boot/disk-uefi.ext4
|
disk_image=binary/boot/disk-uefi.ext4
|
||||||
|
|
||||||
create_empty_disk_image "${disk_image}"
|
# Adjust the image size to accomodate increasing package sizes
|
||||||
|
size=$(( imagesize*115/100 ))
|
||||||
|
create_empty_disk_image "${disk_image}" "${size}"
|
||||||
create_partitions "${disk_image}"
|
create_partitions "${disk_image}"
|
||||||
mount_image "${disk_image}" 1
|
mount_image "${disk_image}" 1
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
# vi: ts=4 expandtab syntax=sh
|
# vi: ts=4 expandtab syntax=sh
|
||||||
|
|
||||||
# default imagesize = 2252*1024**2 = 2.2G (the current size we ship)
|
# default imagesize = 2252*1024**2 = 2.2G (the current size we ship)
|
||||||
|
# However this value may be overridden in individual hooks/binaries (like in buildd and ubuntu-cpc)
|
||||||
imagesize=${IMAGE_SIZE:-2361393152}
|
imagesize=${IMAGE_SIZE:-2361393152}
|
||||||
fs_label="${FS_LABEL:-rootfs}"
|
fs_label="${FS_LABEL:-rootfs}"
|
||||||
|
|
||||||
@ -33,7 +34,8 @@ clean_loops() {
|
|||||||
|
|
||||||
create_empty_disk_image() {
|
create_empty_disk_image() {
|
||||||
# Prepare an empty disk image
|
# Prepare an empty disk image
|
||||||
dd if=/dev/zero of="$1" bs=1 count=0 seek="${imagesize}"
|
size=${2:-$imagesize}
|
||||||
|
dd if=/dev/zero of="$1" bs=1 count=0 seek="${size}"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_manifest() {
|
create_manifest() {
|
||||||
@ -566,8 +568,8 @@ _snap_post_process() {
|
|||||||
# If the 'core' snap is not present, assume we are coreXX-only and
|
# If the 'core' snap is not present, assume we are coreXX-only and
|
||||||
# install the snapd snap.
|
# install the snapd snap.
|
||||||
channel=stable
|
channel=stable
|
||||||
if [ $SUBPROJECT = "dangerous" ]; then
|
if [ "$PROJECT" = "ubuntu" -o "$SUBPROJECT" = "dangerous" ]; then
|
||||||
channel=$CHANNEL
|
channel=edge
|
||||||
fi
|
fi
|
||||||
if [ ! -f ${snaps_dir}/core_[0-9]*.snap ]; then
|
if [ ! -f ${snaps_dir}/core_[0-9]*.snap ]; then
|
||||||
_snap_preseed $CHROOT_ROOT snapd "$channel"
|
_snap_preseed $CHROOT_ROOT snapd "$channel"
|
||||||
@ -666,7 +668,12 @@ _snap_preseed() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_snap_preseed $CHROOT_ROOT $core_snap stable
|
local core_channel=stable
|
||||||
|
if [ "$SUBPROJECT" = "dangerous" ]; then
|
||||||
|
core_channel=edge
|
||||||
|
fi
|
||||||
|
|
||||||
|
_snap_preseed $CHROOT_ROOT $core_snap $core_channel
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -795,6 +802,14 @@ snap_preseed() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$SUBPROJECT" = "dangerous" ]; then
|
||||||
|
# For the dangerous ISOs we want to include edge versions of all the
|
||||||
|
# snaps. Many snaps have a channel like "1/stable/ubuntu-X.Y" in the
|
||||||
|
# seed but the ubuntu-X.Y branches don't usually exist in the edge
|
||||||
|
# channel so strip that off as well.
|
||||||
|
CHANNEL=$(echo $CHANNEL | sed -e s/stable.*/edge/)
|
||||||
|
fi
|
||||||
|
|
||||||
# At this point:
|
# At this point:
|
||||||
# SNAP_NAME is just the snap name
|
# SNAP_NAME is just the snap name
|
||||||
# SNAP is either $SNAP_NAME or $SNAP_NAME/classic for classic confined
|
# SNAP is either $SNAP_NAME or $SNAP_NAME/classic for classic confined
|
||||||
@ -853,6 +868,17 @@ snap_validate_seed() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -e "${CHROOT_ROOT}/var/lib/snapd/seed/seed.yaml" ]; then
|
if [ -e "${CHROOT_ROOT}/var/lib/snapd/seed/seed.yaml" ]; then
|
||||||
|
if [ "${SUBPROJECT}" = "dangerous" ]; then
|
||||||
|
# When we include a snap from edge instead of stable, it may have
|
||||||
|
# require different content provider snaps to be installed and it
|
||||||
|
# is not reasonable to have the seed contain this. So run this
|
||||||
|
# script which figures out which content provider snaps are
|
||||||
|
# missing and include them.
|
||||||
|
./config/snap-seed-missing-providers "${CHROOT_ROOT}/var/lib/snapd/seed/" >> config/missing-providers
|
||||||
|
while read snap; do
|
||||||
|
_snap_preseed chroot "${snap}" edge
|
||||||
|
done < config/missing-providers
|
||||||
|
fi
|
||||||
snap debug validate-seed "${CHROOT_ROOT}/var/lib/snapd/seed/seed.yaml"
|
snap debug validate-seed "${CHROOT_ROOT}/var/lib/snapd/seed/seed.yaml"
|
||||||
/usr/lib/snapd/snap-preseed --reset $(realpath "${CHROOT_ROOT}")
|
/usr/lib/snapd/snap-preseed --reset $(realpath "${CHROOT_ROOT}")
|
||||||
/usr/lib/snapd/snap-preseed $(realpath "${CHROOT_ROOT}")
|
/usr/lib/snapd/snap-preseed $(realpath "${CHROOT_ROOT}")
|
||||||
@ -1057,6 +1083,7 @@ network:
|
|||||||
version: 2
|
version: 2
|
||||||
renderer: NetworkManager
|
renderer: NetworkManager
|
||||||
EOF
|
EOF
|
||||||
|
chmod 600 chroot/etc/netplan/01-network-manager-all.yaml
|
||||||
# Do not limit cloud-init renderers to network-manager as suggested
|
# Do not limit cloud-init renderers to network-manager as suggested
|
||||||
# in LP: #1982855 because subiquity needs to render full networking
|
# in LP: #1982855 because subiquity needs to render full networking
|
||||||
# in ephemeral boot time when autoinstall.network is provided.
|
# in ephemeral boot time when autoinstall.network is provided.
|
||||||
@ -1085,11 +1112,11 @@ EOF
|
|||||||
mkdir -p chroot/etc/systemd/system/
|
mkdir -p chroot/etc/systemd/system/
|
||||||
cat <<EOF > chroot/etc/systemd/system/cloud-init-network.service
|
cat <<EOF > chroot/etc/systemd/system/cloud-init-network.service
|
||||||
${AUTOMATION_HEADER}
|
${AUTOMATION_HEADER}
|
||||||
# Based on cloud-init 24.3 for Desktop LiveCD
|
# Based on cloud-init 25.3 for Desktop LiveCD (LP: #2128887)
|
||||||
# Redact sysinit.target from Before, add After=NetworkManager*.service
|
# Redact sysinit.target from Before, add After=NetworkManager*.service
|
||||||
# (LP: #2008952)
|
# (LP: #2008952)
|
||||||
[Unit]
|
[Unit]
|
||||||
# https://cloudinit.readthedocs.io/en/latest/explanation/boot.html
|
# https://docs.cloud-init.io/en/latest/explanation/boot.html
|
||||||
Description=Cloud-init: Network Stage
|
Description=Cloud-init: Network Stage
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Wants=cloud-init-local.service
|
Wants=cloud-init-local.service
|
||||||
@ -1119,7 +1146,7 @@ Type=oneshot
|
|||||||
# process has completed this stage. The output from the return socket is piped
|
# process has completed this stage. The output from the return socket is piped
|
||||||
# into a shell so that the process can send a completion message (defaults to
|
# into a shell so that the process can send a completion message (defaults to
|
||||||
# "done", otherwise includes an error message) and an exit code to systemd.
|
# "done", otherwise includes an error message) and an exit code to systemd.
|
||||||
ExecStart=sh -c 'echo "start" | netcat -Uu -W1 /run/cloud-init/share/network.sock -s /run/cloud-init/share/network-return.sock | sh'
|
ExecStart=sh -c 'echo "start" | nc -U /run/cloud-init/share/network.sock | sh'
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
TimeoutSec=0
|
TimeoutSec=0
|
||||||
|
|
||||||
@ -1377,3 +1404,50 @@ EOF
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Determine the appropriate partition type UUID for the root filesystem
|
||||||
|
# based on the architecture.
|
||||||
|
# Please see https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
|
||||||
|
# for where the GUIDs come from.
|
||||||
|
gpt_root_partition_uuid() {
|
||||||
|
local ARCH="$1"
|
||||||
|
|
||||||
|
if [ -z "${ARCH:-}" ]; then
|
||||||
|
echo "usage: gpt_root_partition_uuid <arch>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${ARCH}" in
|
||||||
|
"amd64")
|
||||||
|
ROOTFS_PARTITION_TYPE="4f68bce3-e8cd-4db1-96e7-fbcaf984b709"
|
||||||
|
;;
|
||||||
|
"arm64")
|
||||||
|
ROOTFS_PARTITION_TYPE="b921b045-1df0-41c3-af44-4c6f280d3fae"
|
||||||
|
;;
|
||||||
|
"armhf")
|
||||||
|
ROOTFS_PARTITION_TYPE="69dad710-2ce4-4e3c-b16c-21a1d49abed3"
|
||||||
|
;;
|
||||||
|
"riscv64")
|
||||||
|
ROOTFS_PARTITION_TYPE="72ec70a6-cf74-40e6-bd49-4bda08e8f224"
|
||||||
|
;;
|
||||||
|
"ppc64el")
|
||||||
|
ROOTFS_PARTITION_TYPE="c31c45e6-3f39-412e-80fb-4809c4980599"
|
||||||
|
;;
|
||||||
|
"s390x")
|
||||||
|
ROOTFS_PARTITION_TYPE="5eead9a9-fe09-4a1e-a1d7-520d00531306"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported architecture: ${ARCH}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "${ROOTFS_PARTITION_TYPE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wrapper for the isobuild tool. Sets PYTHONPATH so the isobuilder module
|
||||||
|
# is importable, and uses config/iso-dir as the standard working directory
|
||||||
|
# for ISO metadata and intermediate files.
|
||||||
|
isobuild () {
|
||||||
|
PYTHONPATH=/usr/share/livecd-rootfs/live-build/ /usr/share/livecd-rootfs/live-build/isobuild --workdir config/iso-dir "$@"
|
||||||
|
}
|
||||||
|
|||||||
197
live-build/gen-iso-ids
Executable file
197
live-build/gen-iso-ids
Executable file
@ -0,0 +1,197 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Compute various slightly obscure IDs and labels used by ISO builds.
|
||||||
|
#
|
||||||
|
# * ISO9660 images have a "volume id".
|
||||||
|
# * Our ISOs contain a ".disk/info" file that is read by various
|
||||||
|
# other things (casper, the installer) and is generally used as a
|
||||||
|
# record of where an installation came from.
|
||||||
|
# * The code that sets up grub for the ISO needs a "capitalized
|
||||||
|
# project name" or capproject.
|
||||||
|
#
|
||||||
|
# All of these are derived from other build parameters (and/or
|
||||||
|
# information in etc/os-release) in slightly non-obvious ways so the
|
||||||
|
# logic to do so is confined to this file to avoid it cluttering
|
||||||
|
# anywhere else.
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import platform
|
||||||
|
import time
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
# Be careful about the values here. They end up in .disk/info, which is read by
|
||||||
|
# casper to create the live session user, so if there is a space in the
|
||||||
|
# capproject things go a bit wonky.
|
||||||
|
#
|
||||||
|
# It will also be used by make_vol_id to construct an ISO9660 volume ID as
|
||||||
|
#
|
||||||
|
# "$(CAPPROJECT) $(DEBVERSION) $(ARCH)",
|
||||||
|
#
|
||||||
|
# e.g. "Ubuntu 14.10 amd64". The volume ID is limited to 32 characters. This
|
||||||
|
# therefore imposes a limit on the length of project_map values of 25 - (length
|
||||||
|
# of longest relevant architecture name).
|
||||||
|
project_to_capproject_map = {
|
||||||
|
"edubuntu": "Edubuntu",
|
||||||
|
"kubuntu": "Kubuntu",
|
||||||
|
"lubuntu": "Lubuntu",
|
||||||
|
"ubuntu": "Ubuntu",
|
||||||
|
"ubuntu-base": "Ubuntu-Base",
|
||||||
|
"ubuntu-budgie": "Ubuntu-Budgie",
|
||||||
|
"ubuntu-core-installer": "Ubuntu-Core-Installer",
|
||||||
|
"ubuntu-mate": "Ubuntu-MATE",
|
||||||
|
"ubuntu-mini-iso": "Ubuntu-Mini-ISO",
|
||||||
|
"ubuntu-oem": "Ubuntu OEM",
|
||||||
|
"ubuntu-server": "Ubuntu-Server",
|
||||||
|
"ubuntu-unity": "Ubuntu-Unity",
|
||||||
|
"ubuntu-wsl": "Ubuntu WSL",
|
||||||
|
"ubuntucinnamon": "Ubuntu-Cinnamon",
|
||||||
|
"ubuntukylin": "Ubuntu-Kylin",
|
||||||
|
"ubuntustudio": "Ubuntu-Studio",
|
||||||
|
"xubuntu": "Xubuntu",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def make_disk_info(
|
||||||
|
os_release: dict[str, str],
|
||||||
|
arch: str,
|
||||||
|
subarch: str,
|
||||||
|
capproject: str,
|
||||||
|
subproject: str,
|
||||||
|
build_type: str,
|
||||||
|
serial: str,
|
||||||
|
) -> str:
|
||||||
|
# os-release VERSION is _almost_ what goes into .disk/info...
|
||||||
|
# it can be
|
||||||
|
# VERSION="24.04.3 LTS (Noble Numbat)"
|
||||||
|
# or
|
||||||
|
# VERSION="25.10 (Questing Quokka)"
|
||||||
|
# We want the Adjective Animal to be in quotes, not parentheses, e.g.
|
||||||
|
# 'Ubuntu 24.04.3 LTS "Noble Numbat"'. This format is expected by casper
|
||||||
|
# (which parses .disk/info to set up the live session) and the installer.
|
||||||
|
version = os_release["VERSION"]
|
||||||
|
version = version.replace("(", '"')
|
||||||
|
version = version.replace(")", '"')
|
||||||
|
|
||||||
|
capsubproject = ""
|
||||||
|
if subproject == "minimal":
|
||||||
|
capsubproject = " Minimal"
|
||||||
|
|
||||||
|
fullarch = arch
|
||||||
|
if subarch:
|
||||||
|
fullarch += "+" + subarch
|
||||||
|
|
||||||
|
return f"{capproject}{capsubproject} {version} - {build_type} {fullarch} ({serial})"
|
||||||
|
|
||||||
|
|
||||||
|
def make_vol_id(os_release: dict[str, str], arch: str, capproject: str) -> str:
|
||||||
|
# ISO9660 volume IDs are limited to 32 characters. The volume ID format is
|
||||||
|
# "CAPPROJECT VERSION ARCH", e.g. "Ubuntu 24.04.3 LTS amd64". Longer arch
|
||||||
|
# names like ppc64el and riscv64 can push us over the limit, so we shorten
|
||||||
|
# them here. This is why capproject names are also kept short (see the
|
||||||
|
# comment above project_to_capproject_map).
|
||||||
|
arch_for_volid_map = {
|
||||||
|
"ppc64el": "ppc64",
|
||||||
|
"riscv64": "riscv",
|
||||||
|
}
|
||||||
|
arch_for_volid = arch_for_volid_map.get(arch, arch)
|
||||||
|
|
||||||
|
# from
|
||||||
|
# VERSION="24.04.3 LTS (Noble Numbat)"
|
||||||
|
# or
|
||||||
|
# VERSION="25.10 (Questing Quokka)"
|
||||||
|
# we want "24.04.3 LTS" or "25.10", i.e. everything up to the first "(" (apart
|
||||||
|
# from the whitespace).
|
||||||
|
version = os_release["VERSION"].split("(")[0].strip()
|
||||||
|
|
||||||
|
volid = f"{capproject} {version} {arch_for_volid}"
|
||||||
|
|
||||||
|
# If still over 32 characters (e.g. long capproject + LTS version), fall
|
||||||
|
# back to shorter forms. amd64 gets "x64" since it's widely recognized and
|
||||||
|
# fits; other architectures just drop the arch entirely since multi-arch
|
||||||
|
# ISOs are less common for non-amd64 platforms.
|
||||||
|
if len(volid) > 32:
|
||||||
|
if arch == "amd64":
|
||||||
|
volid = f"{capproject} {version} x64"
|
||||||
|
else:
|
||||||
|
volid = f"{capproject} {version}"
|
||||||
|
return volid
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"--project",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--subproject",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--arch",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--subarch",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--serial",
|
||||||
|
type=str,
|
||||||
|
default=time.strftime("%Y%m%d"),
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--build-type",
|
||||||
|
type=str,
|
||||||
|
default="Daily",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--output-dir",
|
||||||
|
type=click.Path(file_okay=False, resolve_path=True, path_type=pathlib.Path),
|
||||||
|
required=True,
|
||||||
|
help="working directory",
|
||||||
|
)
|
||||||
|
def main(
|
||||||
|
project: str,
|
||||||
|
subproject: str,
|
||||||
|
arch: str,
|
||||||
|
subarch: str,
|
||||||
|
serial: str,
|
||||||
|
build_type: str,
|
||||||
|
output_dir: pathlib.Path,
|
||||||
|
):
|
||||||
|
output_dir.mkdir(exist_ok=True)
|
||||||
|
capproject = project_to_capproject_map[project]
|
||||||
|
|
||||||
|
os_release = platform.freedesktop_os_release()
|
||||||
|
|
||||||
|
with output_dir.joinpath("disk-info").open("w") as fp:
|
||||||
|
disk_info = make_disk_info(
|
||||||
|
os_release,
|
||||||
|
arch,
|
||||||
|
subarch,
|
||||||
|
capproject,
|
||||||
|
subproject,
|
||||||
|
build_type,
|
||||||
|
serial,
|
||||||
|
)
|
||||||
|
print(f"disk_info: {disk_info!r}")
|
||||||
|
fp.write(disk_info)
|
||||||
|
|
||||||
|
with output_dir.joinpath("vol-id").open("w") as fp:
|
||||||
|
vol_id = make_vol_id(os_release, arch, capproject)
|
||||||
|
print(f"vol_id: {vol_id!r} {len(vol_id)}")
|
||||||
|
fp.write(vol_id)
|
||||||
|
|
||||||
|
with output_dir.joinpath("capproject").open("w") as fp:
|
||||||
|
print(f"capproject: {capproject!r}")
|
||||||
|
fp.write(capproject)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
221
live-build/isobuild
Executable file
221
live-build/isobuild
Executable file
@ -0,0 +1,221 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Building an ISO requires knowing:
|
||||||
|
#
|
||||||
|
# * The architecture and series we are building for
|
||||||
|
# * The address of the mirror to pull packages from the pool from and the
|
||||||
|
# components of that mirror to use
|
||||||
|
# * The list of packages to include in the pool
|
||||||
|
# * Where the squashfs files that contain the rootfs and other metadata layers
|
||||||
|
# are
|
||||||
|
# * Where to put the final ISO
|
||||||
|
# * All the bits of information that end up in .disk/info on the ISO and in the
|
||||||
|
# "volume ID" for the ISO
|
||||||
|
#
|
||||||
|
# It's not completely trivial to come up with a nice feeling interface between
|
||||||
|
# livecd-rootfs and this tool. There are about 13 parameters that are needed to
|
||||||
|
# build the ISO and having a tool take 13 arguments seems a bit overwhelming. In
|
||||||
|
# addition some steps need to run before the layers are made into squashfs files
|
||||||
|
# and some after. It felt nicer to have a tool with a few subcommands (7, in the
|
||||||
|
# end) and taking arguments relevant to each step:
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" init --disk-id "" --series "" --arch ""
|
||||||
|
#
|
||||||
|
# Set up the work-dir for later steps. Create the skeleton file layout of the
|
||||||
|
# ISO, populate .disk/info etc, create the gpg key referred to above. Store
|
||||||
|
# series and arch somewhere that later steps can refer to.
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" setup-apt --chroot ""
|
||||||
|
#
|
||||||
|
# Set up aptfor use by later steps, using the configuration from the passed
|
||||||
|
# chroot.
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" generate-pool --package-list-file ""
|
||||||
|
#
|
||||||
|
# Create the pool from the passed germinate output file.
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" generate-sources --mountpoint ""
|
||||||
|
#
|
||||||
|
# Generate an apt deb822 source for the pool, assuming it is mounted at the
|
||||||
|
# passed mountpoint, and output it on stdout.
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" add-live-filesystem --artifact-prefix ""
|
||||||
|
#
|
||||||
|
# Copy the relevant artifacts to the casper directory (and extract the uuids
|
||||||
|
# from the initrds)
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" make-bootable --project "" --capitalized-project ""
|
||||||
|
# --subarch ""
|
||||||
|
#
|
||||||
|
# Set up the bootloader etc so that the ISO can boot (for this clones debian-cd
|
||||||
|
# and run the tools/boot/$series-$arch script but those should be folded into
|
||||||
|
# isobuild fairly promptly IMO).
|
||||||
|
#
|
||||||
|
# $ isobuild --work-dir "" make-iso --vol-id "" --dest ""
|
||||||
|
#
|
||||||
|
# Generate the checksum file and run xorriso to build the final ISO.
|
||||||
|
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from isobuilder.builder import ISOBuilder
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
@click.option(
|
||||||
|
"--workdir",
|
||||||
|
type=click.Path(file_okay=False, resolve_path=True, path_type=pathlib.Path),
|
||||||
|
required=True,
|
||||||
|
help="working directory",
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def main(ctxt, workdir):
|
||||||
|
ctxt.obj = ISOBuilder(workdir)
|
||||||
|
cwd = pathlib.Path().cwd()
|
||||||
|
if workdir.is_relative_to(cwd):
|
||||||
|
workdir = workdir.relative_to(cwd)
|
||||||
|
ctxt.obj.logger.log(f"isobuild starting, workdir: {workdir}")
|
||||||
|
|
||||||
|
|
||||||
|
def subcommand(f):
|
||||||
|
"""Decorator that converts a function into a Click subcommand with logging.
|
||||||
|
|
||||||
|
This decorator:
|
||||||
|
1. Converts function name from snake_case to kebab-case for the CLI
|
||||||
|
2. Wraps the function to log the subcommand name and all parameters
|
||||||
|
3. Registers it as a Click command under the main command group
|
||||||
|
4. Extracts the ISOBuilder instance from the context and passes it as first arg
|
||||||
|
"""
|
||||||
|
name = f.__name__.replace("_", "-")
|
||||||
|
|
||||||
|
def wrapped(ctxt, **kw):
|
||||||
|
# Build a log message showing the subcommand and all its parameters.
|
||||||
|
# We use ctxt.params (Click's resolved parameters) rather than **kw
|
||||||
|
# because ctxt.params includes path resolution and type conversion.
|
||||||
|
# Paths are converted to relative form to keep logs readable and avoid
|
||||||
|
# exposing full filesystem paths in build artifacts.
|
||||||
|
msg = f"subcommand {name}"
|
||||||
|
cwd = pathlib.Path().cwd()
|
||||||
|
for k, v in sorted(ctxt.params.items()):
|
||||||
|
if isinstance(v, pathlib.Path):
|
||||||
|
if v.is_relative_to(cwd):
|
||||||
|
v = v.relative_to(cwd)
|
||||||
|
v = shlex.quote(str(v))
|
||||||
|
msg += f" {k}={v}"
|
||||||
|
with ctxt.obj.logger.logged(msg):
|
||||||
|
f(ctxt.obj, **kw)
|
||||||
|
|
||||||
|
return main.command(name=name)(click.pass_context(wrapped))
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--disk-info",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help="contents of .disk/info",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--series",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help="series being built",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--arch",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help="architecture being built",
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def init(builder, disk_info, series, arch):
|
||||||
|
builder.init(disk_info, series, arch)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--chroot",
|
||||||
|
type=click.Path(
|
||||||
|
file_okay=False, resolve_path=True, path_type=pathlib.Path, exists=True
|
||||||
|
),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def setup_apt(builder, chroot: pathlib.Path):
|
||||||
|
builder.setup_apt(chroot)
|
||||||
|
|
||||||
|
|
||||||
|
@click.pass_obj
|
||||||
|
@click.option(
|
||||||
|
"--package-list-file",
|
||||||
|
type=click.Path(
|
||||||
|
dir_okay=False, exists=True, resolve_path=True, path_type=pathlib.Path
|
||||||
|
),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def generate_pool(builder, package_list_file: pathlib.Path):
|
||||||
|
builder.generate_pool(package_list_file)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--mountpoint",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def generate_sources(builder, mountpoint: str):
|
||||||
|
builder.generate_sources(mountpoint)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--artifact-prefix",
|
||||||
|
type=click.Path(dir_okay=False, resolve_path=True, path_type=pathlib.Path),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def add_live_filesystem(builder, artifact_prefix: pathlib.Path):
|
||||||
|
builder.add_live_filesystem(artifact_prefix)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--project",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option("--capproject", type=str, required=True)
|
||||||
|
@click.option(
|
||||||
|
"--subarch",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def make_bootable(builder, project: str, capproject: str | None, subarch: str):
|
||||||
|
# capproject is the "capitalized project name" used in GRUB menu entries,
|
||||||
|
# e.g. "Ubuntu" or "Kubuntu". It should come from gen-iso-ids (which uses
|
||||||
|
# project_to_capproject_map for proper formatting like "Ubuntu-MATE"), but
|
||||||
|
# we provide a simple .capitalize() fallback for cases where the caller
|
||||||
|
# doesn't have the pre-computed value.
|
||||||
|
if capproject is None:
|
||||||
|
capproject = project.capitalize()
|
||||||
|
builder.make_bootable(project, capproject, subarch)
|
||||||
|
|
||||||
|
|
||||||
|
@click.option(
|
||||||
|
"--dest",
|
||||||
|
type=click.Path(dir_okay=False, resolve_path=True, path_type=pathlib.Path),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--volid",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
@subcommand
|
||||||
|
def make_iso(builder, dest: pathlib.Path, volid: str | None):
|
||||||
|
builder.make_iso(dest, volid)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
1
live-build/isobuilder/__init__.py
Normal file
1
live-build/isobuilder/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
#
|
||||||
109
live-build/isobuilder/apt_state.py
Normal file
109
live-build/isobuilder/apt_state.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import dataclasses
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class PackageInfo:
|
||||||
|
package: str
|
||||||
|
filename: str
|
||||||
|
architecture: str
|
||||||
|
version: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spec(self) -> str:
|
||||||
|
return f"{self.package}:{self.architecture}={self.version}"
|
||||||
|
|
||||||
|
|
||||||
|
def check_proc(proc, ok_codes=(0,)) -> None:
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode not in ok_codes:
|
||||||
|
raise Exception(f"{proc} failed")
|
||||||
|
|
||||||
|
|
||||||
|
class AptStateManager:
|
||||||
|
"""Maintain and use an apt state directory to access package info and debs."""
|
||||||
|
|
||||||
|
def __init__(self, logger, series: str, apt_dir: pathlib.Path):
|
||||||
|
self.logger = logger
|
||||||
|
self.series = series
|
||||||
|
self.apt_root = apt_dir.joinpath("root")
|
||||||
|
self.apt_conf_path = apt_dir.joinpath("apt.conf")
|
||||||
|
|
||||||
|
def _apt_env(self) -> dict[str, str]:
|
||||||
|
return dict(os.environ, APT_CONFIG=str(self.apt_conf_path))
|
||||||
|
|
||||||
|
def setup(self, chroot: pathlib.Path):
|
||||||
|
"""Set up the manager by copying the apt configuration from `chroot`."""
|
||||||
|
for path in "etc/apt", "var/lib/apt":
|
||||||
|
tgt = self.apt_root.joinpath(path)
|
||||||
|
tgt.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copytree(chroot.joinpath(path), tgt)
|
||||||
|
self.apt_conf_path.write_text(f'Dir "{self.apt_root}/"; \n')
|
||||||
|
with self.logger.logged("updating apt indices"):
|
||||||
|
self.logger.run(["apt-get", "update"], env=self._apt_env())
|
||||||
|
|
||||||
|
def show(self, pkgs: list[str]) -> Iterator[PackageInfo]:
|
||||||
|
"""Return information about the binary packages named by `pkgs`.
|
||||||
|
|
||||||
|
Parses apt-cache output, which uses RFC822-like format: field names
|
||||||
|
followed by ": " and values, with multi-line values indented with
|
||||||
|
leading whitespace. We skip continuation lines (starting with space)
|
||||||
|
since PackageInfo only needs single-line fields.
|
||||||
|
|
||||||
|
The `fields` set (derived from PackageInfo's dataclass fields) acts as
|
||||||
|
a filter - we only extract fields we care about, ignoring others like
|
||||||
|
Description.
|
||||||
|
"""
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
["apt-cache", "-o", "APT::Cache::AllVersions=0", "show"] + pkgs,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
encoding="utf-8",
|
||||||
|
env=self._apt_env(),
|
||||||
|
)
|
||||||
|
assert proc.stdout is not None
|
||||||
|
fields = {f.name for f in dataclasses.fields(PackageInfo)}
|
||||||
|
params: dict[str, str] = {}
|
||||||
|
for line in proc.stdout:
|
||||||
|
if line == "\n":
|
||||||
|
yield PackageInfo(**params)
|
||||||
|
params = {}
|
||||||
|
continue
|
||||||
|
if line.startswith(" "):
|
||||||
|
continue
|
||||||
|
field, value = line.split(": ", 1)
|
||||||
|
field = field.lower()
|
||||||
|
if field in fields:
|
||||||
|
params[field] = value.strip()
|
||||||
|
check_proc(proc)
|
||||||
|
if params:
|
||||||
|
yield PackageInfo(**params)
|
||||||
|
|
||||||
|
def download(self, rootdir: pathlib.Path, pkg_info: PackageInfo):
|
||||||
|
"""Download the package specified by `pkg_info` under `rootdir`.
|
||||||
|
|
||||||
|
The package is saved to the same path under `rootdir` as it is
|
||||||
|
at in the archive it comes from.
|
||||||
|
"""
|
||||||
|
target_dir = rootdir.joinpath(pkg_info.filename).parent
|
||||||
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.logger.run(
|
||||||
|
["apt-get", "download", pkg_info.spec],
|
||||||
|
cwd=target_dir,
|
||||||
|
check=True,
|
||||||
|
env=self._apt_env(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def in_release_path(self) -> pathlib.Path:
|
||||||
|
"""Return the path to the InRelease file.
|
||||||
|
|
||||||
|
This assumes exactly one InRelease file matches the pattern.
|
||||||
|
Will raise ValueError if there are 0 or multiple matches.
|
||||||
|
"""
|
||||||
|
[path] = self.apt_root.joinpath("var/lib/apt/lists").glob(
|
||||||
|
f"*_dists_{self.series}_InRelease"
|
||||||
|
)
|
||||||
|
return path
|
||||||
386
live-build/isobuilder/builder.py
Normal file
386
live-build/isobuilder/builder.py
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
import contextlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from isobuilder.apt_state import AptStateManager
|
||||||
|
from isobuilder.gpg_key import EphemeralGPGKey
|
||||||
|
from isobuilder.pool_builder import PoolBuilder
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
PACKAGE_BATCH_SIZE = 200
|
||||||
|
MAX_CMD_DISPLAY_LENGTH = 80
|
||||||
|
|
||||||
|
|
||||||
|
def package_list_packages(package_list_file: pathlib.Path) -> list[str]:
|
||||||
|
# Parse germinate output to extract package names. Germinate is Ubuntu's
|
||||||
|
# package dependency resolver that outputs dependency trees for seeds (like
|
||||||
|
# "ship-live" or "server-ship-live").
|
||||||
|
#
|
||||||
|
# Germinate output format has 2 header lines at the start and 2 footer lines
|
||||||
|
# at the end (showing statistics), so we skip them with [2:-2].
|
||||||
|
# Each data line starts with the package name followed by whitespace and
|
||||||
|
# dependency info. This format is stable but if germinate ever changes its
|
||||||
|
# header/footer count, this will break silently.
|
||||||
|
lines = package_list_file.read_text().splitlines()[2:-2]
|
||||||
|
return [line.split(None, 1)[0] for line in lines]
|
||||||
|
|
||||||
|
|
||||||
|
def make_sources_text(
|
||||||
|
series: str, gpg_key: EphemeralGPGKey, components: list[str], mountpoint: str
|
||||||
|
) -> str:
|
||||||
|
"""Generate a deb822-format apt source file for the ISO's package pool.
|
||||||
|
|
||||||
|
deb822 is the modern apt sources format (see sources.list(5) and deb822(5)).
|
||||||
|
It uses RFC822-style fields where multi-line values must be indented with a
|
||||||
|
leading space, and empty lines within a value are represented as " ."
|
||||||
|
(space-dot). This format is required for inline GPG keys in the Signed-By
|
||||||
|
field.
|
||||||
|
"""
|
||||||
|
key = gpg_key.export_public()
|
||||||
|
quoted_key = []
|
||||||
|
for line in key.splitlines():
|
||||||
|
if not line:
|
||||||
|
quoted_key.append(" .")
|
||||||
|
else:
|
||||||
|
quoted_key.append(" " + line)
|
||||||
|
return f"""\
|
||||||
|
Types: deb
|
||||||
|
URIs: file://{mountpoint}
|
||||||
|
Suites: {series}
|
||||||
|
Components: {" ".join(components)}
|
||||||
|
Check-Date: no
|
||||||
|
Signed-By:
|
||||||
|
""" + "\n".join(
|
||||||
|
quoted_key
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._indent = ""
|
||||||
|
|
||||||
|
def log(self, msg):
|
||||||
|
print(self._indent + msg, file=sys.stderr)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def logged(self, msg, done_msg=None):
|
||||||
|
self.log(msg)
|
||||||
|
self._indent += " "
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._indent = self._indent[:-2]
|
||||||
|
if done_msg is not None:
|
||||||
|
self.log(done_msg)
|
||||||
|
|
||||||
|
def msg_for_cmd(self, cmd, limit_length=True, cwd=None) -> str:
|
||||||
|
if cwd is None:
|
||||||
|
_cwd = pathlib.Path().cwd()
|
||||||
|
else:
|
||||||
|
_cwd = cwd
|
||||||
|
fmted_cmd = []
|
||||||
|
for arg in cmd:
|
||||||
|
if isinstance(arg, pathlib.Path):
|
||||||
|
if arg.is_relative_to(_cwd):
|
||||||
|
arg = arg.relative_to(_cwd)
|
||||||
|
arg = str(arg)
|
||||||
|
fmted_cmd.append(shlex.quote(arg))
|
||||||
|
fmted_cmd_str = " ".join(fmted_cmd)
|
||||||
|
if len(fmted_cmd_str) > MAX_CMD_DISPLAY_LENGTH and limit_length:
|
||||||
|
fmted_cmd_str = fmted_cmd_str[:MAX_CMD_DISPLAY_LENGTH] + "..."
|
||||||
|
msg = f"running `{fmted_cmd_str}`"
|
||||||
|
if cwd is not None:
|
||||||
|
msg += f" in {cwd}"
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self, cmd: list[str | pathlib.Path], *args, limit_length=True, check=True, **kw
|
||||||
|
):
|
||||||
|
with self.logged(
|
||||||
|
self.msg_for_cmd(cmd, cwd=kw.get("cwd"), limit_length=limit_length)
|
||||||
|
):
|
||||||
|
return subprocess.run(cmd, *args, check=check, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class ISOBuilder:
|
||||||
|
|
||||||
|
def __init__(self, workdir: pathlib.Path):
|
||||||
|
self.workdir = workdir
|
||||||
|
self.logger = Logger()
|
||||||
|
self.iso_root = workdir.joinpath("iso-root")
|
||||||
|
self._series = self._arch = self._gpg_key = self._apt_state = None
|
||||||
|
|
||||||
|
# UTILITY STUFF
|
||||||
|
|
||||||
|
def _read_config(self):
|
||||||
|
with self.workdir.joinpath("config.json").open() as fp:
|
||||||
|
data = json.load(fp)
|
||||||
|
self._series = data["series"]
|
||||||
|
self._arch = data["arch"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arch(self):
|
||||||
|
if self._arch is None:
|
||||||
|
self._read_config()
|
||||||
|
return self._arch
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series(self):
|
||||||
|
if self._series is None:
|
||||||
|
self._read_config()
|
||||||
|
return self._series
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gpg_key(self):
|
||||||
|
if self._gpg_key is None:
|
||||||
|
self._gpg_key = EphemeralGPGKey(
|
||||||
|
self.logger, self.workdir.joinpath("gpg-home")
|
||||||
|
)
|
||||||
|
return self._gpg_key
|
||||||
|
|
||||||
|
@property
|
||||||
|
def apt_state(self):
|
||||||
|
if self._apt_state is None:
|
||||||
|
self._apt_state = AptStateManager(
|
||||||
|
self.logger, self.series, self.workdir.joinpath("apt-state")
|
||||||
|
)
|
||||||
|
return self._apt_state
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
|
||||||
|
def init(self, disk_info: str, series: str, arch: str):
|
||||||
|
self.logger.log("creating directories")
|
||||||
|
self.workdir.mkdir(exist_ok=True)
|
||||||
|
self.iso_root.mkdir()
|
||||||
|
dot_disk = self.iso_root.joinpath(".disk")
|
||||||
|
dot_disk.mkdir()
|
||||||
|
|
||||||
|
self.logger.log("saving config")
|
||||||
|
with self.workdir.joinpath("config.json").open("w") as fp:
|
||||||
|
json.dump({"arch": arch, "series": series}, fp)
|
||||||
|
|
||||||
|
self.logger.log("populating .disk")
|
||||||
|
dot_disk.joinpath("base_installable").touch()
|
||||||
|
dot_disk.joinpath("cd_type").write_text("full_cd/single\n")
|
||||||
|
dot_disk.joinpath("info").write_text(disk_info)
|
||||||
|
self.iso_root.joinpath("casper").mkdir()
|
||||||
|
|
||||||
|
self.gpg_key.create()
|
||||||
|
|
||||||
|
def setup_apt(self, chroot: pathlib.Path):
|
||||||
|
self.apt_state.setup(chroot)
|
||||||
|
|
||||||
|
def generate_pool(self, package_list_file: pathlib.Path):
|
||||||
|
# do we need any of the symlinks we create here??
|
||||||
|
self.logger.log("creating pool skeleton")
|
||||||
|
self.iso_root.joinpath("ubuntu").symlink_to(".")
|
||||||
|
if self.arch not in ("amd64", "i386"):
|
||||||
|
self.iso_root.joinpath("ubuntu-ports").symlink_to(".")
|
||||||
|
self.iso_root.joinpath("dists", self.series).mkdir(parents=True)
|
||||||
|
|
||||||
|
builder = PoolBuilder(
|
||||||
|
self.logger,
|
||||||
|
series=self.series,
|
||||||
|
rootdir=self.iso_root,
|
||||||
|
apt_state=self.apt_state,
|
||||||
|
)
|
||||||
|
pkgs = package_list_packages(package_list_file)
|
||||||
|
# XXX include 32-bit deps of 32-bit packages if needed here
|
||||||
|
with self.logger.logged("adding packages"):
|
||||||
|
for i in range(0, len(pkgs), PACKAGE_BATCH_SIZE):
|
||||||
|
builder.add_packages(
|
||||||
|
self.apt_state.show(pkgs[i : i + PACKAGE_BATCH_SIZE])
|
||||||
|
)
|
||||||
|
builder.make_packages()
|
||||||
|
release_file = builder.make_release()
|
||||||
|
self.gpg_key.sign(release_file)
|
||||||
|
for name in "stable", "unstable":
|
||||||
|
self.iso_root.joinpath("dists", name).symlink_to(self.series)
|
||||||
|
|
||||||
|
def generate_sources(self, mountpoint: str):
|
||||||
|
components = [p.name for p in self.iso_root.joinpath("pool").iterdir()]
|
||||||
|
print(
|
||||||
|
make_sources_text(
|
||||||
|
self.series, self.gpg_key, mountpoint=mountpoint, components=components
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _extract_casper_uuids(self):
|
||||||
|
# Extract UUID files from initrd images for casper (the live boot system).
|
||||||
|
# Each initrd contains a conf/uuid.conf with a unique identifier that
|
||||||
|
# casper uses at boot time to locate the correct root filesystem. These
|
||||||
|
# UUIDs must be placed in .disk/casper-uuid-<flavor> on the ISO so casper
|
||||||
|
# can verify it's booting from the right media.
|
||||||
|
with self.logger.logged("extracting casper uuids"):
|
||||||
|
casper_dir = self.iso_root.joinpath("casper")
|
||||||
|
prefix = "filesystem.initrd-"
|
||||||
|
dot_disk = self.iso_root.joinpath(".disk")
|
||||||
|
for initrd in casper_dir.glob(f"{prefix}*"):
|
||||||
|
initrddir = self.workdir.joinpath("initrd")
|
||||||
|
with self.logger.logged(
|
||||||
|
f"unpacking {initrd.name} ...", done_msg="... done"
|
||||||
|
):
|
||||||
|
self.logger.run(["unmkinitramfs", initrd, initrddir])
|
||||||
|
# unmkinitramfs can produce different directory structures:
|
||||||
|
# - Platforms with early firmware: subdirs like "main/" or "early/"
|
||||||
|
# containing conf/uuid.conf
|
||||||
|
# - Other platforms: conf/uuid.conf directly in the root
|
||||||
|
# Try to find uuid.conf in both locations. The [uuid_conf] = confs
|
||||||
|
# unpacking asserts exactly one match; multiple matches would
|
||||||
|
# indicate an unexpected initrd structure.
|
||||||
|
confs = list(initrddir.glob("*/conf/uuid.conf"))
|
||||||
|
if confs:
|
||||||
|
[uuid_conf] = confs
|
||||||
|
elif initrddir.joinpath("conf/uuid.conf").exists():
|
||||||
|
uuid_conf = initrddir.joinpath("conf/uuid.conf")
|
||||||
|
else:
|
||||||
|
raise Exception("uuid.conf not found")
|
||||||
|
self.logger.log(f"found {uuid_conf.relative_to(initrddir)}")
|
||||||
|
uuid_conf.rename(
|
||||||
|
dot_disk.joinpath("casper-uuid-" + initrd.name[len(prefix) :])
|
||||||
|
)
|
||||||
|
shutil.rmtree(initrddir)
|
||||||
|
|
||||||
|
def add_live_filesystem(self, artifact_prefix: pathlib.Path):
|
||||||
|
# Link build artifacts into the ISO's casper directory. We use hardlinks
|
||||||
|
# (not copies) for filesystem efficiency - they reference the same inode.
|
||||||
|
#
|
||||||
|
# Artifacts come from the layered build with names like "for-iso.base.squashfs"
|
||||||
|
# and need to be renamed for casper. The prefix is stripped, so:
|
||||||
|
# for-iso.base.squashfs -> base.squashfs
|
||||||
|
# for-iso.kernel-generic -> filesystem.kernel-generic
|
||||||
|
#
|
||||||
|
# Kernel and initrd get the extra "filesystem." prefix because debian-cd
|
||||||
|
# expects names like filesystem.kernel-* and filesystem.initrd-*.
|
||||||
|
casper_dir = self.iso_root.joinpath("casper")
|
||||||
|
artifact_dir = artifact_prefix.parent
|
||||||
|
filename_prefix = artifact_prefix.name
|
||||||
|
|
||||||
|
def link(src, target_name):
|
||||||
|
target = casper_dir.joinpath(target_name)
|
||||||
|
self.logger.log(
|
||||||
|
f"creating link from $ISOROOT/casper/{target_name} to $src/{src.name}"
|
||||||
|
)
|
||||||
|
target.hardlink_to(src)
|
||||||
|
|
||||||
|
with self.logger.logged(
|
||||||
|
f"linking artifacts from {casper_dir} to {artifact_dir}"
|
||||||
|
):
|
||||||
|
for ext in "squashfs", "squashfs.gpg", "size", "manifest", "yaml":
|
||||||
|
for path in artifact_dir.glob(f"{filename_prefix}*.{ext}"):
|
||||||
|
newname = path.name[len(filename_prefix) :]
|
||||||
|
link(path, newname)
|
||||||
|
for item in "kernel", "initrd":
|
||||||
|
for path in artifact_dir.glob(f"{filename_prefix}{item}-*"):
|
||||||
|
newname = "filesystem." + path.name[len(filename_prefix) :]
|
||||||
|
link(path, newname)
|
||||||
|
self._extract_casper_uuids()
|
||||||
|
|
||||||
|
def make_bootable(self, project: str, capproject: str, subarch: str):
|
||||||
|
# debian-cd is Ubuntu's CD/ISO image build system. It contains
|
||||||
|
# architecture and series-specific boot configuration scripts that set up
|
||||||
|
# GRUB, syslinux, EFI boot, etc. The tools/boot/$series/boot-$arch script
|
||||||
|
# knows how to make an ISO bootable for each architecture.
|
||||||
|
#
|
||||||
|
# TODO: The boot configuration logic should eventually be ported directly
|
||||||
|
# into isobuilder to avoid this external dependency and git clone.
|
||||||
|
debian_cd_dir = self.workdir.joinpath("debian-cd")
|
||||||
|
with self.logger.logged("cloning debian-cd"):
|
||||||
|
self.logger.run(
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--depth=1",
|
||||||
|
"https://git.launchpad.net/~ubuntu-cdimage/debian-cd/+git/ubuntu",
|
||||||
|
debian_cd_dir,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# Override apt-selection to use our ISO's apt configuration instead of
|
||||||
|
# debian-cd's default. This ensures the boot scripts get packages from
|
||||||
|
# the correct repository when installing boot packages.
|
||||||
|
apt_selection = debian_cd_dir.joinpath("tools/apt-selection")
|
||||||
|
with self.logger.logged("overwriting apt-selection"):
|
||||||
|
apt_selection.write_text(
|
||||||
|
"#!/bin/sh\n" f"APT_CONFIG={self.apt_state.apt_conf_path} apt-get $@\n"
|
||||||
|
)
|
||||||
|
env = dict(
|
||||||
|
os.environ,
|
||||||
|
BASEDIR=str(debian_cd_dir),
|
||||||
|
DIST=self.series,
|
||||||
|
PROJECT=project,
|
||||||
|
CAPPROJECT=capproject,
|
||||||
|
SUBARCH=subarch,
|
||||||
|
)
|
||||||
|
tool_name = f"tools/boot/{self.series}/boot-{self.arch}"
|
||||||
|
with self.logger.logged(f"running {tool_name} ...", done_msg="... done"):
|
||||||
|
self.logger.run(
|
||||||
|
[
|
||||||
|
debian_cd_dir.joinpath(tool_name),
|
||||||
|
"1",
|
||||||
|
self.iso_root,
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
def checksum(self):
|
||||||
|
# Generate md5sum.txt for ISO integrity verification.
|
||||||
|
# - Symlinks are excluded because their targets are already checksummed
|
||||||
|
# - Files are sorted for deterministic, reproducible output across builds
|
||||||
|
# - Paths use "./" prefix and we run md5sum from iso_root so the output
|
||||||
|
# matches what casper-md5check expects.
|
||||||
|
all_files = []
|
||||||
|
for dirpath, dirnames, filenames in self.iso_root.walk():
|
||||||
|
filepaths = [dirpath.joinpath(filename) for filename in filenames]
|
||||||
|
all_files.extend(
|
||||||
|
"./" + str(filepath.relative_to(self.iso_root))
|
||||||
|
for filepath in filepaths
|
||||||
|
if not filepath.is_symlink()
|
||||||
|
)
|
||||||
|
self.iso_root.joinpath("md5sum.txt").write_bytes(
|
||||||
|
self.logger.run(
|
||||||
|
["md5sum"] + sorted(all_files),
|
||||||
|
cwd=self.iso_root,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_iso(self, dest: pathlib.Path, volid: str | None):
|
||||||
|
# 1.mkisofs_opts is generated by debian-cd's make_bootable step. The "1"
|
||||||
|
# refers to "pass 1" of the build (a legacy naming convention). It contains
|
||||||
|
# architecture-specific xorriso options for boot sectors, EFI images, etc.
|
||||||
|
mkisofs_opts = shlex.split(self.workdir.joinpath("1.mkisofs_opts").read_text())
|
||||||
|
self.checksum()
|
||||||
|
# xorriso with "-as mkisofs" runs in mkisofs compatibility mode.
|
||||||
|
# -r enables Rock Ridge extensions for Unix metadata (permissions, symlinks).
|
||||||
|
# -iso-level 3 (amd64 only) allows files >4GB which some amd64 ISOs need.
|
||||||
|
cmd: list[str | pathlib.Path] = ["xorriso"]
|
||||||
|
if self.arch == "riscv64":
|
||||||
|
# For $reasons, xorriso is not run in mkisofs mode on riscv64 only.
|
||||||
|
cmd.extend(["-rockridge", "on", "-outdev", dest])
|
||||||
|
if volid:
|
||||||
|
cmd.extend(["-volid", volid])
|
||||||
|
cmd.extend(mkisofs_opts)
|
||||||
|
cmd.extend(["-map", self.iso_root, "/"])
|
||||||
|
else:
|
||||||
|
# xorriso with "-as mkisofs" runs in mkisofs compatibility mode on
|
||||||
|
# other architectures. -r enables Rock Ridge extensions for Unix
|
||||||
|
# metadata (permissions, symlinks). -iso-level 3 (amd64 only)
|
||||||
|
# allows files >4GB which some amd64 ISOs need.
|
||||||
|
cmd.extend(["-as", "mkisofs", "-r"])
|
||||||
|
if self.arch == "amd64":
|
||||||
|
cmd.extend(["-iso-level", "3"])
|
||||||
|
if volid:
|
||||||
|
cmd.extend(["-V", volid])
|
||||||
|
cmd.extend(mkisofs_opts + [self.iso_root, "-o", dest])
|
||||||
|
with self.logger.logged("running xorriso"):
|
||||||
|
self.logger.run(cmd, cwd=self.workdir, check=True, limit_length=False)
|
||||||
|
if self.arch == "riscv64":
|
||||||
|
debian_cd_dir = self.workdir.joinpath("debian-cd")
|
||||||
|
add_riscv_gpt = debian_cd_dir.joinpath("tools/add_riscv_gpt")
|
||||||
|
self.logger.run([add_riscv_gpt, dest], cwd=self.workdir)
|
||||||
58
live-build/isobuilder/gpg_key.py
Normal file
58
live-build/isobuilder/gpg_key.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
key_conf = """\
|
||||||
|
%no-protection
|
||||||
|
Key-Type: eddsa
|
||||||
|
Key-Curve: Ed25519
|
||||||
|
Key-Usage: sign
|
||||||
|
Name-Real: Ubuntu ISO One-Time Signing Key
|
||||||
|
Name-Email: noone@nowhere.invalid
|
||||||
|
Expire-Date: 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class EphemeralGPGKey:
|
||||||
|
|
||||||
|
def __init__(self, logger, gpghome):
|
||||||
|
self.logger = logger
|
||||||
|
self.gpghome = gpghome
|
||||||
|
|
||||||
|
def _run_gpg(self, cmd, **kwargs):
|
||||||
|
return self.logger.run(
|
||||||
|
["gpg", "--homedir", self.gpghome] + cmd, check=True, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
with self.logger.logged("creating gpg key ...", done_msg="... done"):
|
||||||
|
self.gpghome.mkdir(mode=0o700)
|
||||||
|
self._run_gpg(
|
||||||
|
["--gen-key", "--batch"],
|
||||||
|
input=key_conf,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def sign(self, path: pathlib.Path):
|
||||||
|
with self.logger.logged(f"signing {path}"):
|
||||||
|
with path.open("rb") as inp:
|
||||||
|
with pathlib.Path(str(path) + ".gpg").open("wb") as outp:
|
||||||
|
self._run_gpg(
|
||||||
|
[
|
||||||
|
"--no-options",
|
||||||
|
"--batch",
|
||||||
|
"--no-tty",
|
||||||
|
"--armour",
|
||||||
|
"--digest-algo",
|
||||||
|
"SHA512",
|
||||||
|
"--detach-sign",
|
||||||
|
],
|
||||||
|
stdin=inp,
|
||||||
|
stdout=outp,
|
||||||
|
)
|
||||||
|
|
||||||
|
def export_public(self) -> str:
|
||||||
|
return self._run_gpg(
|
||||||
|
["--export", "--armor"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
).stdout
|
||||||
166
live-build/isobuilder/pool_builder.py
Normal file
166
live-build/isobuilder/pool_builder.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from isobuilder.apt_state import AptStateManager, PackageInfo
|
||||||
|
|
||||||
|
|
||||||
|
generate_template = """
|
||||||
|
Dir::ArchiveDir "{root}";
|
||||||
|
Dir::CacheDir "{scratch}/apt-ftparchive-db";
|
||||||
|
|
||||||
|
TreeDefault::Contents " ";
|
||||||
|
|
||||||
|
Tree "dists/{series}" {{
|
||||||
|
FileList "{scratch}/filelist_$(SECTION)";
|
||||||
|
Sections "{components}";
|
||||||
|
Architectures "{arches}";
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class PoolBuilder:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, logger, series: str, apt_state: AptStateManager, rootdir: pathlib.Path
|
||||||
|
):
|
||||||
|
self.logger = logger
|
||||||
|
self.series = series
|
||||||
|
self.apt_state = apt_state
|
||||||
|
self.rootdir = rootdir
|
||||||
|
self.arches: set[str] = set()
|
||||||
|
self._present_components: set[str] = set()
|
||||||
|
|
||||||
|
def add_packages(self, pkglist: list[PackageInfo]):
|
||||||
|
for pkg_info in pkglist:
|
||||||
|
if pkg_info.architecture != "all":
|
||||||
|
self.arches.add(pkg_info.architecture)
|
||||||
|
self.apt_state.download(self.rootdir, pkg_info)
|
||||||
|
|
||||||
|
def make_packages(self) -> None:
|
||||||
|
with self.logger.logged("making Packages files"):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
scratchdir = pathlib.Path(tmpdir)
|
||||||
|
with self.logger.logged("scanning for packages"):
|
||||||
|
for component in ["main", "restricted", "universe", "multiverse"]:
|
||||||
|
if not self.rootdir.joinpath("pool", component).is_dir():
|
||||||
|
continue
|
||||||
|
self._present_components.add(component)
|
||||||
|
for arch in self.arches:
|
||||||
|
self.rootdir.joinpath(
|
||||||
|
"dists", self.series, component, f"binary-{arch}"
|
||||||
|
).mkdir(parents=True)
|
||||||
|
proc = self.logger.run(
|
||||||
|
["find", f"pool/{component}"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
cwd=self.rootdir,
|
||||||
|
encoding="utf-8",
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
scratchdir.joinpath(f"filelist_{component}").write_text(
|
||||||
|
"\n".join(sorted(proc.stdout.splitlines()))
|
||||||
|
)
|
||||||
|
with self.logger.logged("writing apt-ftparchive config"):
|
||||||
|
scratchdir.joinpath("apt-ftparchive-db").mkdir()
|
||||||
|
generate_path = scratchdir.joinpath("generate-binary")
|
||||||
|
generate_path.write_text(
|
||||||
|
generate_template.format(
|
||||||
|
arches=" ".join(self.arches),
|
||||||
|
series=self.series,
|
||||||
|
root=self.rootdir.resolve(),
|
||||||
|
scratch=scratchdir.resolve(),
|
||||||
|
components=" ".join(self._present_components),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
with self.logger.logged("running apt-ftparchive generate"):
|
||||||
|
self.logger.run(
|
||||||
|
[
|
||||||
|
"apt-ftparchive",
|
||||||
|
"--no-contents",
|
||||||
|
"--no-md5",
|
||||||
|
"--no-sha1",
|
||||||
|
"--no-sha512",
|
||||||
|
"generate",
|
||||||
|
generate_path,
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_release(self) -> pathlib.Path:
|
||||||
|
# Build the Release file by merging metadata from the mirror with
|
||||||
|
# checksums for our pool. We can't just use apt-ftparchive's Release
|
||||||
|
# output directly because:
|
||||||
|
# 1. apt-ftparchive doesn't know about Origin, Label, Suite, Version,
|
||||||
|
# Codename, etc. - these come from the mirror and maintain package
|
||||||
|
# provenance
|
||||||
|
# 2. We keep the mirror's Date (when packages were released) rather than
|
||||||
|
# apt-ftparchive's Date (when we ran the command)
|
||||||
|
# 3. We need to override Architectures/Components to match our pool
|
||||||
|
#
|
||||||
|
# There may be a cleaner way (apt-get indextargets?) but this works.
|
||||||
|
with self.logger.logged("making Release file"):
|
||||||
|
in_release = self.apt_state.in_release_path()
|
||||||
|
cp_mirror_release = self.logger.run(
|
||||||
|
["gpg", "--verify", "--output", "-", in_release],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
encoding="utf-8",
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if cp_mirror_release.returncode not in (0, 2):
|
||||||
|
# gpg returns code 2 when the public key the InRelease is
|
||||||
|
# signed with is not available, which is most of the time.
|
||||||
|
raise Exception("gpg failed")
|
||||||
|
mirror_release_lines = cp_mirror_release.stdout.splitlines()
|
||||||
|
release_dir = self.rootdir.joinpath("dists", self.series)
|
||||||
|
af_release_lines = self.logger.run(
|
||||||
|
[
|
||||||
|
"apt-ftparchive",
|
||||||
|
"--no-contents",
|
||||||
|
"--no-md5",
|
||||||
|
"--no-sha1",
|
||||||
|
"--no-sha512",
|
||||||
|
"release",
|
||||||
|
".",
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
encoding="utf-8",
|
||||||
|
cwd=release_dir,
|
||||||
|
check=True,
|
||||||
|
).stdout.splitlines()
|
||||||
|
# Build the final Release file by merging mirror metadata with pool
|
||||||
|
# checksums.
|
||||||
|
# Strategy:
|
||||||
|
# 1. Take metadata fields (Suite, Origin, etc.) from the mirror's InRelease
|
||||||
|
# 2. Override Architectures and Components to match what's actually in our
|
||||||
|
# pool
|
||||||
|
# 3. Skip the mirror's checksum sections (MD5Sum, SHA256, etc.) because they
|
||||||
|
# don't apply to our pool
|
||||||
|
# 4. Skip Acquire-By-Hash since we don't use it
|
||||||
|
# 5. Append checksums from apt-ftparchive (but not the Date field)
|
||||||
|
release_lines = []
|
||||||
|
skipping = False
|
||||||
|
for line in mirror_release_lines:
|
||||||
|
if line.startswith("Architectures:"):
|
||||||
|
line = "Architectures: " + " ".join(sorted(self.arches))
|
||||||
|
elif line.startswith("Components:"):
|
||||||
|
line = "Components: " + " ".join(sorted(self._present_components))
|
||||||
|
elif line.startswith("MD5") or line.startswith("SHA"):
|
||||||
|
# Start of a checksum section - skip this and indented lines below
|
||||||
|
# it
|
||||||
|
skipping = True
|
||||||
|
elif not line.startswith(" "):
|
||||||
|
# Non-indented line means we've left the checksum section if we were
|
||||||
|
# in one.
|
||||||
|
skipping = False
|
||||||
|
if line.startswith("Acquire-By-Hash"):
|
||||||
|
continue
|
||||||
|
if not skipping:
|
||||||
|
release_lines.append(line)
|
||||||
|
# Append checksums from apt-ftparchive, but skip its Date field
|
||||||
|
# (we want to keep the Date from the mirror release)
|
||||||
|
for line in af_release_lines:
|
||||||
|
if not line.startswith("Date"):
|
||||||
|
release_lines.append(line)
|
||||||
|
release_path = release_dir.joinpath("Release")
|
||||||
|
release_path.write_text("\n".join(release_lines))
|
||||||
|
return release_path
|
||||||
@ -184,6 +184,18 @@ build_layered_squashfs () {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
create_squashfs "${overlay_dir}" ${squashfs_f}
|
create_squashfs "${overlay_dir}" ${squashfs_f}
|
||||||
|
# Create a "for-iso" variant of the squashfs for ISO builds. For
|
||||||
|
# the root layer (the base system) when building with a pool, we
|
||||||
|
# need to include cdrom.sources so casper can access the ISO's
|
||||||
|
# package repository. This requires regenerating the squashfs with
|
||||||
|
# that file included, then removing it (so it doesn't pollute the
|
||||||
|
# regular squashfs). Non-root layers (desktop environment, etc.)
|
||||||
|
# and builds without pools can just hardlink to the regular squashfs.
|
||||||
|
if [ -n "${POOL_SEED_NAME}" ] && $(is_root_layer $pass); then
|
||||||
|
isobuild generate-sources --mountpoint=/cdrom > ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources
|
||||||
|
create_squashfs "${overlay_dir}" ${PWD}/for-iso.${pass}.squashfs
|
||||||
|
rm ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f config/$pass.catalog-in.yaml ]; then
|
if [ -f config/$pass.catalog-in.yaml ]; then
|
||||||
echo "Expanding catalog entry template for $pass"
|
echo "Expanding catalog entry template for $pass"
|
||||||
@ -227,3 +239,11 @@ if [ -n "$(ls livecd.${PROJECT_FULL}.*install.live.manifest.full 2>/dev/null)" ]
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 644 *.squashfs *.manifest* *.size
|
chmod 644 *.squashfs *.manifest* *.size
|
||||||
|
|
||||||
|
prefix=livecd.${PROJECT_FULL}
|
||||||
|
for artifact in ${prefix}.*; do
|
||||||
|
for_iso_path=for-iso${artifact#${prefix}}
|
||||||
|
if [ ! -f $for_iso_path ]; then
|
||||||
|
ln -v $artifact $for_iso_path
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|||||||
53
live-build/snap-seed-missing-providers.py
Executable file
53
live-build/snap-seed-missing-providers.py
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Usage: snap-seed-add-providers $SEED_DIR
|
||||||
|
|
||||||
|
Check the snaps referenced by the $SEED_DIR/seed.yaml and print any
|
||||||
|
missing providers.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def mounted(thing, target):
|
||||||
|
subprocess.run(["mount", "-r", thing, target], check=True)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
subprocess.run(["umount", target], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
seed_dir = pathlib.Path(argv[0])
|
||||||
|
assert seed_dir.is_dir()
|
||||||
|
with seed_dir.joinpath("seed.yaml").open() as fp:
|
||||||
|
seed = yaml.safe_load(fp)
|
||||||
|
snap_files = set()
|
||||||
|
snap_names = set()
|
||||||
|
for snap_info in seed["snaps"]:
|
||||||
|
snap_files.add(snap_info["file"])
|
||||||
|
snap_names.add(snap_info["name"])
|
||||||
|
tempdir = pathlib.Path(tempfile.mkdtemp())
|
||||||
|
for snap_file in snap_files:
|
||||||
|
snap = seed_dir.joinpath("snaps", snap_file)
|
||||||
|
with mounted(snap, tempdir):
|
||||||
|
with tempdir.joinpath("meta/snap.yaml").open() as fp:
|
||||||
|
metadata = yaml.safe_load(fp)
|
||||||
|
for plug_name, plug_info in metadata.get("plugs", {}).items():
|
||||||
|
if plug_info.get("interface") == "content":
|
||||||
|
default_provider = plug_info.get("default-provider")
|
||||||
|
if default_provider and default_provider not in snap_names:
|
||||||
|
print(default_provider)
|
||||||
|
snap_names.add(default_provider)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1:])
|
||||||
@ -24,7 +24,10 @@ create_partitions() {
|
|||||||
sgdisk "${disk_image}" \
|
sgdisk "${disk_image}" \
|
||||||
--new=2::+8M \
|
--new=2::+8M \
|
||||||
--new=1:
|
--new=1:
|
||||||
sgdisk "${disk_image}" -t 2:4100
|
sgdisk "${disk_image}" \
|
||||||
|
--change-name=1:"$FS_LABEL" \
|
||||||
|
-t 2:4100 \
|
||||||
|
-t 1:"$(gpt_root_partition_uuid $ARCH)"
|
||||||
sgdisk "${disk_image}" \
|
sgdisk "${disk_image}" \
|
||||||
--print
|
--print
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ if [ "$ARCH" = "riscv64" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
case ${PROJECT:-}:${SUBPROJECT:-} in
|
case ${PROJECT:-}:${SUBPROJECT:-} in
|
||||||
ubuntu:)
|
ubuntu:|ubuntu:dangerous)
|
||||||
echo "We don't create EFI images for Ubuntu Desktop."
|
echo "We don't create EFI images for Ubuntu Desktop."
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
@ -70,105 +70,12 @@ create_partitions() {
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
riscv64)
|
riscv64)
|
||||||
# same as arm64/armhf, but set bit 2 legacy bios bootable
|
sgdisk "${disk_image}" \
|
||||||
# on the first partition for uboot
|
--new=15::+106M \
|
||||||
# and have two loader partitions of uboot SPL & real one
|
--typecode=15:ef00 \
|
||||||
# and have CIDATA partition for preinstalled image
|
--new=12::+4M \
|
||||||
if [ -z "${SUBARCH:-}" ]; then
|
--change-name=12:CIDATA \
|
||||||
# cloud-image
|
--new=1:: \
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=2 \
|
|
||||||
--new=15::+106M \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--new=1:: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
elif [ "${SUBARCH:-}" = "nezha" ] || [ "${SUBARCH:-}" = "licheerv" ]; then
|
|
||||||
# Nezha/LicheeRV D1 boards
|
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=2 \
|
|
||||||
--new=13:256:25575 \
|
|
||||||
--change-name=13:loader1 \
|
|
||||||
--typecode=13:B161E8AB-7D4B-4DB4-821C-4120A0554A35 \
|
|
||||||
--attributes=13:set:0 \
|
|
||||||
--new=16:25576:32799 \
|
|
||||||
--change-name=16:loader2b \
|
|
||||||
--typecode=16:F79E76D9-AC98-418B-8F31-E17EA24FF07C \
|
|
||||||
--attributes=16:set:0 \
|
|
||||||
--new=14:32800:43007 \
|
|
||||||
--change-name=14:loader2 \
|
|
||||||
--typecode=14:F4FA3898-3478-4941-887D-FCEC4E9E3C05 \
|
|
||||||
--attributes=14:set:0 \
|
|
||||||
--new=15::+106M \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--change-name=15:ESP \
|
|
||||||
--new=12::+4M \
|
|
||||||
--change-name=12:CIDATA \
|
|
||||||
--new=1:: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
elif [ "${SUBARCH:-}" = "icicle" ] || [ "${SUBARCH:-}" = "pic64gx" ]; then
|
|
||||||
# Microchip Icicle Kit
|
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=2 \
|
|
||||||
--new=13:256:25575 \
|
|
||||||
--change-name=13:loader \
|
|
||||||
--typecode=13:ef02 \
|
|
||||||
--attributes=13:set:0 \
|
|
||||||
--new=15::+106M \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--change-name=15:ESP \
|
|
||||||
--new=12::+4M \
|
|
||||||
--change-name=12:CIDATA \
|
|
||||||
--new=1:: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
elif [ "${SUBARCH:-}" = "visionfive" ]; then
|
|
||||||
# VisionFive
|
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=2 \
|
|
||||||
--new=15::+106M \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--change-name=15:ESP \
|
|
||||||
--new=12::+4M \
|
|
||||||
--change-name=12:CIDATA \
|
|
||||||
--new=3::+1M \
|
|
||||||
--change-name=3:uEnv \
|
|
||||||
--new=1:: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
elif [ "${SUBARCH:-}" = "visionfive2" ] || [ "${SUBARCH:-}" = "milkvmars" ] || [ "${SUBARCH:-}" = "jh7110" ]; then
|
|
||||||
# JH7110 chips: VisionFive 2, Milk-V Mars
|
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=4096 \
|
|
||||||
--new=13:4096:8191 \
|
|
||||||
--typecode=13:2E54B353-1271-4842-806F-E436D6AF6985 \
|
|
||||||
--change-name=13:loader1 \
|
|
||||||
--new=2:8192:40959 \
|
|
||||||
--typecode=2:7a097280-70d2-44bc-886c-ff5ffbb7b098 \
|
|
||||||
--change-name=2:loader2 \
|
|
||||||
--new=12:40960:49151 \
|
|
||||||
--change-name=12:CIDATA \
|
|
||||||
--new=15:49152:253951 \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--change-name=15:ESP \
|
|
||||||
--new=1:253952: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
else
|
|
||||||
# preinstalled server, currently FU540
|
|
||||||
# FU740 too in the future
|
|
||||||
sgdisk "${disk_image}" \
|
|
||||||
--set-alignment=2 \
|
|
||||||
--new=13:34:2081 \
|
|
||||||
--change-name=13:loader1 \
|
|
||||||
--typecode=13:5B193300-FC78-40CD-8002-E86C45580B47 \
|
|
||||||
--attributes=13:set:0 \
|
|
||||||
--new=14:2082:10239 \
|
|
||||||
--change-name=14:loader2 \
|
|
||||||
--typecode=14:2E54B353-1271-4842-806F-E436D6AF6985 \
|
|
||||||
--attributes=14:set:0 \
|
|
||||||
--new=15::+106M \
|
|
||||||
--typecode=15:ef00 \
|
|
||||||
--new=12::+4M \
|
|
||||||
--change-name=12:CIDATA \
|
|
||||||
--new=1:: \
|
|
||||||
--attributes=1:set:2
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
amd64)
|
amd64)
|
||||||
@ -265,126 +172,31 @@ install_grub() {
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
riscv64)
|
riscv64)
|
||||||
if [ -n "${SUBARCH:-}" ]; then
|
# Per-device images
|
||||||
# Per-device images
|
local my_d=$(dirname $(readlink -f ${0}))
|
||||||
local my_d=$(dirname $(readlink -f ${0}))
|
echo "Adjusting GRUB defaults for ${ARCH}/${SUBARCH}"
|
||||||
echo "Adjusting GRUB defaults for ${ARCH}"
|
mkdir -p mountpoint/etc/default/grub.d/
|
||||||
mkdir -p mountpoint/etc/default/grub.d/
|
rm -rf mountpoint/etc/default/grub.d/*
|
||||||
cp ${my_d}/riscv64/grub/10_cmdline.cfg mountpoint/etc/default/grub.d/
|
cp ${my_d}/riscv64/grub/10_cmdline.cfg mountpoint/etc/default/grub.d/
|
||||||
echo "Installing GRUB for ${SUBARCH} board"
|
echo "Copying device trees"
|
||||||
case "${SUBARCH}" in
|
kver=$(ls mountpoint/lib/modules | sort -V | tail -n 1)
|
||||||
"icicle")
|
dtb_src_dirs=(
|
||||||
cp ${my_d}/riscv64/grub/90_watchdog-thresh.cfg mountpoint/etc/default/grub.d/
|
"mountpoint/usr/lib/linux-image-$kver"
|
||||||
# The real U-Boot
|
"mountpoint/lib/firmware/$kver/device-tree"
|
||||||
chroot mountpoint apt-get install -qqy u-boot-microchip
|
)
|
||||||
loader="${loop_device}p13"
|
dtb_tgt_dir="mountpoint/boot/efi/dtb/"
|
||||||
dd if=mountpoint/usr/lib/u-boot/microchip_icicle/u-boot.payload of=$loader
|
mkdir -p "$dtb_tgt_dir"
|
||||||
;;
|
for src_dir in "${dtb_src_dirs[@]}"; do
|
||||||
"nezha"|"licheerv")
|
[ -d "$src_dir" ] && cp -r -v "$src_dir"/* "$dtb_tgt_dir" || echo "Skipping missing: $src_dir"
|
||||||
echo "Reducing initramfs size for ${SUBARCH} board"
|
done
|
||||||
mkdir -p mountpoint/etc/initramfs-tools/conf.d/
|
chroot mountpoint bash -c 'FK_FORCE=yes apt-get install -qqy grub-efi-riscv64 flash-kernel'
|
||||||
cp ${my_d}/riscv64/initramfs-tools/modules_list.conf mountpoint/etc/initramfs-tools/conf.d/
|
efi_target=riscv64-efi
|
||||||
cat ${my_d}/riscv64/initramfs-tools/allwinner >> mountpoint/etc/initramfs-tools/modules
|
# Provide end-user modifyable CIDATA
|
||||||
chroot mountpoint update-initramfs -c -v -k all
|
cidata_dev="${loop_device}p12"
|
||||||
echo "Installing U-Boot for ${SUBARCH} board"
|
setup_cidata "${cidata_dev}"
|
||||||
if [ "$SUBARCH" = "licheerv" ]; then
|
# Provide stock nocloud datasource
|
||||||
# cryptsetup-initramfs is a large contributor of the initrd size: we have to
|
# Allow interactive login without a cloud datasource.
|
||||||
# remove it for the LicheeRV board, otherwise it fails to boot. cryptsetup-initramfs
|
setup_cinocloud mountpoint
|
||||||
# needs to embed plymouth (and then the drm/gpu stuff) for interacting with the user
|
|
||||||
# to decrypt the rootfs (passphrase key).
|
|
||||||
chroot mountpoint bash -c "apt remove -qqy cryptsetup-initramfs"
|
|
||||||
fi
|
|
||||||
# u-boot-nezha supports both the LicheeRV and the Nezha D1.
|
|
||||||
chroot mountpoint apt-get install -qqy u-boot-nezha
|
|
||||||
# Since version 2022.10 U-Boot SPL and U-Boot are installed onto the same partition.
|
|
||||||
# Package nezha-boot0 is not needed anymore.
|
|
||||||
loader1="${loop_device}p13"
|
|
||||||
dd if=mountpoint/usr/lib/u-boot/${SUBARCH}/u-boot-sunxi-with-spl.bin of=$loader1
|
|
||||||
;;
|
|
||||||
"pic64gx")
|
|
||||||
cp ${my_d}/riscv64/grub/90_watchdog-thresh.cfg mountpoint/etc/default/grub.d/
|
|
||||||
# u-boot-pic64gx contains the vendor U-Boot
|
|
||||||
chroot mountpoint apt-get install -qqy u-boot-pic64gx
|
|
||||||
loader="${loop_device}p13"
|
|
||||||
dd if=mountpoint/usr/lib/u-boot-pic64gx/u-boot.payload of=$loader
|
|
||||||
;;
|
|
||||||
"visionfive")
|
|
||||||
# factory u-boot requires a p3 partition with /boot/uEnv.txt file
|
|
||||||
uenv_dev="${loop_device}p3"
|
|
||||||
mkfs.ext4 "${uenv_dev}"
|
|
||||||
uenv_mnt_dir=`mktemp -d uenvXXX`
|
|
||||||
mount "${uenv_dev}" "${uenv_mnt_dir}"
|
|
||||||
mkdir -p "${uenv_mnt_dir}"/boot
|
|
||||||
|
|
||||||
cat <<'EOF' >${uenv_mnt_dir}/boot/uEnv.txt
|
|
||||||
scriptaddr=0x88100000
|
|
||||||
script_offset_f=0x1fff000
|
|
||||||
script_size_f=0x1000
|
|
||||||
|
|
||||||
kernel_addr_r=0x84000000
|
|
||||||
kernel_comp_addr_r=0x90000000
|
|
||||||
kernel_comp_size=0x10000000
|
|
||||||
|
|
||||||
fdt_addr_r=0x88000000
|
|
||||||
ramdisk_addr_r=0x88300000
|
|
||||||
|
|
||||||
bootcmd=load mmc 0:f ${kernel_addr_r} /EFI/ubuntu/grubriscv64.efi; bootefi ${kernel_addr_r}
|
|
||||||
bootcmd_mmc0=devnum=0; run mmc_boot
|
|
||||||
|
|
||||||
ipaddr=192.168.120.200
|
|
||||||
netmask=255.255.255.0
|
|
||||||
EOF
|
|
||||||
|
|
||||||
umount "${uenv_mnt_dir}"
|
|
||||||
rmdir "${uenv_mnt_dir}"
|
|
||||||
;;
|
|
||||||
"visionfive2"|"milkvmars"|"jh7110")
|
|
||||||
cp ${my_d}/riscv64/grub/90_watchdog-thresh.cfg mountpoint/etc/default/grub.d/
|
|
||||||
chroot mountpoint apt-get install -qqy u-boot-starfive
|
|
||||||
# U-Boot SPL
|
|
||||||
loader1="${loop_device}p13"
|
|
||||||
# Main U-Boot
|
|
||||||
loader2="${loop_device}p2"
|
|
||||||
dd if=mountpoint/usr/lib/u-boot/starfive_visionfive2/u-boot-spl.bin.normal.out of=$loader1
|
|
||||||
dd if=mountpoint/usr/lib/u-boot/starfive_visionfive2/u-boot.itb of=$loader2
|
|
||||||
;;
|
|
||||||
unmatched)
|
|
||||||
cp ${my_d}/riscv64/grub/90_watchdog-thresh.cfg mountpoint/etc/default/grub.d/
|
|
||||||
chroot mountpoint apt-get install -qqy u-boot-sifive
|
|
||||||
# U-Boot SPL
|
|
||||||
loader1="${loop_device}p13"
|
|
||||||
# Main U-Boot
|
|
||||||
loader2="${loop_device}p14"
|
|
||||||
dd if=mountpoint/usr/lib/u-boot/sifive_unmatched/u-boot-spl.bin of=$loader1
|
|
||||||
dd if=mountpoint/usr/lib/u-boot/sifive_unmatched/u-boot.itb of=$loader2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
echo "Copying device trees"
|
|
||||||
kver=$(ls mountpoint/lib/modules | sort -V | tail -n 1)
|
|
||||||
dtb_src_dirs=(
|
|
||||||
"mountpoint/usr/lib/linux-image-$kver"
|
|
||||||
"mountpoint/lib/firmware/$kver/device-tree"
|
|
||||||
)
|
|
||||||
dtb_tgt_dir="mountpoint/boot/efi/dtb/"
|
|
||||||
mkdir -p "$dtb_tgt_dir"
|
|
||||||
for src_dir in "${dtb_src_dirs[@]}"; do
|
|
||||||
[ -d "$src_dir" ] && cp -r -v "$src_dir"/* "$dtb_tgt_dir" || echo "Skipping missing: $src_dir"
|
|
||||||
done
|
|
||||||
chroot mountpoint bash -c 'FK_FORCE=yes apt-get install -qqy grub-efi-riscv64 flash-kernel'
|
|
||||||
efi_target=riscv64-efi
|
|
||||||
# Provide end-user modifyable CIDATA
|
|
||||||
cidata_dev="${loop_device}p12"
|
|
||||||
setup_cidata "${cidata_dev}"
|
|
||||||
# Provide stock nocloud datasource
|
|
||||||
# Allow interactive login without a cloud datasource.
|
|
||||||
setup_cinocloud mountpoint
|
|
||||||
else
|
|
||||||
# Other images e.g. cloud images
|
|
||||||
chroot mountpoint apt-get install -qqy u-boot-menu grub-efi-riscv64
|
|
||||||
efi_target=riscv64-efi
|
|
||||||
|
|
||||||
chroot mountpoint u-boot-update
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,9 @@ create_partitions() {
|
|||||||
--typecode=15:ef00 \
|
--typecode=15:ef00 \
|
||||||
--new=13::1G \
|
--new=13::1G \
|
||||||
--typecode=13:ea00 \
|
--typecode=13:ea00 \
|
||||||
--new=1:
|
--new=1: \
|
||||||
|
--change-name=1:"$FS_LABEL" \
|
||||||
|
--typecode=1:"$(gpt_root_partition_uuid $ARCH)"
|
||||||
;;
|
;;
|
||||||
riscv64)
|
riscv64)
|
||||||
sgdisk "${disk_image}" \
|
sgdisk "${disk_image}" \
|
||||||
@ -51,6 +53,8 @@ create_partitions() {
|
|||||||
--new=15::+106M \
|
--new=15::+106M \
|
||||||
--typecode=15:ef00 \
|
--typecode=15:ef00 \
|
||||||
--new=1:: \
|
--new=1:: \
|
||||||
|
--change-name=1:"$FS_LABEL" \
|
||||||
|
--typecode=1:"$(gpt_root_partition_uuid $ARCH)" \
|
||||||
--attributes=1:set:2
|
--attributes=1:set:2
|
||||||
;;
|
;;
|
||||||
amd64)
|
amd64)
|
||||||
@ -61,8 +65,10 @@ create_partitions() {
|
|||||||
--new=15::+106M \
|
--new=15::+106M \
|
||||||
--new=1::
|
--new=1::
|
||||||
sgdisk "${disk_image}" \
|
sgdisk "${disk_image}" \
|
||||||
|
--change-name=1:"$FS_LABEL" \
|
||||||
-t 14:ef02 \
|
-t 14:ef02 \
|
||||||
-t 15:ef00
|
-t 15:ef00 \
|
||||||
|
-t 1:"$(gpt_root_partition_uuid $ARCH)"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
sgdisk "${disk_image}" \
|
sgdisk "${disk_image}" \
|
||||||
|
|||||||
@ -14,6 +14,10 @@ ROOTPART_START=1
|
|||||||
|
|
||||||
my_d=$(dirname $(readlink -f ${0}))
|
my_d=$(dirname $(readlink -f ${0}))
|
||||||
|
|
||||||
|
# NEW CPC default imagesize is 2.5G [2684354560 bytes] to avail of the slow increase in pkg sizes
|
||||||
|
# This value will be passed into live-build/functions as an override
|
||||||
|
imagesize=2684354560
|
||||||
|
|
||||||
case $ARCH:$SUBARCH in
|
case $ARCH:$SUBARCH in
|
||||||
ppc64el:*|powerpc:*)
|
ppc64el:*|powerpc:*)
|
||||||
echo "POWER disk images are handled separately"
|
echo "POWER disk images are handled separately"
|
||||||
@ -95,6 +99,7 @@ if [ "${should_install_grub}" -eq 1 ]; then
|
|||||||
echo "(hd0) ${loop_device}" > mountpoint/tmp/device.map
|
echo "(hd0) ${loop_device}" > mountpoint/tmp/device.map
|
||||||
# install the required package to get the grub-install command
|
# install the required package to get the grub-install command
|
||||||
chroot mountpoint apt-get -qqy install --no-install-recommends grub-pc grub2-common
|
chroot mountpoint apt-get -qqy install --no-install-recommends grub-pc grub2-common
|
||||||
|
chroot mountpoint df --all --human-readable --print-type
|
||||||
chroot mountpoint grub-install ${loop_device}
|
chroot mountpoint grub-install ${loop_device}
|
||||||
chroot mountpoint grub-bios-setup \
|
chroot mountpoint grub-bios-setup \
|
||||||
--boot-image=i386-pc/boot.img \
|
--boot-image=i386-pc/boot.img \
|
||||||
|
|||||||
@ -2,3 +2,4 @@
|
|||||||
# For minimum output use
|
# For minimum output use
|
||||||
# GRUB_CMDLINE_LINUX_DEFAULT="quiet"
|
# GRUB_CMDLINE_LINUX_DEFAULT="quiet"
|
||||||
GRUB_CMDLINE_LINUX_DEFAULT="efi=debug earlycon=sbi"
|
GRUB_CMDLINE_LINUX_DEFAULT="efi=debug earlycon=sbi"
|
||||||
|
GRUB_TERMINAL=console
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
# When booting with Radeon GPUs a soft lockup was observed. Increase the
|
|
||||||
# watchdog threshhold.
|
|
||||||
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT sysctl.kernel.watchdog_thresh=60"
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# Required drivers to boot off MMC
|
|
||||||
mmc-block
|
|
||||||
sunxi-mmc
|
|
||||||
@ -1 +0,0 @@
|
|||||||
U_BOOT_PARAMETERS="ro efi=debug earlycon=sbi"
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# When booting with Radeon GPUs a soft lockup was observed. Increase the
|
|
||||||
# watchdog threshhold.
|
|
||||||
U_BOOT_PARAMETERS="$U_BOOT_PARAMETERS sysctl.kernel.watchdog_thresh=60"
|
|
||||||
@ -1,5 +1,18 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
generate_grub_config ()
|
||||||
|
{
|
||||||
|
# Generate the grub config file for netboot tarballs
|
||||||
|
# $1 Generated file path
|
||||||
|
cat > "${1}" <<EOF
|
||||||
|
menuentry "Install Ubuntu Server" {
|
||||||
|
set gfxpayload=keep
|
||||||
|
linux linux iso-url=#ISOURL# ip=dhcp ---
|
||||||
|
initrd initrd
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
case $PASS in
|
case $PASS in
|
||||||
ubuntu-server-minimal.ubuntu-server.installer.generic*.netboot)
|
ubuntu-server-minimal.ubuntu-server.installer.generic*.netboot)
|
||||||
;;
|
;;
|
||||||
@ -46,13 +59,7 @@ case $ARCH in
|
|||||||
mv chroot/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed tarball/amd64/grubx64.efi
|
mv chroot/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed tarball/amd64/grubx64.efi
|
||||||
|
|
||||||
mkdir tarball/amd64/grub tarball/amd64/pxelinux.cfg
|
mkdir tarball/amd64/grub tarball/amd64/pxelinux.cfg
|
||||||
cat > tarball/amd64/grub/grub.cfg.in <<EOF
|
generate_grub_config tarball/amd64/grub/grub.cfg.in
|
||||||
menuentry "Install Ubuntu Server" {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux linux iso-url=#ISOURL# ip=dhcp ---
|
|
||||||
initrd initrd
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
cat > tarball/amd64/pxelinux.cfg/default.in <<EOF
|
cat > tarball/amd64/pxelinux.cfg/default.in <<EOF
|
||||||
DEFAULT install
|
DEFAULT install
|
||||||
LABEL install
|
LABEL install
|
||||||
@ -72,13 +79,7 @@ EOF
|
|||||||
mv chroot/usr/lib/grub/arm64-efi-signed/grubnetaa64.efi.signed tarball/arm64/grubaa64.efi
|
mv chroot/usr/lib/grub/arm64-efi-signed/grubnetaa64.efi.signed tarball/arm64/grubaa64.efi
|
||||||
|
|
||||||
mkdir tarball/arm64/grub
|
mkdir tarball/arm64/grub
|
||||||
cat > tarball/arm64/grub/grub.cfg.in <<EOF
|
generate_grub_config tarball/arm64/grub/grub.cfg.in
|
||||||
menuentry "Install Ubuntu Server" {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux linux iso-url=#ISOURL# ip=dhcp ---
|
|
||||||
initrd initrd
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
s390x)
|
s390x)
|
||||||
@ -125,6 +126,13 @@ LABEL install
|
|||||||
append=ip=dhcp iso-url=#ISOURL# ---
|
append=ip=dhcp iso-url=#ISOURL# ---
|
||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
riscv64)
|
||||||
|
mv chroot/usr/lib/grub/riscv64-efi/monolithic/grubnetriscv64.efi tarball/riscv64/grubriscv64.efi
|
||||||
|
|
||||||
|
mkdir tarball/riscv64/grub
|
||||||
|
generate_grub_config tarball/riscv64/grub/grub.cfg.in
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
tar -C tarball -czf livecd.${PROJECT}.netboot.tar.gz .
|
tar -C tarball -czf livecd.${PROJECT}.netboot.tar.gz .
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
# AppArmor restrictions of unprivileged user namespaces
|
||||||
|
|
||||||
|
# Allows to restrict the use of unprivileged user namespaces to applications
|
||||||
|
# which have an AppArmor profile loaded which specifies the userns
|
||||||
|
# permission. All other applications (whether confined by AppArmor or not) will
|
||||||
|
# be denied the use of unprivileged user namespaces.
|
||||||
|
#
|
||||||
|
# See
|
||||||
|
# https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_userns_restriction
|
||||||
|
# https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_unconfined
|
||||||
|
#
|
||||||
|
# If it is desired to disable this restriction, it is preferable to create an
|
||||||
|
# additional file named /etc/sysctl.d/20-apparmor.conf which will override this
|
||||||
|
# current file and sets this value to 0 rather than editing this current file
|
||||||
|
kernel.apparmor_restrict_unprivileged_userns = 0
|
||||||
|
kernel.apparmor_restrict_unprivileged_unconfined = 1
|
||||||
@ -15,6 +15,25 @@ cat <<EOF > /etc/initramfs-tools/conf.d/default-layer.conf
|
|||||||
LAYERFS_PATH=${PASS}.squashfs
|
LAYERFS_PATH=${PASS}.squashfs
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > /etc/sysctl.d/20-apparmor.conf
|
||||||
|
# AppArmor restrictions of unprivileged user namespaces
|
||||||
|
|
||||||
|
# Allows to restrict the use of unprivileged user namespaces to applications
|
||||||
|
# which have an AppArmor profile loaded which specifies the userns
|
||||||
|
# permission. All other applications (whether confined by AppArmor or not) will
|
||||||
|
# be denied the use of unprivileged user namespaces.
|
||||||
|
#
|
||||||
|
# See
|
||||||
|
# https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_userns_restriction
|
||||||
|
# https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_unconfined
|
||||||
|
#
|
||||||
|
# If it is desired to disable this restriction, it is preferable to create an
|
||||||
|
# additional file named /etc/sysctl.d/20-apparmor.conf which will override this
|
||||||
|
# current file and sets this value to 0 rather than editing this current file
|
||||||
|
kernel.apparmor_restrict_unprivileged_userns = 0
|
||||||
|
kernel.apparmor_restrict_unprivileged_unconfined = 1
|
||||||
|
EOF
|
||||||
|
|
||||||
if which glib-compile-schemas >/dev/null 2>&1; then
|
if which glib-compile-schemas >/dev/null 2>&1; then
|
||||||
glib-compile-schemas /usr/share/glib-2.0/schemas/
|
glib-compile-schemas /usr/share/glib-2.0/schemas/
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -12,14 +12,103 @@ case ${PASS:-} in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [ -n "${SUBPROJECT:-}" ]; then
|
|
||||||
echo "We don't run Ubuntu Desktop hooks for this project."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
. config/binary
|
. config/binary
|
||||||
. config/functions
|
. config/functions
|
||||||
|
|
||||||
|
|
||||||
|
# Naive conversion from YAML to JSON. This is needed because yq is in universe
|
||||||
|
# (but jq is not).
|
||||||
|
yaml_to_json()
|
||||||
|
{
|
||||||
|
python3 -c '
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
json.dump(yaml.safe_load(sys.stdin), sys.stdout, default=str)
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Use jq to retrieve a list of --snap options from a given *signed* model.
|
||||||
|
get_snaps_args_excluding()
|
||||||
|
{
|
||||||
|
local model=$1
|
||||||
|
local jq_filter='
|
||||||
|
# Find all snaps that are not filtered out.
|
||||||
|
# The filtered out snaps are passed as positional arguments so they end up in
|
||||||
|
# the $ARGS.positional array.
|
||||||
|
.snaps[] | select(.name | IN($ARGS.positional[]) | not)
|
||||||
|
# Then forge the --snap option.
|
||||||
|
| "--snap=" + .name + "=" + .["default-channel"]'
|
||||||
|
|
||||||
|
shift
|
||||||
|
|
||||||
|
# The model is signed and is not valid YAML unless we get rid of the
|
||||||
|
# signature. Here we assume the only blank line is before the signature.
|
||||||
|
sed '/^$/,$d' -- "$model" \
|
||||||
|
| yaml_to_json \
|
||||||
|
| jq --raw-output "$jq_filter" --args "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use jq to retrieve a list of --snap options from a given *signed* model.
|
||||||
|
get_snaps_args()
|
||||||
|
{
|
||||||
|
local model=$1
|
||||||
|
get_snaps_args_excluding "$model"
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_components_filtered()
|
||||||
|
{
|
||||||
|
local excluded=$1
|
||||||
|
local model=$2
|
||||||
|
local jq_filter='
|
||||||
|
# Find all snaps that are either filtered in or filtered out
|
||||||
|
# The filtered in (or out) snaps are passed as positional arguments so they end up in
|
||||||
|
# the $ARGS.positional array. The excluded variable is passed separately and
|
||||||
|
# tells if we want to filter in (i.e., excluded=false) or filter out (i.e.,
|
||||||
|
# excluded=true).
|
||||||
|
.snaps[] | select(.name | IN($ARGS.positional[]) | if $excluded then not else . end)
|
||||||
|
# and have components
|
||||||
|
| select(.components)
|
||||||
|
# Then save the name of each snap in a variable
|
||||||
|
| .name as $snap
|
||||||
|
# Then for each entry that has "optional"
|
||||||
|
| .components | to_entries | map(select(.value.presence == "optional"))
|
||||||
|
# Output its name with the snap name prepended
|
||||||
|
| "\($snap)" + "+" + .[].key'
|
||||||
|
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
sed '/^$/,$d' -- "$model" \
|
||||||
|
| yaml_to_json \
|
||||||
|
| jq --raw-output "$jq_filter" --argjson excluded "$excluded" --args "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of all components for all snaps
|
||||||
|
get_all_components()
|
||||||
|
{
|
||||||
|
local model=$1
|
||||||
|
# Provide an exclusion list but empty
|
||||||
|
_get_components_filtered true "$model"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of all components for all snaps except the ones specified.
|
||||||
|
get_components_excluding()
|
||||||
|
{
|
||||||
|
local model=$1
|
||||||
|
shift
|
||||||
|
_get_components_filtered true "$model" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of all components for the snaps specified.
|
||||||
|
get_components()
|
||||||
|
{
|
||||||
|
local model=$1
|
||||||
|
shift
|
||||||
|
_get_components_filtered false "$model" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
# Generation of the model:
|
# Generation of the model:
|
||||||
# * At https://github.com/canonical/models one can find a repo of raw,
|
# * At https://github.com/canonical/models one can find a repo of raw,
|
||||||
# unsigned, input .json files, and their signed .model equivalents.
|
# unsigned, input .json files, and their signed .model equivalents.
|
||||||
@ -36,13 +125,61 @@ fi
|
|||||||
# live-build/${PROJECT}/ubuntu-classic-amd64.model
|
# live-build/${PROJECT}/ubuntu-classic-amd64.model
|
||||||
|
|
||||||
# env SNAPPY_STORE_NO_CDN=1 snap known --remote model series=16 brand-id=canonical model=ubuntu-classic-2410-amd64 > config/classic-model.model
|
# env SNAPPY_STORE_NO_CDN=1 snap known --remote model series=16 brand-id=canonical model=ubuntu-classic-2410-amd64 > config/classic-model.model
|
||||||
model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64.model
|
#
|
||||||
|
dangerous_model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64-dangerous.model
|
||||||
|
stable_model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64.model
|
||||||
|
|
||||||
|
prepare_args=()
|
||||||
|
|
||||||
|
components=()
|
||||||
|
|
||||||
# for the dangerous subproject, we need the dangerous model!
|
# for the dangerous subproject, we need the dangerous model!
|
||||||
if [ $SUBPROJECT = "dangerous" ]; then
|
if [ "$SUBPROJECT" = "dangerous" ]; then
|
||||||
model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64-dangerous.model
|
# As with the "classically" seeded snaps, snaps from the edge channel may
|
||||||
|
# require different content snaps to be installed, so they must be
|
||||||
|
# included in the system as well. We just use the same list as was
|
||||||
|
# computed in snap_validate_seed.
|
||||||
|
model="${dangerous_model}"
|
||||||
|
while read snap; do
|
||||||
|
prepare_args+=("--snap=${snap}=edge")
|
||||||
|
done < config/missing-providers
|
||||||
|
|
||||||
|
for comp in $(get_all_components "$model"); do
|
||||||
|
components+=("$comp")
|
||||||
|
done
|
||||||
|
else
|
||||||
|
model="${stable_model}"
|
||||||
|
# If we need to override anything from the model, we need grade: dangerous.
|
||||||
|
# And if so, uncomment the below to use the dangerous model and set the
|
||||||
|
# snaps_from_dangerous and snaps_from_beta variables to still use snaps
|
||||||
|
# from the stable model.
|
||||||
|
#model="${dangerous_model}"
|
||||||
|
snaps_from_dangerous=()
|
||||||
|
# For these snaps, we ignore the model entirely.
|
||||||
|
snaps_from_beta=()
|
||||||
|
for snap in "${snaps_from_beta[@]}"; do
|
||||||
|
prepare_args+=("--snap=$snap=beta")
|
||||||
|
done
|
||||||
|
# snaps that we are special casing.
|
||||||
|
_exclude=("${snaps_from_dangerous[@]}" "${snaps_from_beta[@]}")
|
||||||
|
|
||||||
|
if [ "$model" = "$dangerous_model" ]; then
|
||||||
|
for snap_arg in $(get_snaps_args_excluding "$stable_model" "${_exclude[@]}"); do
|
||||||
|
prepare_args+=("$snap_arg")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
for comp in $(get_components_excluding "$stable_model" "${_exclude[@]}"); do
|
||||||
|
components+=("$comp")
|
||||||
|
done
|
||||||
|
for comp in $(get_components "$dangerous_model" "${snaps_from_dangerous[@]}"); do
|
||||||
|
components+=("$comp")
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
for comp in "${components[@]}"; do
|
||||||
|
prepare_args+=(--comp "$comp")
|
||||||
|
done
|
||||||
|
|
||||||
channel=""
|
channel=""
|
||||||
if [ -n "${CHANNEL:-}" ]; then
|
if [ -n "${CHANNEL:-}" ]; then
|
||||||
channel="--channel $CHANNEL"
|
channel="--channel $CHANNEL"
|
||||||
@ -52,7 +189,7 @@ fi
|
|||||||
# snap versions regardless of phasing status
|
# snap versions regardless of phasing status
|
||||||
|
|
||||||
env SNAPPY_STORE_NO_CDN=1 UBUNTU_STORE_COHORT_KEY="+" snap prepare-image \
|
env SNAPPY_STORE_NO_CDN=1 UBUNTU_STORE_COHORT_KEY="+" snap prepare-image \
|
||||||
--classic $model $channel chroot
|
--classic $model $channel "${prepare_args[@]}" chroot
|
||||||
|
|
||||||
mv chroot/system-seed/systems/* chroot/system-seed/systems/enhanced-secureboot-desktop
|
mv chroot/system-seed/systems/* chroot/system-seed/systems/enhanced-secureboot-desktop
|
||||||
rsync -av chroot/system-seed/{systems,snaps} chroot/var/lib/snapd/seed
|
rsync -av chroot/system-seed/{systems,snaps} chroot/var/lib/snapd/seed
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
echo "Creating Hyper-V image with Desktop..."
|
echo "Creating Hyper-V image with Desktop..."
|
||||||
|
|
||||||
case ${SUBPROJECT:-} in
|
case ${SUBPROJECT:-} in
|
||||||
minimized|"")
|
minimized|dangerous|"")
|
||||||
echo "We don't create minimized images for $0."
|
echo "We don't create minimized images for $0."
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
# When booting the live ISO, snapd seeding takes a while to complete, which
|
||||||
|
# can cause GDM to start before the Ubuntu installer is seeded and ready to be
|
||||||
|
# launched. This leads to a confusing delay between the user leaving Plymouth
|
||||||
|
# and seeing the desktop wallpaper and the installer launching.
|
||||||
|
# This drop-in delays display-manager.service until snapd seeding completes, so
|
||||||
|
# the installer launches within seconds of Plymouth disappearing.
|
||||||
|
[Unit]
|
||||||
|
After=snapd.seeded.service
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
# force reexecuting the snapd snap version on the live system
|
||||||
|
# while developping features that only lands on edge, even if the
|
||||||
|
# deb version is higher.
|
||||||
|
# This allows automated tests to always run what’s next.
|
||||||
|
#[Service]
|
||||||
|
#Environment="SNAP_REEXEC=force"
|
||||||
@ -2,7 +2,7 @@ type: model
|
|||||||
authority-id: canonical
|
authority-id: canonical
|
||||||
series: 16
|
series: 16
|
||||||
brand-id: canonical
|
brand-id: canonical
|
||||||
model: ubuntu-classic-2510-amd64-dangerous
|
model: ubuntu-classic-2604-amd64-dangerous
|
||||||
architecture: amd64
|
architecture: amd64
|
||||||
base: core24
|
base: core24
|
||||||
classic: true
|
classic: true
|
||||||
@ -10,12 +10,17 @@ distribution: ubuntu
|
|||||||
grade: dangerous
|
grade: dangerous
|
||||||
snaps:
|
snaps:
|
||||||
-
|
-
|
||||||
default-channel: classic-25.10/edge
|
default-channel: classic-26.04/edge
|
||||||
id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
|
id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
|
||||||
name: pc
|
name: pc
|
||||||
type: gadget
|
type: gadget
|
||||||
-
|
-
|
||||||
default-channel: 25.10/beta
|
components:
|
||||||
|
nvidia-580-uda-ko:
|
||||||
|
presence: optional
|
||||||
|
nvidia-580-uda-user:
|
||||||
|
presence: optional
|
||||||
|
default-channel: 26.04/beta
|
||||||
id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
|
id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
|
||||||
name: pc-kernel
|
name: pc-kernel
|
||||||
type: kernel
|
type: kernel
|
||||||
@ -29,6 +34,11 @@ snaps:
|
|||||||
id: dwTAh7MZZ01zyriOZErqd1JynQLiOGvM
|
id: dwTAh7MZZ01zyriOZErqd1JynQLiOGvM
|
||||||
name: core24
|
name: core24
|
||||||
type: base
|
type: base
|
||||||
|
-
|
||||||
|
default-channel: latest/edge
|
||||||
|
id: cUqM61hRuZAJYmIS898Ux66VY61gBbZf
|
||||||
|
name: core26
|
||||||
|
type: base
|
||||||
-
|
-
|
||||||
default-channel: latest/edge
|
default-channel: latest/edge
|
||||||
id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
|
id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
|
||||||
@ -40,55 +50,60 @@ snaps:
|
|||||||
name: bare
|
name: bare
|
||||||
type: base
|
type: base
|
||||||
-
|
-
|
||||||
default-channel: 1/edge/ubuntu-25.10
|
default-channel: latest/edge
|
||||||
|
id: HyhSEBPv3vHsW6uOHkQR384NgI7S6zpj
|
||||||
|
name: mesa-2404
|
||||||
|
type: app
|
||||||
|
-
|
||||||
|
default-channel: 1/edge
|
||||||
id: EI0D1KHjP8XiwMZKqSjuh6W8zvcowUVP
|
id: EI0D1KHjP8XiwMZKqSjuh6W8zvcowUVP
|
||||||
name: firmware-updater
|
name: firmware-updater
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 1/edge/ubuntu-25.10
|
default-channel: 1/edge
|
||||||
id: FppXWunWzuRT2NUT9CwoBPNJNZBYOCk0
|
id: FppXWunWzuRT2NUT9CwoBPNJNZBYOCk0
|
||||||
name: desktop-security-center
|
name: desktop-security-center
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 1/edge/ubuntu-25.10
|
default-channel: 1/edge
|
||||||
id: aoc5lfC8aUd2VL8VpvynUJJhGXp5K6Dj
|
id: aoc5lfC8aUd2VL8VpvynUJJhGXp5K6Dj
|
||||||
name: prompting-client
|
name: prompting-client
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 2/edge/ubuntu-25.10
|
default-channel: 2/edge
|
||||||
id: gjf3IPXoRiipCu9K0kVu52f0H56fIksg
|
id: gjf3IPXoRiipCu9K0kVu52f0H56fIksg
|
||||||
name: snap-store
|
name: snap-store
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/edge/ubuntu-25.10
|
default-channel: latest/edge
|
||||||
id: jZLfBRzf1cYlYysIjD2bwSzNtngY0qit
|
id: jZLfBRzf1cYlYysIjD2bwSzNtngY0qit
|
||||||
name: gtk-common-themes
|
name: gtk-common-themes
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/edge/ubuntu-25.10
|
default-channel: latest/edge
|
||||||
id: 3wdHCAVyZEmYsCMFDE9qt92UV8rC8Wdk
|
id: 3wdHCAVyZEmYsCMFDE9qt92UV8rC8Wdk
|
||||||
name: firefox
|
name: firefox
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/edge/ubuntu-25.10
|
default-channel: latest/edge
|
||||||
id: lATO8HzwVvrAPrlZRAWpfyrJKlAJrZS3
|
id: ew7OxpbRTxfK7ImpIygRR85lkxvU7Pzt
|
||||||
name: gnome-42-2204
|
name: gnome-46-2404
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/edge/ubuntu-25.10
|
default-channel: latest/edge
|
||||||
id: IrwRHakqtzhFRHJOOPxKVPU0Kk7Erhcu
|
id: IrwRHakqtzhFRHJOOPxKVPU0Kk7Erhcu
|
||||||
name: snapd-desktop-integration
|
name: snapd-desktop-integration
|
||||||
type: app
|
type: app
|
||||||
timestamp: 2025-05-01T12:00:00.0Z
|
timestamp: 2025-12-09T12:00:00.0Z
|
||||||
sign-key-sha3-384: 9tydnLa6MTJ-jaQTFUXEwHl1yRx7ZS4K5cyFDhYDcPzhS7uyEkDxdUjg9g08BtNn
|
sign-key-sha3-384: 9tydnLa6MTJ-jaQTFUXEwHl1yRx7ZS4K5cyFDhYDcPzhS7uyEkDxdUjg9g08BtNn
|
||||||
|
|
||||||
AcLBXAQAAQoABgUCaKKyiQAKCRDgT5vottzAEt+LD/9GTgoaYQg0qYohdnYYQkiWJbtNcZgLRpUf
|
AcLBXAQAAQoABgUCaUFt7QAKCRDgT5vottzAEhdnD/92LBcQm3iw/kPao4KqGE0OhfXDFd7Z6+Qv
|
||||||
gPswZsBzmDLbH0XyLWB9h32gDyZ6Gdt3c9uK2hqCaNTaLJyf5eGs/7zjs9lVmEf3MNSr9FGx6Vek
|
A1Dlzz6Cw0tuj0r5aZH7vJQCx4kC1Eaoi8apg3XhqAyhr74/MsIwMhPPL8qcSNv8ZWruoGwFp/rx
|
||||||
i+NPeOEjrmwjMXzi9FnTDTJzW0cMzCEzYiTHWRF8K5WCVumuzMMUCihZbuHvZCjXoIJ+RrSBMvbE
|
M6NSBKc6hrYqACYfEkBwfq9SgmIDQKFeBVudwswLK2SN58wrDNJjuWz/eJ5hUIIe3ga5ScfzO4Jr
|
||||||
udzmJ0NIEgCGA+r32kEkcauNTrMwXdosdTYhrKy/dcy6SrACaZzvQAwPYXy3UJEDmz6qQQ/94G9s
|
jTWS4kh5lpttCPFX8ouLkMgLUxijQpxFbHoF1trXJndFvavStT0yuC0y5TXzb3wJbbiF/MXZWyjV
|
||||||
5P/bqrOZVoL22H9a/6WnEulHyCNAVlotpbY3Dij1yHp5KEiFCuREP/MauLdONCY+snimwxpItm7L
|
/4U+oQLodO77MhaD01kk2y5bZ62YuQ3MPL0fQGypon12GPHeNNcEcYWRZlFv+JkWAduWlnuefj1D
|
||||||
B4D92BTkhe9noiyRbSU3fLrMJGY//PKCxkQMVyvkx/aUnyqeySRCP6U7U6t09P2/WzmJULT+a+9N
|
dVWV8dQQmSZGZNiGTsIJxkY9+4B+t/OhosGDc6jEmEZcKNVi9fnl0+awkzK6scNNmupZ8NwJl8ZR
|
||||||
pgXrXt3k69zqI0U74R3+JwiU/VrZq6cssdwx8unKYtoOT5O3G0b7q50Lv2RyNHQSjrlSU397HKkQ
|
mJSsfaBcH4paYV1x31y4uTELv+OuDWAJ3D0RoCR8H0djTBxRhsF2/JpSJasxVmSbzWHPSeM3f1aO
|
||||||
Gnnb/w4caV+O/cKlDlXNXijZI/kxfP+tKqRHMSRLmV3I8W+/nh6YE4/NMiXVicej3tTaPtWlBI+9
|
ChZGwbD6J2SpzsrdogUP/9z6o8YuVnJkOxoBYuXhT1pEYTd93/hE++j3MpOqey/xw8UDbYmq5oJf
|
||||||
hj0Chtlk+wWD+9MjC1suJh4XLHAXbYJczSFdb3qcQq7f64v70sNoLzW4ekGUlSpbazNaX7P+Liov
|
uKaYLOMphqDm5hUCZmxQp8gTzDleZGjxYS2fOS4qFUJlvyVwsSoJMXU+6YfA6tgEQ4Dbh6zp6r78
|
||||||
VBGXSpziQJuF4y/BSU8tCweJExhkibFEBar5SCKbvw==
|
MjEqfWn4lL16xW2Zzr6e8xWwUrM7T3Gp4WTA7/xOeA==
|
||||||
|
|||||||
@ -2,7 +2,7 @@ type: model
|
|||||||
authority-id: canonical
|
authority-id: canonical
|
||||||
series: 16
|
series: 16
|
||||||
brand-id: canonical
|
brand-id: canonical
|
||||||
model: ubuntu-classic-2510-amd64
|
model: ubuntu-classic-2604-amd64
|
||||||
architecture: amd64
|
architecture: amd64
|
||||||
base: core24
|
base: core24
|
||||||
classic: true
|
classic: true
|
||||||
@ -10,12 +10,17 @@ distribution: ubuntu
|
|||||||
grade: signed
|
grade: signed
|
||||||
snaps:
|
snaps:
|
||||||
-
|
-
|
||||||
default-channel: classic-25.10/stable
|
default-channel: classic-26.04/stable
|
||||||
id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
|
id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
|
||||||
name: pc
|
name: pc
|
||||||
type: gadget
|
type: gadget
|
||||||
-
|
-
|
||||||
default-channel: 25.10/stable
|
components:
|
||||||
|
nvidia-580-uda-ko:
|
||||||
|
presence: optional
|
||||||
|
nvidia-580-uda-user:
|
||||||
|
presence: optional
|
||||||
|
default-channel: 26.04/stable
|
||||||
id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
|
id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
|
||||||
name: pc-kernel
|
name: pc-kernel
|
||||||
type: kernel
|
type: kernel
|
||||||
@ -40,55 +45,60 @@ snaps:
|
|||||||
name: bare
|
name: bare
|
||||||
type: base
|
type: base
|
||||||
-
|
-
|
||||||
default-channel: 1/stable/ubuntu-25.10
|
default-channel: latest/stable/ubuntu-26.04
|
||||||
|
id: HyhSEBPv3vHsW6uOHkQR384NgI7S6zpj
|
||||||
|
name: mesa-2404
|
||||||
|
type: app
|
||||||
|
-
|
||||||
|
default-channel: 1/stable/ubuntu-26.04
|
||||||
id: EI0D1KHjP8XiwMZKqSjuh6W8zvcowUVP
|
id: EI0D1KHjP8XiwMZKqSjuh6W8zvcowUVP
|
||||||
name: firmware-updater
|
name: firmware-updater
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 1/stable/ubuntu-25.10
|
default-channel: 1/stable/ubuntu-26.04
|
||||||
id: FppXWunWzuRT2NUT9CwoBPNJNZBYOCk0
|
id: FppXWunWzuRT2NUT9CwoBPNJNZBYOCk0
|
||||||
name: desktop-security-center
|
name: desktop-security-center
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 1/stable/ubuntu-25.10
|
default-channel: 1/stable/ubuntu-26.04
|
||||||
id: aoc5lfC8aUd2VL8VpvynUJJhGXp5K6Dj
|
id: aoc5lfC8aUd2VL8VpvynUJJhGXp5K6Dj
|
||||||
name: prompting-client
|
name: prompting-client
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: 2/stable/ubuntu-25.10
|
default-channel: 2/stable/ubuntu-26.04
|
||||||
id: gjf3IPXoRiipCu9K0kVu52f0H56fIksg
|
id: gjf3IPXoRiipCu9K0kVu52f0H56fIksg
|
||||||
name: snap-store
|
name: snap-store
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/stable/ubuntu-25.10
|
default-channel: latest/stable/ubuntu-26.04
|
||||||
id: jZLfBRzf1cYlYysIjD2bwSzNtngY0qit
|
id: jZLfBRzf1cYlYysIjD2bwSzNtngY0qit
|
||||||
name: gtk-common-themes
|
name: gtk-common-themes
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/stable/ubuntu-25.10
|
default-channel: latest/stable/ubuntu-26.04
|
||||||
id: 3wdHCAVyZEmYsCMFDE9qt92UV8rC8Wdk
|
id: 3wdHCAVyZEmYsCMFDE9qt92UV8rC8Wdk
|
||||||
name: firefox
|
name: firefox
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/stable/ubuntu-25.10
|
default-channel: latest/stable/ubuntu-26.04
|
||||||
id: lATO8HzwVvrAPrlZRAWpfyrJKlAJrZS3
|
id: ew7OxpbRTxfK7ImpIygRR85lkxvU7Pzt
|
||||||
name: gnome-42-2204
|
name: gnome-46-2404
|
||||||
type: app
|
type: app
|
||||||
-
|
-
|
||||||
default-channel: latest/stable/ubuntu-25.10
|
default-channel: latest/stable/ubuntu-26.04
|
||||||
id: IrwRHakqtzhFRHJOOPxKVPU0Kk7Erhcu
|
id: IrwRHakqtzhFRHJOOPxKVPU0Kk7Erhcu
|
||||||
name: snapd-desktop-integration
|
name: snapd-desktop-integration
|
||||||
type: app
|
type: app
|
||||||
timestamp: 2025-08-06T12:00:00.0Z
|
timestamp: 2025-12-09T12:00:00.0Z
|
||||||
sign-key-sha3-384: 9tydnLa6MTJ-jaQTFUXEwHl1yRx7ZS4K5cyFDhYDcPzhS7uyEkDxdUjg9g08BtNn
|
sign-key-sha3-384: 9tydnLa6MTJ-jaQTFUXEwHl1yRx7ZS4K5cyFDhYDcPzhS7uyEkDxdUjg9g08BtNn
|
||||||
|
|
||||||
AcLBXAQAAQoABgUCaJuDnwAKCRDgT5vottzAEqjkD/4+SAjC0APhGmSh73ewaUe57Nbs4qDfrUJZ
|
AcLBXAQAAQoABgUCaYzP9QAKCRDgT5vottzAEus2D/4jJVutpoPmDrLjNQLn2KNf/f1L2zU8ESSe
|
||||||
P8aaq9UO3siLmp1og/3OwbuMhddmqxvurYqe0jby6Qbv7NbCcI7YZvn4wDxnessWeYJnOd1Le8UC
|
VpFjy+9Ff7AxXckALM4eEy/J5mc+UNhHQ/7Thp4XYy2NiH14n9Lv5kVqZCz8udiEfcfLy5gGveio
|
||||||
cOmhckh7WZzQreR2SjIRq9ElGGI4RIft4Ex6H2EYG3A9EpTdwfZ1jmnwUlw6qqPEjXJv/PKtcpm0
|
oXyGX7J5x9sq3YXV1IHS84aqJS0si80TTLCRQXUN8oUZIVRkgFOGIVVneQkn1ppNs87kNgvBT1ow
|
||||||
qrWetewWMyZF5JmoZUyCWd4xP0y3VpbrTCHvBl5C/Q63xy/KELSGYAesgBfcO1bIP/NbVWsHbvTd
|
nwr9fVvZnt5bTprCxs4R5cEUlWTJMN4l96Eh530Q+wqCjFxbTs6FADUYielsFnBDl/Q1M0fozg4F
|
||||||
qCew5lTQx7tUIu4mnBuvt1bZ6U3jbXUu15g1EgJwsft2ker5bX8GbsJEBnz7TBgdYmrRb3DCthZ7
|
Ct4gBbvFGWZhp8LXiCbJvTd3PAAV1HYAgtKDKZT0NQp8qaU5DpgTDiUzIjaAJP7feSU5AYDLuVSH
|
||||||
tiZT2XWmOSiwMK++5BNnJkActkkCXFRVzL/f2ofUOJ96bLxk3qlPxh6bkpqUkkSkygC46123MzOZ
|
V3zD8sosg1nmPvVtuSi2q5Z+/zd6gmG+vLn5d16whNqELDnX0O9Hxarc/3DD3ANZrrbXlq/PEJNB
|
||||||
dRehpGuqsZ3VrLZx8CrrwIb0nZZFeR0ZH8y1gL2uXcRPQDSOPFgwRrTLo+NzVLfdUyraRZaU50Y+
|
Lor5osHLN4utW7CUC5MIEQ5/Z/6cSuav6rQ+bBiAOzQSHRCbhfyCGSMMINX2CE3ePw3moi9gwXeh
|
||||||
xRzh0nT8GSFDY9QeZZwq83UDsUNILZxRBmnD7fidYDFHRNzqLPH+xYAuAakX6zGlA2foOpC1CbIs
|
vKw1iItEOxywEKbeBNEvddnGsvmzoqf9Jg53/X0yrQQVZTHYFsQlTRk9ggajdZnPjJMTqlAqjXnP
|
||||||
LUAhSzFdKJKBGucG3EumAggOCWqggc6RxY3c4eXDbrESE1RG1ndKINScNf+l/cYAwZSa6q9HKx3R
|
QCsgnprvln0akW4IfEzc+IgoF5eiShJd4IidkBbbdNXRRYlHfmOG7ZvR9upJwe1M73Zfu1nQFEvT
|
||||||
w7sGOLj+FzSGWqBaljjQeBd1jk3udS34yaDEtLlcxQ==
|
fly59e2Vw8O50ljOVW3jT5fW36z8h1+ttxkKwVsQJg==
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user