Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e57143c4bc
|
||
|
|
3275ec9845
|
||
|
|
b68623a782
|
||
|
|
88e73bbc22
|
||
|
|
025082a92f
|
||
|
|
699ed6a935
|
||
|
|
e63f9cb14f
|
||
|
|
c5c7ab9b8b
|
||
|
|
03f0ca05e0
|
||
|
|
5299b90bbc
|
||
|
|
55b66a1373
|
||
|
|
504a568570
|
||
|
|
0ae2f036b7
|
||
|
|
0e57df4360
|
||
|
|
da26e88147
|
||
|
|
f06bfd1512
|
||
|
|
a940d91794
|
||
|
|
9af4bc6c27
|
||
|
|
3c540f8bed
|
||
|
|
3f3bba05f3
|
||
|
|
01913f7682
|
||
|
|
f9a2978517
|
||
|
|
1e8a1e3282
|
||
|
|
db83549996
|
||
|
|
59d82f06f5
|
||
|
|
1bb6f8dddd
|
||
|
|
0fbec83573
|
||
|
|
a6c99b72d0
|
||
|
|
c64c64dc97
|
||
|
|
319333e400
|
||
|
|
bb6f64e977
|
||
|
|
3b3764573e
|
||
|
|
8501a60165
|
||
|
|
7c601ca53d
|
||
|
|
f3bd10cf0f
|
||
|
|
83ed87f6a9
|
||
|
|
5ffd5d0d82
|
||
|
|
8484bddcf2
|
||
|
|
0755091721
|
||
|
|
c71168371d
|
||
|
|
117b5e97aa
|
||
|
|
8a88921913
|
||
|
|
9614192416
|
||
|
|
2f1ac8d052
|
||
|
|
fa7eff8517
|
||
|
|
24f3f0f399
|
||
|
|
ac59de33c8
|
||
|
|
da0855d341
|
||
|
|
50d49bbe38 | ||
|
|
1090e3c75b
|
||
|
|
48b760d54e
|
+486
-154
@@ -22,18 +22,174 @@ jobs:
|
||||
- name: Setup local AUR repo
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
mkdir -p /tmp/localrepo
|
||||
cp localrepo/*.pkg.tar.zst localrepo/local-aur.db* localrepo/local-aur.files* /tmp/localrepo/
|
||||
sed -i 's|file:///home/alvin/alvinarch/localrepo|file:///tmp/localrepo|' pacman.conf
|
||||
sed -i 's|file:///home/alvin/AlvinArch/localrepo|file:///tmp/localrepo|' pacman.conf
|
||||
# The medium needs its own copy: the ISO's /etc/pacman.conf points
|
||||
# [local-aur] at file:///opt/localrepo, which pacstrap reads at
|
||||
# install time to resolve packages that are not in the official repos
|
||||
# (built during the ISO build and harvested there by
|
||||
# airootfs/root/customize_airootfs.sh).
|
||||
mkdir -p airootfs/opt/localrepo
|
||||
shipped=(localrepo/*.pkg.tar.zst localrepo/local-aur.db* localrepo/local-aur.files*)
|
||||
if (( ${#shipped[@]} > 0 )); then
|
||||
cp -L "${shipped[@]}" airootfs/opt/localrepo/
|
||||
fi
|
||||
echo "airootfs/opt/localrepo: $(ls airootfs/opt/localrepo | wc -l) file(s)"
|
||||
|
||||
- name: Install archiso and dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pacman -Syu --noconfirm --needed archiso jq git-lfs zstd
|
||||
# Install with a *private* config instead of /etc/pacman.conf.
|
||||
# The host config is shared mutable state: the tests workflow
|
||||
# rewrites it and jobs overlap on this runner (runs 132 and 133
|
||||
# ran concurrently), so writing it here could corrupt a concurrent
|
||||
# tests run. It also carries [cachyos] and [chaotic-aur] leftovers
|
||||
# from tests.yaml, and syncing those two repos means extra DNS
|
||||
# lookups against a gateway that intermittently drops UDP/53 —
|
||||
# that is what failed run 131 at this very step. The build only
|
||||
# needs archiso/jq from extra; mkarchiso reads the *profile's*
|
||||
# pacman.conf for everything that goes into the ISO.
|
||||
BUILD_CONF=$(mktemp)
|
||||
trap 'rm -f "$BUILD_CONF"' EXIT
|
||||
cat > "$BUILD_CONF" << 'STOCKCONF'
|
||||
[options]
|
||||
DisableSandbox
|
||||
HoldPkg = pacman glibc
|
||||
Architecture = auto
|
||||
CheckSpace
|
||||
ParallelDownloads = 5
|
||||
SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[extra]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
STOCKCONF
|
||||
sed -i 's/^ //' "$BUILD_CONF"
|
||||
|
||||
# Warm the resolver cache for the mirrors we are about to query.
|
||||
# systemd-resolved serves later lookups from cache, so a gateway
|
||||
# UDP/53 burst cannot hit them again. Failures here are not fatal —
|
||||
# the retry loop below is the real safety net.
|
||||
for host in mirror.aarnet.edu.au gsl-syd.mm.fcix.net au.arch.niranjan.co; do
|
||||
for try in 1 2 3 4 5; do
|
||||
if getent hosts "$host" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
echo " ⏳ DNS for $host failed (try $try/5), retrying in $(( try * 3 ))s..."
|
||||
sleep $(( try * 3 ))
|
||||
done
|
||||
done
|
||||
|
||||
# The gateway drops DNS queries in bursts; one burst must not fail
|
||||
# the whole job, so retry with backoff before giving up.
|
||||
synced=0
|
||||
for try in 1 2 3 4 5; do
|
||||
if pacman --config "$BUILD_CONF" -Syu --noconfirm --needed archiso jq; then
|
||||
synced=1
|
||||
break
|
||||
fi
|
||||
echo " ⚠️ pacman -Syu failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
|
||||
sleep $(( try * 15 ))
|
||||
done
|
||||
if [ "$synced" -ne 1 ]; then
|
||||
echo "❌ pacman -Syu failed after 5 attempts — giving up" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Trust cachyos and chaotic-aur signing keys
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The profile's pacman.conf verifies [cachyos] and [chaotic-aur]
|
||||
# signatures (SigLevel inherited from [options]); mkarchiso's
|
||||
# pacstrap checks them against the host keyring.
|
||||
# F3B607488DB35A47 CachyOS <admin@cachyos.org>
|
||||
# 3056513887B78AEB chaotic-aur (Pedro Henrique Lara Campos)
|
||||
for key in F3B607488DB35A47 3056513887B78AEB; do
|
||||
if pacman-key --list-keys "$key" >/dev/null 2>&1; then
|
||||
echo " ✅ $key already in the keyring"
|
||||
else
|
||||
got=0
|
||||
# Retry: gateway drops UDP/53 in bursts (see run 131)
|
||||
for try in 1 2 3 4 5; do
|
||||
if pacman-key --recv-keys "$key" --keyserver keyserver.ubuntu.com; then
|
||||
got=1
|
||||
break
|
||||
fi
|
||||
echo " ⚠️ recv-keys $key failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
|
||||
sleep $(( try * 15 ))
|
||||
done
|
||||
if [ "$got" -ne 1 ]; then
|
||||
echo "❌ Could not fetch signing key $key" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
pacman-key --lsign-key "$key"
|
||||
done
|
||||
|
||||
- name: Ensure /dev/fd symlinks exist for mkinitcpio and procsub
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# mkinitcpio's preset step pipes through /dev/stdin and bash process
|
||||
# substitution (< <(...)) uses /dev/fd; both break if these symlinks
|
||||
# are missing (they are normally created once at boot by mkinitcpio's
|
||||
# init). Two places need them, so cover both:
|
||||
# 1. the executor's own /dev: mkarchiso reads /dev/stdin on the host
|
||||
# (machine-id, version, pkglist) and the workflow's own scripts use
|
||||
# process substitution here. A no-op if the links already exist.
|
||||
ln -sfn /proc/self/fd /dev/fd
|
||||
ln -sfn /proc/self/fd/0 /dev/stdin
|
||||
ln -sfn /proc/self/fd/1 /dev/stdout
|
||||
ln -sfn /proc/self/fd/2 /dev/stderr
|
||||
# 2. the chroot's /dev: devtmpfs is single-instance, so mounting it
|
||||
# here yields the same tree that pacstrap/arch-chroot expose as
|
||||
# <chroot>/dev. When the executor's /dev is itself devtmpfs (1)
|
||||
# already fixed that tree; this pass is what makes it work when
|
||||
# the executor's /dev is a plain tmpfs or directory instead.
|
||||
d="$(mktemp -d)"
|
||||
trap 'umount "$d" 2>/dev/null || true; rmdir "$d" 2>/dev/null || true' EXIT
|
||||
mount -t devtmpfs devtmpfs "$d"
|
||||
ln -sfn /proc/self/fd "$d/fd"
|
||||
ln -sfn /proc/self/fd/0 "$d/stdin"
|
||||
ln -sfn /proc/self/fd/1 "$d/stdout"
|
||||
ln -sfn /proc/self/fd/2 "$d/stderr"
|
||||
ls -l "$d/fd" "$d/stdin" "$d/stdout" "$d/stderr"
|
||||
umount "$d"
|
||||
rmdir "$d"
|
||||
trap - EXIT
|
||||
|
||||
- name: Require CAP_SETFCAP for file capabilities
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Without CAP_SETFCAP, pacstrap cannot restore security.capability
|
||||
# xattrs ("unable to set CAP_SETFCAP effective capability"), so the
|
||||
# live ISO silently ships newuidmap/newgidmap, snap-confine, dumpcap,
|
||||
# kwin_wayland, ... without their capabilities. Fix the runner
|
||||
# (e.g. allow CAP_SETFCAP in the gitea-runner service) rather than
|
||||
# building a broken image.
|
||||
eff="$(awk '/^CapEff:/{print $2}' /proc/self/status)"
|
||||
if (( (16#$eff >> 31) & 1 )); then
|
||||
echo "✅ CAP_SETFCAP is available (CapEff=$eff)"
|
||||
else
|
||||
echo "❌ CAP_SETFCAP missing (CapEff=$eff) - file capabilities would be dropped from the ISO" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build Arch ISO
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Pin one timestamp for the whole job: mkarchiso derives the ISO label,
|
||||
# volume UUID, file mtimes, and ext4 hash seed from SOURCE_DATE_EPOCH.
|
||||
# Exporting it explicitly makes every timestamp-derived value traceable
|
||||
# (and lets later steps log the same value via $GITHUB_ENV).
|
||||
SOURCE_DATE_EPOCH="$(date -u +%s)"
|
||||
export SOURCE_DATE_EPOCH
|
||||
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" >> "$GITHUB_ENV"
|
||||
echo "Building with SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
|
||||
# Clean up immediately even if this step is interrupted (cancel/exit):
|
||||
# unmount leftovers first, then remove the multi-GB work dir.
|
||||
trap 'umount -R "$PWD/work" 2>/dev/null || true; rm -rf "$PWD/work"' EXIT HUP INT TERM
|
||||
@@ -43,6 +199,80 @@ jobs:
|
||||
mkdir -p "$PWD/out"
|
||||
mkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD"
|
||||
|
||||
- name: Verify ISO search metadata
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
isos=(out/*.iso)
|
||||
if (( ${#isos[@]} != 1 )); then
|
||||
echo "❌ Expected exactly 1 ISO in out/, found ${#isos[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
iso=${isos[0]}
|
||||
|
||||
# blkid exits non-zero when a field is missing; keep going so the
|
||||
# explicit guard below can report a useful error.
|
||||
vol_uuid=$(blkid -o value -s UUID "$iso" || true)
|
||||
vol_label=$(blkid -o value -s LABEL "$iso" || true)
|
||||
if [ -z "$vol_uuid" ]; then
|
||||
echo "❌ blkid found no UUID for $iso — volume date chain broken?" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Volume UUID: $vol_uuid"
|
||||
echo "Volume label: ${vol_label:-<none>}"
|
||||
echo "SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:-<unset>}"
|
||||
|
||||
fail() {
|
||||
echo "❌ $1" >&2
|
||||
echo " If you changed boot configs (efiboot/, syslinux/, grub/) on purpose," >&2
|
||||
echo " update this 'Verify ISO search metadata' step to match." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 1) /boot/<uuid>.uuid (the archisosearchfilename fallback key) must
|
||||
# exist inside the ISO and its stem must equal the volume UUID.
|
||||
uuid_refs=0
|
||||
while IFS= read -r member; do
|
||||
[ -z "$member" ] && continue
|
||||
member=${member#/}
|
||||
stem=$(basename "$member" .uuid)
|
||||
if [ "$stem" != "$vol_uuid" ]; then
|
||||
fail "$member: stem '$stem' != volume UUID '$vol_uuid'"
|
||||
fi
|
||||
echo " ✅ $member matches volume UUID"
|
||||
uuid_refs=$((uuid_refs + 1))
|
||||
done < <(bsdtar -tf "$iso" | grep -E '(^|/)boot/[0-9A-Za-z-]+\.uuid$' || true)
|
||||
if (( uuid_refs == 0 )); then
|
||||
fail "no /boot/<uuid>.uuid file found inside the ISO"
|
||||
fi
|
||||
|
||||
# 2) Every archisosearchuuid= in shipped boot configs must equal the
|
||||
# volume UUID; every %ARCHISO% placeholder must have been substituted.
|
||||
search_refs=0
|
||||
while IFS= read -r member; do
|
||||
[ -z "$member" ] && continue
|
||||
member=${member#/}
|
||||
if ! content=$(bsdtar -xOf "$iso" "$member" 2>/dev/null); then
|
||||
fail "could not extract $member from the ISO"
|
||||
fi
|
||||
if printf '%s\n' "$content" | grep -q '%ARCHISO'; then
|
||||
fail "$member contains an unsubstituted %ARCHISO% placeholder"
|
||||
fi
|
||||
while IFS= read -r val; do
|
||||
[ -z "$val" ] && continue
|
||||
search_refs=$((search_refs + 1))
|
||||
if [ "$val" != "$vol_uuid" ]; then
|
||||
fail "$member: archisosearchuuid=$val != volume UUID '$vol_uuid'"
|
||||
fi
|
||||
echo " ✅ $member: archisosearchuuid=$val matches volume UUID"
|
||||
done < <(printf '%s\n' "$content" | grep -o 'archisosearchuuid=[^[:space:]]*' | cut -d= -f2- || true)
|
||||
done < <(bsdtar -tf "$iso" | grep -E '(^|/)(loader/entries/.*\.conf|boot/.*\.(cfg|conf))$' || true)
|
||||
if (( search_refs == 0 )); then
|
||||
fail "no archisosearchuuid= references found in shipped boot configs (params removed without updating this check?)"
|
||||
fi
|
||||
|
||||
echo "✅ ISO search metadata consistent: $uuid_refs uuid file(s), $search_refs search reference(s)"
|
||||
|
||||
- name: Generate Unique Dynamic Tag
|
||||
id: tag
|
||||
env:
|
||||
@@ -73,22 +303,32 @@ jobs:
|
||||
echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_name=Arch Linux ISO (${BASE_DATE} Rev ${REVISION})" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create Release and Upload ISO via Git LFS
|
||||
- name: Create Release and Upload ISO
|
||||
id: release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
# Use internal Gitea URL to bypass traefik proxy timeout on large uploads
|
||||
GITEA_API: http://192.168.1.168:3000/api/v1
|
||||
GITEA_PUBLIC_URL: https://git.vilaythong.us
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
RELEASES_REPO: alvinlollo/AlvinArch-releases
|
||||
TAG_NAME: ${{ steps.tag.outputs.tag_name }}
|
||||
RELEASE_NAME: ${{ steps.tag.outputs.release_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
set -eu
|
||||
shopt -s nullglob
|
||||
test -n "$GITEA_TOKEN"
|
||||
test -n "$TAG_NAME"
|
||||
test -n "$RELEASE_NAME"
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "❌ GITEA_TOKEN is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$TAG_NAME" ]; then
|
||||
echo "❌ TAG_NAME is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$RELEASE_NAME" ]; then
|
||||
echo "❌ RELEASE_NAME is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pre-flight: confirm the internal Gitea API is reachable so failures are clear
|
||||
if ! curl -sfS -o /dev/null "$GITEA_API/version"; then
|
||||
@@ -96,24 +336,13 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASES_OWNER="${RELEASES_REPO%%/*}"
|
||||
RELEASES_NAME="${RELEASES_REPO#*/}"
|
||||
GITEA_BASE="${GITEA_API%/api/v1}"
|
||||
GITEA_SCHEME="${GITEA_BASE%%://*}"
|
||||
GITEA_HOST="${GITEA_BASE#*://}"
|
||||
CLONE_URL="${GITEA_SCHEME}://${RELEASES_OWNER}:${GITEA_TOKEN}@${GITEA_HOST}/${RELEASES_REPO}.git"
|
||||
REPO_DIR=/tmp/alvinarch-releases
|
||||
trap 'rm -rf "$REPO_DIR"' EXIT
|
||||
|
||||
# Collect the ISOs produced by the build
|
||||
# Collect the ISOs produced by the build and record SHA-256
|
||||
isos=(out/*.iso)
|
||||
if (( ${#isos[@]} == 0 )); then
|
||||
echo "❌ No ISO found in out/ — nothing to upload, aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Compress each ISO with zstd and record its SHA-256
|
||||
zst_files=()
|
||||
sha256sums=()
|
||||
for iso in "${isos[@]}"; do
|
||||
fname=$(basename "$iso")
|
||||
@@ -123,155 +352,256 @@ jobs:
|
||||
echo "❌ '$iso' is only $size bytes — refusing to upload (not an ISO)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Compressing $fname ($(( size / 1024 / 1024 )) MiB) with zstd..."
|
||||
zstd -T0 -19 -f "$iso" -o "$iso.zst"
|
||||
sha=$(sha256sum "$iso.zst" | awk '{print $1}')
|
||||
echo " ✅ ${fname}.zst: $(( $(stat -c %s "$iso.zst") / 1024 / 1024 )) MiB, sha256=$sha"
|
||||
zst_files+=("$iso.zst")
|
||||
sha=$(sha256sum "$iso" | awk '{print $1}')
|
||||
if [ -z "$sha" ]; then
|
||||
echo "❌ Failed to compute SHA-256 for '$iso'" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " ✅ $fname: $(( size / 1024 / 1024 )) MiB, sha256=$sha"
|
||||
sha256sums+=("$sha")
|
||||
done
|
||||
|
||||
# Make sure the releases repo exists (create it via API if missing)
|
||||
if ! curl -sf -o /dev/null -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$RELEASES_REPO"; then
|
||||
echo "Creating releases repo '$RELEASES_REPO'..."
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITEA_API/user/repos" \
|
||||
-d "{\"name\": \"$RELEASES_NAME\", \"description\": \"Compressed Arch Linux live ISOs (Git LFS)\", \"auto_init\": true}" > /dev/null
|
||||
fi
|
||||
|
||||
# Push the compressed ISOs to the releases repo via Git LFS
|
||||
if ! git clone --quiet "$CLONE_URL" "$REPO_DIR" 2>/dev/null; then
|
||||
echo "❌ Failed to clone $RELEASES_REPO (check token access)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Work on the 'main' branch (create it if the remote default differs)
|
||||
if git -C "$REPO_DIR" rev-parse --verify -q origin/main >/dev/null 2>&1; then
|
||||
git -C "$REPO_DIR" checkout -q -B main origin/main
|
||||
else
|
||||
git -C "$REPO_DIR" checkout -q -B main
|
||||
fi
|
||||
|
||||
git -C "$REPO_DIR" lfs install --local
|
||||
git -C "$REPO_DIR" lfs track "*.iso.zst"
|
||||
mkdir -p "$REPO_DIR/releases/$TAG_NAME"
|
||||
for zst in "${zst_files[@]}"; do
|
||||
cp "$zst" "$REPO_DIR/releases/$TAG_NAME/"
|
||||
done
|
||||
|
||||
# Seed a short README redirecting to the main repo (status badges on top)
|
||||
cat > "$REPO_DIR/README.md" <<'MDEOF'
|
||||
[](https://git.vilaythong.us/alvinlollo/AlvinArch/actions?workflow=build-iso.yaml)
|
||||
[](https://git.vilaythong.us/alvinlollo/AlvinArch/actions?workflow=tests.yaml)
|
||||
|
||||
# AlvinArch Releases
|
||||
|
||||
Compressed (zstd) Arch Linux live ISOs built automatically by
|
||||
[alvinlollo/AlvinArch](https://git.vilaythong.us/alvinlollo/AlvinArch).
|
||||
|
||||
**Main repository:** [alvinlollo/AlvinArch](https://git.vilaythong.us/alvinlollo/AlvinArch)
|
||||
MDEOF
|
||||
|
||||
git -C "$REPO_DIR" config user.name "Gitea Actions"
|
||||
git -C "$REPO_DIR" config user.email "actions@gitea.local"
|
||||
git -C "$REPO_DIR" add .gitattributes README.md "releases/$TAG_NAME"
|
||||
if git -C "$REPO_DIR" diff --cached --quiet; then
|
||||
echo "⚠️ No changes to commit for $TAG_NAME — releasing as-is." >&2
|
||||
else
|
||||
git -C "$REPO_DIR" commit --quiet -m "Add $TAG_NAME"
|
||||
fi
|
||||
if ! git -C "$REPO_DIR" push --quiet origin main 2>/dev/null; then
|
||||
echo "❌ Failed to push $TAG_NAME to $RELEASES_REPO." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify each LFS object exists server-side (oid == the sha256 we recorded)
|
||||
for i in "${!zst_files[@]}"; do
|
||||
fname=$(basename "${zst_files[$i]}")
|
||||
size=$(stat -c %s "${zst_files[$i]}")
|
||||
oid=${sha256sums[$i]}
|
||||
lfs_payload=$(jq -nc --arg oid "$oid" --argjson sz "$size" \
|
||||
'{operation:"download",transfers:["basic"],objects:[{oid:$oid,size:$sz}]}')
|
||||
obj=$(curl -sf -u "$RELEASES_OWNER:$GITEA_TOKEN" \
|
||||
-H "Content-Type: application/vnd.git-lfs+json" \
|
||||
-d "$lfs_payload" \
|
||||
"$GITEA_BASE/$RELEASES_REPO.git/info/lfs/objects/batch")
|
||||
if [[ "$(jq -r '.objects[0] | has("actions")' <<< "$obj")" != "true" ]]; then
|
||||
echo "❌ Verification failed: LFS object for '$fname' ($size bytes) missing on server." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " ✅ Verified '$fname' ($size bytes) on $RELEASES_REPO"
|
||||
done
|
||||
|
||||
# Create the release with a link to the file directory + SHA-256 checksums
|
||||
DOWNLOAD_URL="$GITEA_PUBLIC_URL/$RELEASES_REPO/src/branch/main/releases/$TAG_NAME"
|
||||
printf -v body 'Arch Linux live ISO %s\n\nDownload: %s\n\nChecksums (SHA-256):\n' \
|
||||
"$RELEASE_NAME" "$DOWNLOAD_URL"
|
||||
for i in "${!zst_files[@]}"; do
|
||||
printf -v line -- '- `%s`: sha256 `%s`\n' "$(basename "${zst_files[$i]}")" "${sha256sums[$i]}"
|
||||
# Build the release body with SHA-256 checksums
|
||||
printf -v body 'Arch Linux live ISO %s\n\nChecksums (SHA-256):\n' "$RELEASE_NAME"
|
||||
for i in "${!isos[@]}"; do
|
||||
# Release body: markdown backticks are literal (printf format below)
|
||||
# shellcheck disable=SC2016
|
||||
printf -v line -- '- `%s`: sha256 `%s`\n' "$(basename "${isos[$i]}")" "${sha256sums[$i]}"
|
||||
body+="$line"
|
||||
done
|
||||
|
||||
# Create the release (curl result written to a file, checked with if)
|
||||
payload=$(jq -nc --arg tag "$TAG_NAME" --arg name "$RELEASE_NAME" --arg body "$body" \
|
||||
'{tag_name:$tag,name:$name,body:$body}')
|
||||
RELEASE_ID=$(curl -sf -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases" \
|
||||
-d "$payload" | jq -er '.id')
|
||||
|
||||
echo " ✅ Released $TAG_NAME (ID: $RELEASE_ID) with download link + SHA-256 checksums"
|
||||
|
||||
# Mirror the release tag on the releases repo for redundancy/discovery
|
||||
if ! git -C "$REPO_DIR" ls-remote --exit-code origin \
|
||||
refs/tags/"$TAG_NAME" >/dev/null 2>&1; then
|
||||
git -C "$REPO_DIR" tag "$TAG_NAME"
|
||||
if ! git -C "$REPO_DIR" push --quiet origin "$TAG_NAME" 2>/dev/null; then
|
||||
echo "❌ Failed to push tag $TAG_NAME to $RELEASES_REPO." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if ! curl -sf -o /dev/null -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$RELEASES_REPO/releases/tags/$TAG_NAME"; then
|
||||
curl -sf -X POST \
|
||||
if ! curl -sf -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITEA_API/repos/$RELEASES_REPO/releases" \
|
||||
-d "$payload" > /dev/null
|
||||
echo " ✅ Mirrored $TAG_NAME release on $RELEASES_REPO"
|
||||
"$GITEA_API/repos/$REPOSITORY/releases" \
|
||||
-d "$payload" -o "$tmpdir/release.json"; then
|
||||
echo "❌ Failed to create release $TAG_NAME" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! RELEASE_ID=$(jq -er '.id' "$tmpdir/release.json"); then
|
||||
echo "❌ Release response contained no id" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
|
||||
echo " ✅ Created release $TAG_NAME (ID: $RELEASE_ID)"
|
||||
|
||||
# Upload each ISO as a release asset, up to 3 attempts per file
|
||||
MAX_ATTEMPTS=3
|
||||
for i in "${!isos[@]}"; do
|
||||
iso=${isos[$i]}
|
||||
fname=$(basename "$iso")
|
||||
size=$(stat -c %s "$iso")
|
||||
echo "Uploading $fname ($(( size / 1024 / 1024 )) MiB) to release $TAG_NAME..."
|
||||
uploaded=0
|
||||
attempt=1
|
||||
while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do
|
||||
# Fresh output file each attempt: curl does not truncate -o on
|
||||
# transport failure, so a stale body must not be misreported.
|
||||
rm -f "$tmpdir/upload.json"
|
||||
# A previous attempt may have saved the asset but lost the
|
||||
# response; Gitea rejects duplicate names, so only after the
|
||||
# first attempt check for and remove same-name leftovers
|
||||
if [ "$attempt" -gt 1 ]; then
|
||||
if curl -sf -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets" \
|
||||
-o "$tmpdir/assets.json"; then
|
||||
asset_ids=$(jq -r --arg name "$fname" \
|
||||
'.[] | select(.name == $name) | .id' "$tmpdir/assets.json")
|
||||
while read -r asset_id; do
|
||||
if [ -n "$asset_id" ]; then
|
||||
echo " ↻ Removing leftover asset id=$asset_id from a previous attempt"
|
||||
if ! curl -sf -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets/$asset_id" \
|
||||
-o /dev/null; then
|
||||
echo " ⚠️ Could not remove leftover asset id=$asset_id (continuing)" >&2
|
||||
fi
|
||||
fi
|
||||
done <<< "$asset_ids"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Capture HTTP status explicitly: `-f` would swallow the Gitea
|
||||
# error body (e.g. attachment size-limit messages) and leave the
|
||||
# failure reason unknown.
|
||||
if http_code=$(curl -sS -X POST --max-time 600 \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-F "attachment=@$iso" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets" \
|
||||
-o "$tmpdir/upload.json" -w '%{http_code}'); then
|
||||
if [ "${http_code:0:1}" = "2" ] \
|
||||
&& download_url=$(jq -er '.browser_download_url' "$tmpdir/upload.json"); then
|
||||
echo " ✅ Uploaded $fname (attempt $attempt/$MAX_ATTEMPTS): $download_url"
|
||||
uploaded=1
|
||||
break
|
||||
fi
|
||||
echo " ⚠️ Upload of $fname: HTTP $http_code (attempt $attempt/$MAX_ATTEMPTS)" >&2
|
||||
echo " Response: $(head -c 2000 "$tmpdir/upload.json" 2>/dev/null)" >&2
|
||||
else
|
||||
rc=$?
|
||||
echo " ⚠️ Upload of $fname failed (attempt $attempt/$MAX_ATTEMPTS): curl exit $rc, HTTP ${http_code:-000}" >&2
|
||||
if [ -s "$tmpdir/upload.json" ]; then
|
||||
echo " Response: $(head -c 2000 "$tmpdir/upload.json")" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$attempt" -lt "$MAX_ATTEMPTS" ]; then
|
||||
sleep $(( attempt * 10 ))
|
||||
fi
|
||||
attempt=$(( attempt + 1 ))
|
||||
done
|
||||
|
||||
if [ "$uploaded" -ne 1 ]; then
|
||||
echo "❌ Failed to upload $fname after $MAX_ATTEMPTS attempts — aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "✅ All ISOs uploaded to release $TAG_NAME"
|
||||
exit 0
|
||||
|
||||
- name: Verify uploaded ISO checksums
|
||||
if: success()
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
# Internal origin bypasses traefik (and its Anubis bot wall) — the
|
||||
# same host the upload step already uses.
|
||||
GITEA_API: http://192.168.1.168:3000/api/v1
|
||||
GITEA_ORIGIN: http://192.168.1.168:3000
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
TAG_NAME: ${{ steps.tag.outputs.tag_name }}
|
||||
RELEASE_ID: ${{ steps.release.outputs.release_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
for var in GITEA_TOKEN TAG_NAME RELEASE_ID; do
|
||||
if [ -z "${!var:-}" ]; then
|
||||
echo "❌ $var is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
case "$RELEASE_ID" in
|
||||
''|*[!0-9]*) echo "❌ RELEASE_ID is not numeric: $RELEASE_ID" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# fail_verify <reason...>: job fails but the release is kept — used
|
||||
# for transport/API errors where the upload itself may be fine.
|
||||
fail_verify() {
|
||||
echo "❌ $1" >&2
|
||||
echo " Release $TAG_NAME (ID $RELEASE_ID) left in place (not proven corrupt)." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# abort_release <reason...>: the asset is provably wrong — delete the
|
||||
# release so a corrupt asset can never be published, then fail.
|
||||
abort_release() {
|
||||
echo "❌ $1" >&2
|
||||
echo "Deleting release $TAG_NAME (ID $RELEASE_ID) so the bad upload cannot be used..." >&2
|
||||
if ! curl -sf -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID" -o /dev/null; then
|
||||
echo "⚠️ Could not delete release $RELEASE_ID — delete it manually!" >&2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
isos=(out/*.iso)
|
||||
if (( ${#isos[@]} == 0 )); then
|
||||
echo "❌ No ISO in out/ to verify" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Keep only latest 3 releases
|
||||
# Recompute local checksums
|
||||
declare -A local_sha local_size
|
||||
for iso in "${isos[@]}"; do
|
||||
fname=$(basename "$iso")
|
||||
local_sha["$fname"]=$(sha256sum "$iso" | awk '{print $1}')
|
||||
local_size["$fname"]=$(stat -c %s "$iso")
|
||||
if [ -z "${local_sha[$fname]}" ]; then
|
||||
echo "❌ Failed to compute SHA-256 for '$iso'" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# List the release's assets as uploaded (API error = transient, no delete)
|
||||
if ! curl -sf -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets" \
|
||||
-o "$tmpdir/assets.json"; then
|
||||
fail_verify "Failed to list assets of release $RELEASE_ID (API/transport error)"
|
||||
fi
|
||||
|
||||
for iso in "${isos[@]}"; do
|
||||
fname=$(basename "$iso")
|
||||
api_size=$(jq -er --arg name "$fname" \
|
||||
'.[] | select(.name == $name) | .size' "$tmpdir/assets.json" 2>/dev/null) \
|
||||
|| abort_release "Asset '$fname' missing from release $RELEASE_ID"
|
||||
if [ "$api_size" != "${local_size[$fname]}" ]; then
|
||||
abort_release "Asset '$fname' size mismatch: server=$api_size local=${local_size[$fname]}"
|
||||
fi
|
||||
|
||||
# Download from the internal origin (traefik/Anubis bypassed).
|
||||
# iso_name contains a space ("Alvin archlinux"), so the filename
|
||||
# must be percent-encoded or curl rejects the URL outright.
|
||||
enc_fname=$(jq -rn --arg v "$fname" '$v|@uri')
|
||||
url="$GITEA_ORIGIN/$REPOSITORY/releases/download/$TAG_NAME/$enc_fname"
|
||||
downloaded=0
|
||||
for attempt in 1 2 3; do
|
||||
# Try header auth, then anonymous (public repo). No ?token=
|
||||
# fallback: a token in the query string lands in access logs.
|
||||
if curl -sfL --max-time 600 -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$url" -o "$tmpdir/$fname"; then
|
||||
downloaded=1
|
||||
elif curl -sfL --max-time 600 "$url" -o "$tmpdir/$fname"; then
|
||||
downloaded=1
|
||||
fi
|
||||
[ "$downloaded" -eq 1 ] && break
|
||||
echo " ⚠️ Download of $fname failed (attempt $attempt/3)" >&2
|
||||
[ "$attempt" -lt 3 ] && sleep $(( attempt * 10 ))
|
||||
done
|
||||
if [ "$downloaded" -ne 1 ]; then
|
||||
fail_verify "Could not download asset '$fname' back from release $RELEASE_ID (auth/transport error, not proven corrupt)"
|
||||
fi
|
||||
|
||||
actual=$(sha256sum "$tmpdir/$fname" | awk '{print $1}')
|
||||
expected=${local_sha[$fname]}
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
abort_release "SHA-256 mismatch for '$fname': server=$actual local=$expected"
|
||||
fi
|
||||
echo " ✅ $fname: size and SHA-256 match"
|
||||
rm -f "$tmpdir/$fname"
|
||||
done
|
||||
|
||||
echo "✅ All uploaded assets verified against local ISO checksums"
|
||||
exit 0
|
||||
|
||||
- name: Keep only latest 2 releases
|
||||
if: success()
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_API: http://192.168.1.168:3000/api/v1
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
RELEASES_REPO: alvinlollo/AlvinArch-releases
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$GITEA_TOKEN"
|
||||
|
||||
for repo in "$REPOSITORY" "$RELEASES_REPO"; do
|
||||
echo "Pruning old releases in $repo..."
|
||||
readarray -t ids < <(curl -sf -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$repo/releases" \
|
||||
| jq -er 'sort_by(.created_at) | .[].id')
|
||||
echo "Pruning old releases in $REPOSITORY..."
|
||||
readarray -t ids < <(curl -sf -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases" \
|
||||
| jq -er 'sort_by(.created_at) | .[].id')
|
||||
|
||||
to_delete=$(( ${#ids[@]} - 3 ))
|
||||
if (( to_delete > 0 )); then
|
||||
for ((i = 0; i < to_delete; i++)); do
|
||||
id=${ids[$i]}
|
||||
echo "Deleting old release ID: $id (in $repo)"
|
||||
curl -sf -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$repo/releases/$id"
|
||||
done
|
||||
fi
|
||||
done
|
||||
to_delete=$(( ${#ids[@]} - 2 ))
|
||||
if (( to_delete > 0 )); then
|
||||
for ((i = 0; i < to_delete; i++)); do
|
||||
id=${ids[$i]}
|
||||
echo "Deleting old release ID: $id"
|
||||
curl -sf -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API/repos/$REPOSITORY/releases/$id"
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Cleanup mkarchiso mounts and workdirs
|
||||
if: always() # Runs on success, failure, or cancellation
|
||||
@@ -287,15 +617,17 @@ jobs:
|
||||
# Unmount any leftover loop devices or bind mounts in the work directory
|
||||
$PRIV umount -R "$PWD/work" 2>/dev/null || true
|
||||
|
||||
# Clean up loop devices if any remain attached to the work dir
|
||||
$PRIV losetup -D || true
|
||||
# Detach only loop devices backed by files in our work dir:
|
||||
# `losetup -D` would detach every loop device on the runner host.
|
||||
$PRIV losetup -nO NAME,BACK-FILE 2>/dev/null \
|
||||
| awk -v w="$PWD/work/" 'index($2, w) == 1 {print $1}' \
|
||||
| while read -r dev; do $PRIV losetup -d "$dev" || true; done
|
||||
|
||||
# Remove working directories with proper permissions
|
||||
$PRIV rm -rf "$PWD/work"
|
||||
|
||||
- name: Remove built ISO (success or cancellation)
|
||||
if: success() || cancelled()
|
||||
- name: Remove built ISO
|
||||
if: always()
|
||||
run: |
|
||||
set -eu
|
||||
# On failure the ISO is kept in out/ for manual upload
|
||||
rm -rf "$PWD/out"
|
||||
rm -rf "$PWD/out"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Upstream pin check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 1" # Mondays 06:00 UTC — catches upstream/Qt drift between ISO builds
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD"
|
||||
- "localrepo/check-quickshell-pin.sh"
|
||||
- "airootfs/root/customize_airootfs.sh"
|
||||
- ".gitea/workflows/pin-check.yaml"
|
||||
|
||||
jobs:
|
||||
quickshell-pin:
|
||||
runs-on: archlinux
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Tripwire for the failure mode that broke ISO build run 137:
|
||||
# a quickshell pin (ours or end-4's) that predates a Qt compat fix
|
||||
# or has simply gone stale. Fails the job so the drift is visible
|
||||
# in Actions instead of surfacing as a 3-hour ISO build failure.
|
||||
- name: Check quickshell pins
|
||||
run: ./localrepo/check-quickshell-pin.sh
|
||||
+129
-27
@@ -5,6 +5,12 @@ on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
# This job rewrites the runner's shared /etc/pacman.conf - never let two
|
||||
# runs do that at the same time.
|
||||
concurrency:
|
||||
group: host-pacman-conf
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: archlinux
|
||||
@@ -36,25 +42,52 @@ jobs:
|
||||
rm -f /var/lib/pacman/sync/cachyos* /var/lib/pacman/sync/chaotic*
|
||||
|
||||
- name: Install prerequisites
|
||||
run: pacman -Syu --noconfirm --needed shellcheck yamllint
|
||||
run: |
|
||||
# The gateway intermittently drops UDP/53 (it failed build run 131
|
||||
# at the equivalent step); retry with backoff instead of dying on
|
||||
# the first bad DNS burst.
|
||||
synced=0
|
||||
for try in 1 2 3 4 5; do
|
||||
if pacman -Syu --noconfirm --needed shellcheck yamllint; then
|
||||
synced=1
|
||||
break
|
||||
fi
|
||||
echo "⚠️ pacman -Syu failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
|
||||
sleep $(( try * 15 ))
|
||||
done
|
||||
if [ "$synced" -ne 1 ]; then
|
||||
echo "❌ pacman -Syu failed after 5 attempts — giving up" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set up CachyOS repo
|
||||
run: |
|
||||
# Key straight from the keyserver; no pinned keyring/mirrorlist
|
||||
# package URLs (they 404 once the mirror prunes that version, and
|
||||
# the mirrorlist was overwritten below anyway).
|
||||
pacman-key --recv-keys F3B607488DB35A47 --keyserver keyserver.ubuntu.com
|
||||
pacman-key --lsign-key F3B607488DB35A47
|
||||
|
||||
pacman -U --noconfirm \
|
||||
'https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst' \
|
||||
'https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-27-1-any.pkg.tar.zst'
|
||||
|
||||
# Fix the mirrorlist URL (pacnew has wrong format)
|
||||
printf '%s\n' 'Server = https://mirror.cachyos.org/repo/$arch/$repo' \
|
||||
> /etc/pacman.d/cachyos-mirrorlist
|
||||
|
||||
printf '\n[cachyos]\nInclude = /etc/pacman.d/cachyos-mirrorlist\n' \
|
||||
>> /etc/pacman.conf
|
||||
|
||||
pacman -Syu --noconfirm --needed
|
||||
# Retry: gateway drops UDP/53 in bursts (see run 131)
|
||||
synced=0
|
||||
for try in 1 2 3 4 5; do
|
||||
if pacman -Syu --noconfirm --needed; then
|
||||
synced=1
|
||||
break
|
||||
fi
|
||||
echo "⚠️ pacman -Syu failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
|
||||
sleep $(( try * 15 ))
|
||||
done
|
||||
if [ "$synced" -ne 1 ]; then
|
||||
echo "❌ pacman -Syu failed after 5 attempts — giving up" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set up chaotic-aur repo
|
||||
run: |
|
||||
@@ -65,15 +98,34 @@ jobs:
|
||||
printf '\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n' \
|
||||
>> /etc/pacman.conf
|
||||
|
||||
pacman -Syu --noconfirm --needed
|
||||
# Retry: gateway drops UDP/53 in bursts (see run 131)
|
||||
synced=0
|
||||
for try in 1 2 3 4 5; do
|
||||
if pacman -Syu --noconfirm --needed; then
|
||||
synced=1
|
||||
break
|
||||
fi
|
||||
echo "⚠️ pacman -Syu failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
|
||||
sleep $(( try * 15 ))
|
||||
done
|
||||
if [ "$synced" -ne 1 ]; then
|
||||
echo "❌ pacman -Syu failed after 5 attempts — giving up" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Lint shell scripts with ShellCheck
|
||||
run: |
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " Running ShellCheck on all .sh files"
|
||||
echo " Running ShellCheck on all bash scripts"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
|
||||
SH_FILES=$(find . -name '*.sh' -not -path './.git/*')
|
||||
# *.sh plus extensionless bash scripts (alvin-install, choose-mirror,
|
||||
# livecd-sound, ...) picked up by their shebang
|
||||
SH_FILES=$(
|
||||
find . -name '*.sh' -not -path './.git/*'
|
||||
find airootfs/usr/local/bin -type f ! -name '*.sh' \
|
||||
-exec grep -lE '^#!(/usr/bin/env bash|/bin/bash)' {} +
|
||||
)
|
||||
if [ -z "$SH_FILES" ]; then
|
||||
echo "No shell scripts found."
|
||||
exit 0
|
||||
@@ -216,10 +268,18 @@ jobs:
|
||||
done
|
||||
|
||||
# 2) Check localrepo/ for a pre-built .pkg.tar.zst
|
||||
if [[ $MATCHED -eq 0 ]] && compgen -G "localrepo/${pkg}-*.pkg.tar.zst" > /dev/null 2>&1; then
|
||||
MATCHED=1
|
||||
FOUND=$((FOUND + 1))
|
||||
echo "📦 LOCAL: $pkg (pre-built in localrepo/)"
|
||||
# Exact name: strip -<pkgver>-<pkgrel>-<arch>.pkg.tar.zst, so
|
||||
# "foo" is not satisfied by foo-bar-1.0-1-any.pkg.tar.zst
|
||||
if [[ $MATCHED -eq 0 ]]; then
|
||||
for f in localrepo/"${pkg}"-*.pkg.tar.zst; do
|
||||
b=${f##*/}
|
||||
if [[ "${b%-*-*-*}" == "$pkg" ]]; then
|
||||
MATCHED=1
|
||||
FOUND=$((FOUND + 1))
|
||||
echo "📦 LOCAL: $pkg (pre-built in localrepo/)"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 3) Check build-end4-deps.sh for the package name
|
||||
@@ -267,23 +327,65 @@ jobs:
|
||||
echo " Checking for conflicting packages in packages.x86_64"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
|
||||
# Collect all package names from the list
|
||||
# Resolve against the repos mkarchiso sees, including [local-aur]
|
||||
# (localrepo/), so conflicts between pre-built AUR packages and
|
||||
# anything else are caught here instead of in the ISO build.
|
||||
# A fresh dbpath = empty local db, like pacstrap's target root:
|
||||
# packages installed on the runner can neither mask a conflict
|
||||
# nor cause a false one.
|
||||
CONF=$(mktemp)
|
||||
DB=$(mktemp -d)
|
||||
ROOT=$(mktemp -d)
|
||||
cp /etc/pacman.conf "$CONF"
|
||||
printf '\n[local-aur]\nSigLevel = Optional TrustAll\nServer = file://%s/localrepo\n' \
|
||||
"$PWD" >> "$CONF"
|
||||
mkdir -p "$DB/sync"
|
||||
cp -p /var/lib/pacman/sync/*.db "$DB/sync/"
|
||||
pac() { pacman --config "$CONF" --dbpath "$DB" "$@"; }
|
||||
pac -Sy --noconfirm
|
||||
# From here on never download (set after -Sy, which needs to fetch
|
||||
# the file:// local-aur db): the check only needs a prepared
|
||||
# transaction
|
||||
sed -i '/^\[options\]/a XferCommand = /usr/bin/false %u' "$CONF"
|
||||
|
||||
# Packages built at ISO build time (build-end4-deps.sh) can't be
|
||||
# resolved yet; everything else must be
|
||||
mapfile -t AVAIL < <(pac -Slq | sort -u)
|
||||
PKGS=()
|
||||
while IFS= read -r pkg; do
|
||||
[[ -z "$pkg" || "$pkg" =~ ^# ]] && continue
|
||||
PKGS+=("$pkg")
|
||||
if printf '%s\n' "${AVAIL[@]}" | grep -qxF -- "$pkg"; then
|
||||
PKGS+=("$pkg")
|
||||
else
|
||||
echo "⚠️ Skipping unresolvable package: $pkg"
|
||||
fi
|
||||
done < packages.x86_64
|
||||
|
||||
# Use pacman -Sp --print to resolve the full install set and check for conflicts
|
||||
# This catches "X and Y are in conflict" errors before the ISO build
|
||||
PKG_LIST=$(printf '%s\n' "${PKGS[@]}")
|
||||
if CONFLICTS=$(echo "$PKG_LIST" | pacman -Sp --needed - 2>&1); then
|
||||
echo "✅ No package conflicts detected"
|
||||
# `pacman -Sp` skips conflict checks entirely, so run a real -S
|
||||
# into an empty root. Conflicts and broken deps fail in "prepare",
|
||||
# before any download; a clean list gets as far as the download,
|
||||
# which the XferCommand above makes fail on purpose.
|
||||
if [ ${#PKGS[@]} -eq 0 ]; then
|
||||
echo "✅ No resolvable packages to check."
|
||||
else
|
||||
echo "$CONFLICTS" | grep -i "conflict" || true
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " ❌ Conflicting packages found — fix packages.x86_64"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
exit 1
|
||||
# pacman exits non-zero either way; judge by its output, not
|
||||
# its status (bash -e would otherwise kill the step silently)
|
||||
OUT=$(pac -S --noconfirm --root "$ROOT" --cachedir "$ROOT" "${PKGS[@]}" 2>&1) || true
|
||||
if grep -q 'failed to prepare transaction' <<< "$OUT"; then
|
||||
echo "------- Pacman Output -------"
|
||||
grep -vE '^(http|file|ftp)' <<< "$OUT" | tail -40
|
||||
echo "-----------------------------"
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " ❌ Conflicting/unresolvable packages — fix packages.x86_64"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
exit 1
|
||||
elif ! grep -q 'Proceed with installation' <<< "$OUT"; then
|
||||
echo "------- Pacman Output -------"
|
||||
tail -40 <<< "$OUT"
|
||||
echo "-----------------------------"
|
||||
echo "❌ pacman stopped before the conflict check completed"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ No package conflicts detected (repos + local-aur)"
|
||||
fi
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# Multi-GB mkarchiso output (see AGENTS.md build command)
|
||||
work/
|
||||
out/
|
||||
|
||||
# In-image copy of localrepo/ — populated by localrepo/update.sh and
|
||||
# .gitea/workflows/build-iso.yaml, shipped as /opt/localrepo on the ISO
|
||||
airootfs/opt/localrepo/
|
||||
|
||||
# Build stamp staged by profiledef.sh at ISO build time (see AGENTS.md);
|
||||
# consumed by customize_airootfs.sh, never committed
|
||||
airootfs/root/.iso-build-stamp
|
||||
|
||||
# makepkg output from localrepo/build-end4-deps.sh (it builds in place):
|
||||
# keep only the PKGBUILDs and hooks under end4-pkgbuilds/
|
||||
localrepo/end4-pkgbuilds/*/*
|
||||
!localrepo/end4-pkgbuilds/*/PKGBUILD
|
||||
!localrepo/end4-pkgbuilds/*/*.hook
|
||||
# repo-add backups
|
||||
localrepo/*.old
|
||||
@@ -17,9 +17,12 @@ sudo mkarchiso -v -w work -o out/ .
|
||||
./localrepo/update.sh pkg1.pkg.tar.zst # add files, then re-index
|
||||
```
|
||||
|
||||
`update.sh` also refreshes `airootfs/opt/localrepo/` (the copy that ships on the
|
||||
ISO), so run it after touching `localrepo/` before building.
|
||||
|
||||
## CI Checks (must pass before merge)
|
||||
|
||||
- **ShellCheck**: all `.sh` files checked with `shellcheck -x -s bash`
|
||||
- **ShellCheck**: all `.sh` files, plus extensionless bash scripts in `airootfs/usr/local/bin/` (by shebang), checked with `shellcheck -x -s bash`
|
||||
- **yamllint**: all `.yml`/`.yaml` files (relaxed config)
|
||||
- **packages.x86_64 syntax**: one package per line, no trailing whitespace, `[a-zA-Z0-9._@+\-]+` only
|
||||
- **Package repo check**: every package in `packages.x86_64` must be resolvable via `pacman -Si` in allowed repos (`cachyos`, `extra`, `multilib`, `chaotic-aur`), OR have a pre-built `.pkg.tar.zst` in `localrepo/`, OR appear in `localrepo/build-end4-deps.sh`
|
||||
@@ -32,6 +35,13 @@ sudo mkarchiso -v -w work -o out/ .
|
||||
2. **`localrepo/*.pkg.tar.zst`** — pre-built AUR packages. The `local-aur` repo is configured in `pacman.conf` and picked up by mkarchiso automatically.
|
||||
3. **`build-end4-deps.sh`** — builds end-4 AUR deps at build time.
|
||||
|
||||
There is a **fourth source**: `customize_airootfs.sh` runs end-4's own
|
||||
`./setup install-deps` inside the image (as a throwaway `ii-builder` account),
|
||||
which resolves and compiles everything the dotfiles need (quickshell, MicroTeX,
|
||||
theming, fonts, ...). Those packages are installed in the live system, land in
|
||||
`pkglist.x86_64.txt`, and are harvested into `/opt/localrepo` so pacstrap can
|
||||
install them into the target without the network.
|
||||
|
||||
**Important**: `localrepo/update.sh` auto-appends new package names to `packages.x86_64`. If you remove a package from `packages.x86_64` that exists in `localrepo/`, it will be re-added on the next `update.sh` run.
|
||||
|
||||
## Key Files
|
||||
@@ -39,17 +49,72 @@ sudo mkarchiso -v -w work -o out/ .
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `packages.x86_64` | Live ISO package list (~402 packages) |
|
||||
| `profiledef.sh` | ISO metadata, compression, boot modes |
|
||||
| `profiledef.sh` | ISO metadata, compression, boot modes, build-stamp staging |
|
||||
| `pacman.conf` | Repos: cachyos → core → extra → multilib → chaotic-aur → local-aur |
|
||||
| `bootstrap_packages` | Minimal build-time packages (`base`, `arch-install-scripts`) |
|
||||
| `airootfs/root/customize_airootfs.sh` | Runs inside chroot during build: keyrings, oh-my-zsh, lazy.nvim, plugins |
|
||||
| `airootfs/root/customize_airootfs.sh` | Runs inside chroot during build: keyrings, skel dotfiles (incl. end4-pC), qsConfig patch, end-4 `install-deps`, package harvest, uv/font pre-warm |
|
||||
| `airootfs/root/.live-pacman.conf` | `/etc/pacman.conf` of the built image (adds `[local-aur]` = `file:///opt/localrepo`) |
|
||||
| `airootfs/usr/local/bin/alvin-install` | Installer: partitions, pacstrap, LUKS (systemd initramfs **or** experimental unl0kr + optional TPM2 unlock), user (fish shell), git identity + GitHub SSH keys, login manager (none/greetd/sddm/gdm) + autologin, optional sshd (default on), end-4 dotfiles (`--skip-alldeps`), boot |
|
||||
| `localrepo/build-end4-deps.sh` | Non-interactive AUR builds for end-4 dependencies |
|
||||
| `localrepo/update.sh` | Regenerates localrepo DB + syncs names to packages.x86_64 |
|
||||
| `localrepo/update.sh` | Regenerates localrepo DB, syncs names to packages.x86_64, syncs `airootfs/opt/localrepo/` |
|
||||
| `localrepo/check-quickshell-pin.sh` | Tripwire: our quickshell pin and end-4's must include the Qt 6.12 fix and stay <45d old (`QUICKSHELL_PIN_MAX_AGE_DAYS` overrides) |
|
||||
| `.gitea/workflows/pin-check.yaml` | Runs the pin check weekly (Mon 06:00), on dispatch, and on pushes touching pins/`customize_airootfs.sh` |
|
||||
|
||||
## alvin-install (installer inside the ISO)
|
||||
|
||||
Source: `airootfs/usr/local/bin/alvin-install` (~1,880 lines, **no `.sh` extension**).
|
||||
The airootfs overlay ships it as `/usr/local/bin/alvin-install` (mode `0755` forced in
|
||||
`profiledef.sh` `file_permissions`), and `/etc/motd` advertises it on the live medium.
|
||||
|
||||
- **Invoke**: as root from the live session. Hard requirements: `gum` and UEFI —
|
||||
`check_prereqs` exits if `/sys/firmware/efi` is missing. Log: `/var/log/alvin-install.log`
|
||||
(copied into the target at the end; `ERR` trap prints the failing line).
|
||||
- **Structure**: `main()` runs 16 `screen_*` TUI functions (collect answers: welcome, keymap,
|
||||
network, disk, luks, unl0kr, filesystem, username, password, hostname, git, timezone,
|
||||
locale, login manager, sshd, summary) then a fixed order of `install_*` steps
|
||||
(partition → LUKS → format/mount → pacstrap → fstab → repos → timezone → locale →
|
||||
hostname → network → sshd → user → git/SSH → end-4 dots → mkinitcpio → bootloader →
|
||||
TPM/unl0kr → login manager → autologin → cleanup). Screens and install steps are
|
||||
independent — edit one without
|
||||
touching the other. An `EXIT` trap unmounts `$TARGET` and closes `cryptroot` on any exit.
|
||||
- **Package list**: `install_pacstrap` builds the *installed* system's list from the medium's
|
||||
`/run/archiso/bootmnt/arch/pkglist.x86_64.txt` (i.e. `pacman -Q` of the live ISO), so
|
||||
`packages.x86_64` changes flow into installed systems. It drops `PKGS_EXCLUDE`
|
||||
(`archiso mkinitcpio-archiso mkinitcpio-nfs-utils syslinux memtest86+`) plus names not
|
||||
resolvable in the live repos, then always appends hardcoded essentials
|
||||
(`base linux linux-firmware linux-cachyos networkmanager grub efibootmgr sudo git …`).
|
||||
- **Linting**: CI's shellcheck step picks it up by its bash shebang. Locally:
|
||||
`shellcheck -x -s bash airootfs/usr/local/bin/alvin-install`
|
||||
- end-4 quirks (`--skip-alldeps`, `yes i`, end4-pC re-copy) are in [Gotchas](#gotchas) below.
|
||||
|
||||
## Verify a single change locally (mirrors CI)
|
||||
|
||||
```bash
|
||||
shellcheck -x -s bash <script>.sh # plus the alvin-install command above
|
||||
yamllint -d "{extends: relaxed, rules: {document-start: disable, line-length: disable, new-line-at-end-of-file: disable}}" <file>.yaml
|
||||
```
|
||||
|
||||
`packages.x86_64` syntax: one package per line, `[a-zA-Z0-9._@+\-]+` only, no trailing
|
||||
whitespace.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- The profile root is the **repo root** (not a `releng/` subdirectory). The CI build-iso workflow uses `mkarchiso -v -w work -o out/ releng` but this is a naming convention — the actual profile files live at the repo root.
|
||||
- `customize_airootfs.sh` clones git repos (oh-my-zsh, lazy.nvim, plugins) into `/etc/skel` during the ISO build. These require network access at build time but make the live ISO work offline.
|
||||
- The `[local-aur]` repo path in `pacman.conf` (`file:///home/alvin/alvinarch/localrepo`) is for local builds only. The CI does not use this path.
|
||||
- The profile root is the **repo root** (not a `releng/` subdirectory). CI builds with `mkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD"` (`.gitea/workflows/build-iso.yaml`), and the local build command above does the same — never pass a `releng` path.
|
||||
- `customize_airootfs.sh` clones git repos (oh-my-zsh, lazy.nvim, plugins, end-4 dots) into `/etc/skel` and `/opt` during the ISO build. These require network access at build time but make the live ISO work offline.
|
||||
- `customize_airootfs.sh` runs `./setup install-deps -f -s --skip-plasmaintg` as the throwaway user `ii-builder` (end-4's scripts refuse to run as root) and aborts the build if it fails. `--skip-plasmaintg` is deliberate: that optional Firefox widget pulls 410 packages / 526 MiB of KDE.
|
||||
- The `[local-aur]` repo path in `pacman.conf` (`file:///home/alvin/AlvinArch/localrepo`) is for local builds only. The CI does not use this path.
|
||||
- The **image** ships its own `[local-aur]` at `file:///opt/localrepo` (see `.live-pacman.conf`). It holds `localrepo/` (copied by CI/`update.sh`) plus everything `install-deps` built. pacstrap runs without `-P`, so the target keeps the stock `/etc/pacman.conf`; `alvin-install` strips `[local-aur]` from it anyway as a safety net, because `/opt/localrepo` exists only on the medium.
|
||||
- `alvin-install` runs end-4 with `--skip-alldeps` — dependencies must come from the ISO. It feeds `yes i` to end-4's failure prompt: inside the installer chroot the medium's `/run` is bind-mounted in, so end-4's `systemctl --machine=…@.host --user enable … --now` lines can never work (and `--now` would start services on the live medium). `install_end4_dots` re-applies the enable-only part (bluetooth, ydotool, i2c module) outside the chroot and reports every step end-4 had to skip. If the end-4 step reports "incomplete", check that `hyprctl`/`quickshell`/the venv were resolvable (harvest) before re-running anything.
|
||||
- `/etc/skel/.cache/uv` + `/etc/skel/.local/share/uv/python` are pre-warmed by the build (~460 MB: CPython 3.12 and the pinned wheel cache) so end-4's `uv venv`/`uv pip install` at install time needs no network.
|
||||
- **end4-pC** (`pctrade/end4-pC`) is the default quickshell config: cloned into `/etc/skel/.config/quickshell/end4-pC` at build time, selected by patching `hl.env("qsConfig", "ii")` → `"end4-pC"` in the **staged** `/opt/dots-hyprland/dots/.config/hypr/hyprland/variables.lua` (there is no `hypr/hyprland/` in this repo — `./setup install` copies it to the installed system). `alvin-install` re-copies end4-pC into the user's home *after* `./setup install`, because end-4's installer `rsync --delete`s all of `~/.config/quickshell`.
|
||||
- `airootfs/etc/skel/.config/illogical-impulse/presets/` carries the user's presets (`Laptop.json`, `Personal-Performance.json`, `Personal-Performance.zip`). They embed `/home/alvin/...` paths, so wallpapers/screenshots need fixing up after install.
|
||||
- End-4 metapackages are built from `localrepo/end4-pkgbuilds/` (quickshell-git, bibata-modern-classic-bin, microtex-git).
|
||||
- **quickshell pin / Qt 6.12**: end-4 pins quickshell to `41651d7` (Sep 25), which predates upstream fix `5d5d498` (Oct 3, *"use MOC includes instead of opaque pointer definitions — … Qt 6.12 compatibility"*). Arch shipped qt6 6.12.0 on Oct 8 and the old pin died with `Meta Types must be fully defined` in the moc build of quickshell-hyprland-ipc (failed CI run 137). `customize_airootfs.sh` therefore rewrites end-4's `_commit` to `11ca60be` after cloning — but **only** while it still matches `^_commit='41651d7`, so once end-4 bumps their own pin the guard goes inert and theirs wins. `localrepo/check-quickshell-pin.sh` (weekly `pin-check.yaml` job) fails CI if either pin goes stale or loses the fix; it warns instead of failing when end-4's known-stale pin is covered by the override.
|
||||
- **MicroTeX flags**: the image's `makepkg.conf` is CachyOS's (`-march=native -O3`, LTO). GCC 16.2 hit an internal compiler error (segfault) on MicroTeX's `gtkmm_main.cpp` with it (failed CI run 155; not reproducible locally), so `customize_airootfs.sh` patches end-4's `illogical-impulse-microtex-git` PKGBUILD to `options=(!lto)` + `-O2` (our `localrepo/end4-pkgbuilds/microtex-git` carries the same).
|
||||
- **CAP_SETFCAP on the runner**: without it pacstrap drops `security.capability` xattrs (newuidmap/newgidmap, snap-confine, dumpcap, kwin_wayland, ...) from the live ISO with only a warning. `build-iso.yaml` fails the job early if the runner lacks it — fix the runner, don't remove the check.
|
||||
- **yay SIGSEGV on exit**: yay 13 (libalpm via purego/`dyalpm`) can segfault in `alpm_release()` *after* a finished transaction (failed CI run at end-4's `illogical-impulse-kde` step), which aborted `install-deps`. `customize_airootfs.sh` puts a temporary `yay` shim first on the builder's `PATH`: a failed plain `yay -S <pkgs>` counts as success only if `pacman -T` confirms every target is installed (it logs `[yay-shim]`). Every other failure keeps yay's exit code.
|
||||
- mono's `cert-sync` runs before `ca-certificates.crt` exists during pacstrap, so `customize_airootfs.sh` (live) and `install_pacstrap` (target) re-run it.
|
||||
- `/etc/skel/.zshrc` is owned by `grml-zsh-config`, so ours is staged as `airootfs/root/.zshrc-skel` and installed by `customize_airootfs.sh` after pacstrap — never add files under `airootfs/` at paths a package owns, or pacstrap aborts with "exists in filesystem".
|
||||
- `packages.x86_64` is sorted alphabetically. Keep it sorted when adding/removing entries.
|
||||
- `airootfs/opt/localrepo/` is a build artifact (gitignored) — never commit it.
|
||||
- **Build stamp flow**: `profiledef.sh` (sourced by mkarchiso) stages the gitignored `airootfs/root/.iso-build-stamp` when a `.git` dir exists **or** `CI`/`GITEA_ACTIONS` is set — line reads `AlvinArch build <date> | commit <date> | <4-char hash>` (falls back to `commit unknown` if `git -c safe.directory` can't read the repo; without `.git`/CI no file is staged). `customize_airootfs.sh` keeps it as `/etc/alvin-build-stamp` on the live medium **and appends it to the live `/etc/motd`**, so the booted ISO identifies its build. The installed system's motd is never touched by us — `alvin-install` has no motd step (an earlier `install_motd` that copied the banner + stamp into the target was removed on purpose).
|
||||
|
||||
@@ -21,7 +21,6 @@ AlvinArch/
|
||||
├── localrepo/
|
||||
│ ├── build-end4-deps.sh # non-interactive AUR build (all deps)
|
||||
│ ├── end4-pkgbuilds/ # microtex-git, quickshell-git, bibata
|
||||
│ ├── illogical-impulse-quickshell-git-*.pkg.tar.zst
|
||||
│ ├── illogical-impulse-bibata-modern-classic-bin-*.pkg.tar.zst
|
||||
│ └── update.sh # existing, regenerates repo db
|
||||
└── packages.x86_64 # 420 packages (65+ end-4 deps added)
|
||||
@@ -37,9 +36,10 @@ AlvinArch/
|
||||
6. [System Services & Networking](#system-services--networking)
|
||||
7. [Local Repository (localrepo/)](#local-repository-localrepo)
|
||||
8. [Boot Configuration](#boot-configuration)
|
||||
9. [Build & Test](#build--test)
|
||||
10. [Planned Additions](#planned-additions)
|
||||
11. [Troubleshooting](#troubleshooting)
|
||||
9. [Automated Installer (alvin-install)](#automated-installer-alvin-install)
|
||||
10. [Build & Test](#build--test)
|
||||
11. [Planned Additions](#planned-additions)
|
||||
12. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
@@ -50,6 +50,7 @@ AlvinArch/
|
||||
### Key Features
|
||||
|
||||
- **Hyprland + end-4** — Wayland compositor with illogical-impulse dotfiles (quickshell, bibata cursors, microtex)
|
||||
- **Automated installer (`alvin-install`)** — gum TUI: LUKS with systemd initramfs or experimental touchscreen unl0kr, optional TPM2 auto-unlock, GitHub SSH key import, login manager choice (none/greetd/sddm/gdm), optional sshd (on by default), autologin; the build stamp identifies the live ISO via its `/etc/motd`
|
||||
- **LazyVim** — Pre-configured Neovim with lazy.nvim and curated plugins
|
||||
- **Fish + Zsh + Starship** — Dual shell setup with oh-my-zsh and a cross-shell prompt
|
||||
- **Local AUR repo** — Pre-built packages (end-4 deps, wallust, grub-customizer, etc.) that aren't in official repos
|
||||
@@ -137,7 +138,7 @@ AlvinArch/
|
||||
│ │ └── mkinitcpio.conf.d/ # archiso initramfs config
|
||||
│ ├── usr/
|
||||
│ │ ├── lib/os-release # usr-merge os-release
|
||||
│ │ ├── local/bin/ # choose-mirror, Installation_guide, livecd-sound
|
||||
│ │ ├── local/bin/ # alvin-install, choose-mirror, Installation_guide, livecd-sound
|
||||
│ │ ├── local/share/livecd-sound/ # ALSA config (asound.conf.in)
|
||||
│ │ └── share/pacman/keyrings/ # CachyOS GPG keyring + trust data
|
||||
│ └── root/
|
||||
@@ -151,7 +152,7 @@ AlvinArch/
|
||||
│ ├── update.sh # Regenerates repo DB + syncs names to packages.x86_64
|
||||
│ ├── end4-pkgbuilds/ # PKGBUILDs: quickshell-git, microtex-git, bibata-modern-classic-bin
|
||||
│ ├── *.pkg.tar.zst # Pre-built packages (illogical-impulse-*, wallust, grub-customizer,
|
||||
│ │ # snapd, upscayl, meshy-git, pacserve, etc.)
|
||||
│ │ # snapd, upscayl, eternalterminal, etc.)
|
||||
│ └── local-aur.db{,.tar.gz} # Repo database files
|
||||
├── efiboot/ # UEFI boot (systemd-boot)
|
||||
│ └── loader/
|
||||
@@ -229,7 +230,7 @@ Cross-shell prompt config for [Starship](https://starship.rs/). Works with both
|
||||
|
||||
### **.zshrc / .fzf.zsh**
|
||||
|
||||
Zsh configuration adapted for oh-my-zsh (installed by `customize_airootfs.sh`) and fzf shell integration.
|
||||
Zsh configuration adapted for oh-my-zsh (installed by `customize_airootfs.sh`) and fzf shell integration. fish is the default login shell; zsh is there if you `chsh` to it. The `.zshrc` source lives at `airootfs/root/.zshrc-skel`, because grml-zsh-config owns `/etc/skel/.zshrc` and shipping it under `airootfs/etc/skel/` would abort the build. zsh-autocomplete is cloned but disabled; see [Troubleshooting](#zsh-autocomplete-partial-load-error).
|
||||
|
||||
---
|
||||
|
||||
@@ -284,7 +285,7 @@ Custom PKGBUILDs for end-4 metapackages:
|
||||
|
||||
### Pre-built Packages
|
||||
|
||||
Packages like `illogical-impulse-quickshell-git`, `illogical-impulse-bibata-modern-classic-bin`, `wallust`, `grub-customizer`, `snapd`, `upscayl-bin`, `meshy-git`, `xwaylandvideobridge`, `pacserve`, etc.
|
||||
Packages like `illogical-impulse-bibata-modern-classic-bin`, `wallust`, `grub-customizer`, `snapd`, `upscayl-bin`, `xwaylandvideobridge`, `eternalterminal`, etc.
|
||||
|
||||
---
|
||||
|
||||
@@ -304,6 +305,66 @@ Legacy BIOS boot with custom splash screen. Includes PXE network boot config.
|
||||
|
||||
---
|
||||
|
||||
## Automated Installer (alvin-install)
|
||||
|
||||
The ISO ships an interactive installer at `/usr/local/bin/alvin-install` (source:
|
||||
`airootfs/usr/local/bin/alvin-install`, ~1,880 lines of bash; the live `/etc/motd`
|
||||
advertises it). It partitions, encrypts, installs, and fully configures a system —
|
||||
including the end-4 dotfiles — without network access to this repo.
|
||||
|
||||
### Running it
|
||||
|
||||
```bash
|
||||
alvin-install # as root on the live medium (root autologins on tty1)
|
||||
```
|
||||
|
||||
Requirements: **UEFI boot** (it exits if `/sys/firmware/efi` is missing — BIOS/CSM
|
||||
is not supported by the installer) and `gum`. A network connection is required and
|
||||
verified by pinging `archlinux.org` before any disk is touched; the network screen
|
||||
can open `nmtui` for WiFi. Every step is logged to `/var/log/alvin-install.log`,
|
||||
which is copied into the installed system.
|
||||
|
||||
### What it asks (gum TUI screens)
|
||||
|
||||
Keymap → network → target disk (the live USB is hidden from the list) → LUKS
|
||||
encryption (optional; **the LUKS passphrase is the same as your login password**)
|
||||
→ experimental unl0kr touchscreen unlock → filesystem (`ext4`/`btrfs`) →
|
||||
username/password/hostname → git identity + GitHub username (both optional) →
|
||||
timezone/locale → login manager → SSH server (enabled by default) → summary.
|
||||
|
||||
### What it does
|
||||
|
||||
1. GPT partitioning (1 GiB ESP) on the chosen disk, optional LUKS2 on the rest,
|
||||
format + mount at `/mnt`
|
||||
2. `pacstrap` with the package list derived from the medium's
|
||||
`pkglist.x86_64.txt` (`pacman -Q` of the live ISO) — `archiso`, `syslinux`,
|
||||
`memtest86+` etc. are dropped, hardcoded essentials (kernel, GRUB,
|
||||
NetworkManager, cryptsetup, microcode) always appended
|
||||
3. User account, sudo, git config, GitHub SSH keys (`ssh-import-id`, falling
|
||||
back to `github.com/<user>.keys`)
|
||||
4. end-4 dotfiles from the staged `/opt/dots-hyprland` via
|
||||
`./setup install -f -s --skip-alldeps`
|
||||
5. Initramfs in one of two modes: **Mode A** — systemd initramfs + `sd-encrypt`
|
||||
(default); **Mode B** — busybox hooks + unl0kr touchscreen unlock
|
||||
(experimental, mutually exclusive with TPM2; falls back to Mode A on failure)
|
||||
6. Optional **TPM2 auto-unlock** (`systemd-cryptenroll`, PCR 0+7; passphrase
|
||||
slot kept as fallback; warns if Secure Boot is off)
|
||||
7. GRUB (UEFI, boot entry `ALVIN`), login manager — `none` (plain tty, Hyprland
|
||||
autostarts), `greetd`+tuigreet, `sddm`, or `gdm` — falls back to `none` only
|
||||
if the **package** can't be resolved/installed; a failed `systemctl enable`
|
||||
keeps `getty@tty1` as the login path (the summary still reports the chosen
|
||||
manager). Optional autologin (forced off when TPM2 is enrolled), and sshd
|
||||
enabled at boot when selected (default: on) — Arch's stock config allows
|
||||
**password login with the same password as LUKS**, so only accept the default
|
||||
on networks you trust (or harden `sshd_config` afterwards). The final banner
|
||||
reports whether the enable actually succeeded, not just what was selected
|
||||
|
||||
On failure an `EXIT` trap unmounts `/mnt` and closes the `cryptroot` mapping, so
|
||||
re-running is safe. If the end-4 step reports "incomplete", see the repository's
|
||||
`AGENTS.md` Gotchas for the chroot/systemd limitations that cause it.
|
||||
|
||||
---
|
||||
|
||||
## Build & Test
|
||||
|
||||
### Building the ISO
|
||||
@@ -376,6 +437,56 @@ The following dotfiles are not yet included but are planned for future versions:
|
||||
- `customize_airootfs.sh` clones repos at build time — requires network access during `mkarchiso`
|
||||
- Check build logs for git clone failures
|
||||
|
||||
### zsh-autocomplete partial-load error
|
||||
|
||||
Symptom:
|
||||
|
||||
```
|
||||
_autocomplete__history_lines: function not defined
|
||||
_autocomplete__unambiguous: function not defined
|
||||
```
|
||||
|
||||
Cause: zsh-autocomplete was loaded after `compinit` (e.g. via oh-my-zsh
|
||||
`plugins=(...)`), a stale completion dump, or a conflict with
|
||||
zsh-autosuggestions / zsh-history-substring-search. On this ISO `compinit`
|
||||
always runs first: grml-zsh-config's global `/etc/zsh/zshrc` calls it
|
||||
unconditionally before `~/.zshrc` is read.
|
||||
|
||||
The shipped `~/.zshrc` leaves zsh-autocomplete out of `plugins=(...)`. The
|
||||
plugin stays cloned at `~/.oh-my-zsh/custom/plugins/zsh-autocomplete/`
|
||||
either way, just disabled by default.
|
||||
|
||||
**Fix: drop autocomplete (recommended)**
|
||||
|
||||
```bash
|
||||
# 1. Edit ~/.zshrc, remove this line from plugins=(...):
|
||||
# zsh-autocomplete
|
||||
|
||||
# 2. Clear stale dumps (bash avoids zsh's nomatch abort on the glob):
|
||||
bash -c 'rm -f ~/.zcompdump* ~/.cache/zsh/zcompdump*'
|
||||
|
||||
# 3. Restart the shell and verify:
|
||||
exec zsh
|
||||
typeset -f _autocomplete__history_lines >/dev/null && echo "still loaded" || echo "clean"
|
||||
```
|
||||
|
||||
**Fix: keep autocomplete instead**
|
||||
|
||||
```bash
|
||||
# 1. Remove zsh-autocomplete, zsh-autosuggestions and
|
||||
# zsh-history-substring-search from plugins=(...) in ~/.zshrc.
|
||||
|
||||
# 2. Stop the global compinit. `skip_global_compinit=1` in ~/.zshenv does
|
||||
# NOT work here (that is a Debian/Ubuntu /etc/zsh/zshrc convention; grml
|
||||
# ignores it), so remove the package that ships the global zshrc:
|
||||
sudo pacman -R grml-zsh-config
|
||||
|
||||
# 3. At the very top of ~/.zshrc, before oh-my-zsh is sourced:
|
||||
source ~/.oh-my-zsh/custom/plugins/zsh-autocomplete/zsh-autocomplete.plugin.zsh
|
||||
|
||||
# 4. Clear dumps as above, then: exec zsh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# mkinitcpio preset file for the 'linux-cachyos' package on archiso
|
||||
# (the kernel the boot entries load). Not owned by any package: mkinitcpio's
|
||||
# install hook only generates a preset when none exists, so this one wins and
|
||||
# the image gets the archiso initramfs alone - no unused fallback image.
|
||||
|
||||
PRESETS=('archiso')
|
||||
|
||||
ALL_kver='/boot/vmlinuz-linux-cachyos'
|
||||
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
|
||||
|
||||
archiso_image="/boot/initramfs-linux-cachyos.img"
|
||||
@@ -1,8 +0,0 @@
|
||||
# mkinitcpio preset file for the 'linux' package on archiso
|
||||
|
||||
PRESETS=('archiso')
|
||||
|
||||
ALL_kver='/boot/vmlinuz-linux'
|
||||
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
|
||||
|
||||
archiso_image="/boot/initramfs-linux.img"
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
To install [38;2;23;147;209mArch Linux[0m follow the installation guide:
|
||||
To install [38;2;23;147;209mArch Linux[0m, follow the installation guide,
|
||||
or run [35malvin-install[0m for an automated setup.
|
||||
https://wiki.archlinux.org/title/Installation_guide
|
||||
|
||||
For Wi-Fi, run [35mnmtui[0m to configure a wireless network.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[Trigger]
|
||||
Type = Package
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Target = *
|
||||
|
||||
[Action]
|
||||
Description = Ensuring /dev/stdin exists...
|
||||
When = PreTransaction
|
||||
Exec = /usr/bin/sh -c "[ -e /dev/stdin ] || ln -sf /proc/self/fd/0 /dev/stdin; [ -e /dev/stdout ] || ln -sf /proc/self/fd/1 /dev/stdout; [ -e /dev/stderr ] || ln -sf /proc/self/fd/2 /dev/stderr"
|
||||
@@ -23,7 +23,7 @@ if status is-interactive
|
||||
alias celar "printf '\033[2J\033[3J\033[1;1H'"
|
||||
alias claer "printf '\033[2J\033[3J\033[1;1H'"
|
||||
alias pamcan pacman
|
||||
alias q 'qs -c ii'
|
||||
alias q 'qs -c end4-pC'
|
||||
if test "$TERM" != "linux"
|
||||
alias ls 'eza --icons'
|
||||
end
|
||||
|
||||
@@ -0,0 +1,677 @@
|
||||
{
|
||||
"appearance": {
|
||||
"extraBackgroundTint": true,
|
||||
"fakeScreenRounding": 0,
|
||||
"fonts": {
|
||||
"expressive": "Space Grotesk",
|
||||
"iconNerd": "JetBrains Mono NF",
|
||||
"main": "Google Sans Flex",
|
||||
"monospace": "JetBrains Mono NF",
|
||||
"numbers": "Google Sans Flex",
|
||||
"reading": "Readex Pro",
|
||||
"title": "Google Sans Flex"
|
||||
},
|
||||
"palette": {
|
||||
"accentColor": "",
|
||||
"type": "scheme-tonal-spot"
|
||||
},
|
||||
"transparency": {
|
||||
"automatic": true,
|
||||
"backgroundTransparency": 0.11,
|
||||
"contentTransparency": 0.57,
|
||||
"enable": false
|
||||
},
|
||||
"wallpaperTheming": {
|
||||
"enableAppsAndShell": true,
|
||||
"enableQtApps": true,
|
||||
"enableTerminal": true,
|
||||
"terminalGenerationProps": {
|
||||
"forceDarkMode": false,
|
||||
"harmonizeThreshold": 100,
|
||||
"harmony": 0.5,
|
||||
"termFgBoost": 0.35
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"bluetooth": "kcmshell6 kcm_bluetooth",
|
||||
"changePassword": "kitty -1 --hold=yes fish -i -c 'passwd'",
|
||||
"manageUser": "kcmshell6 kcm_users",
|
||||
"network": "kcmshell6 kcm_networkmanagement",
|
||||
"networkEthernet": "kcmshell6 kcm_networkmanagement",
|
||||
"taskManager": "plasma-systemmonitor --page-name Processes",
|
||||
"terminal": "kitty -1",
|
||||
"update": "kitty -1 --hold=yes fish -i -c 'pkexec pacman -Syu'",
|
||||
"volumeMixer": "~/.config/hypr/hyprland/scripts/launch_first_available.sh \"pavucontrol-qt\" \"pavucontrol\""
|
||||
},
|
||||
"background": {
|
||||
"blurRadius": 32,
|
||||
"centeredWallpaper": false,
|
||||
"centeredWallpaperColor": "primaryContainer",
|
||||
"centeredWallpaperOnlyWhenLocked": false,
|
||||
"centeredWallpaperShape": "Cookie7Sided",
|
||||
"centeredWallpaperSize": 400,
|
||||
"enableWallpaperPreview": false,
|
||||
"hideWhenFullscreen": true,
|
||||
"lockWall": "",
|
||||
"parallax": {
|
||||
"autoVertical": false,
|
||||
"enableSidebar": true,
|
||||
"enableWorkspace": true,
|
||||
"vertical": false,
|
||||
"widgetsFactor": 1.2,
|
||||
"workspaceZoom": 1.07
|
||||
},
|
||||
"screenList": [],
|
||||
"showBlur": true,
|
||||
"showGrid": true,
|
||||
"showSnapLines": true,
|
||||
"splitRatio": "25",
|
||||
"splitSide": "right",
|
||||
"thumbnailPath": "",
|
||||
"wallpaperAnimation": "random",
|
||||
"wallpaperPath": "/home/alvin/Pictures/Wallpapers/Dynamic-Wallpapers/Dark/japan-street-Dark.png",
|
||||
"widgets": {
|
||||
"blurRadius": 32,
|
||||
"blurWidgets": false,
|
||||
"calendar": {
|
||||
"enable": true,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "2x2",
|
||||
"x": 1596,
|
||||
"y": 744,
|
||||
"z": 4
|
||||
},
|
||||
"clock": {
|
||||
"color": "",
|
||||
"cookie": {
|
||||
"aiStyling": false,
|
||||
"constantlyRotate": false,
|
||||
"dateInClock": true,
|
||||
"dateStyle": "bubble",
|
||||
"dialNumberStyle": "full",
|
||||
"hourHandStyle": "hollow",
|
||||
"hourMarks": false,
|
||||
"minuteHandStyle": "medium",
|
||||
"secondHandStyle": "line",
|
||||
"sides": 12,
|
||||
"timeIndicators": true,
|
||||
"useSineCookie": false
|
||||
},
|
||||
"digital": {
|
||||
"adaptiveAlignment": true,
|
||||
"animateChange": true,
|
||||
"font": {
|
||||
"family": "Google Sans Flex",
|
||||
"roundness": 1.1290989771359807,
|
||||
"size": 129.49608904933814,
|
||||
"weight": 595.725165314564,
|
||||
"width": 99.44532190132371
|
||||
},
|
||||
"showDate": true,
|
||||
"vertical": false
|
||||
},
|
||||
"enable": true,
|
||||
"pixel": {
|
||||
"orientation": "vertical"
|
||||
},
|
||||
"placementStrategy": "leastBusy",
|
||||
"quote": {
|
||||
"enable": true,
|
||||
"followClock": false,
|
||||
"text": "Kawaī neko o zenbu tabe chaimasu."
|
||||
},
|
||||
"showOnlyWhenLocked": false,
|
||||
"style": "digital",
|
||||
"styleLocked": "cookie",
|
||||
"x": 1423,
|
||||
"y": 383,
|
||||
"z": 3
|
||||
},
|
||||
"customImage": {
|
||||
"enable": true,
|
||||
"path": "",
|
||||
"placementStrategy": "free",
|
||||
"shape": "Cookie4Sided",
|
||||
"size": 200,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"customText": {
|
||||
"alignment": "center",
|
||||
"color": "",
|
||||
"content": "Hello world",
|
||||
"enable": true,
|
||||
"fontFamily": "Caveat",
|
||||
"fontSize": 72,
|
||||
"placementStrategy": "free",
|
||||
"shadow": true,
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"images": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"media": {
|
||||
"backgroundShape": "Cookie4Sided",
|
||||
"enable": true,
|
||||
"placementStrategy": "free",
|
||||
"showControls": true,
|
||||
"showLyrics": false,
|
||||
"showTitles": true,
|
||||
"sizeMode": "1x3",
|
||||
"x": 744,
|
||||
"y": 876,
|
||||
"z": 1
|
||||
},
|
||||
"notes": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"resources": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"vertical": false,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"shadow": true,
|
||||
"sticker": {
|
||||
"enable": false,
|
||||
"items": [],
|
||||
"outlineColor": "#ffffff",
|
||||
"outlineWidth": 8,
|
||||
"path": "",
|
||||
"placementStrategy": "free",
|
||||
"rotation": 0,
|
||||
"size": 200,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"timers": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"vertical": false,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"todo": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"userCard": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "1x2",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"visualizer": {
|
||||
"colorSource": "theme",
|
||||
"enable": true,
|
||||
"height": 343,
|
||||
"placementStrategy": "free",
|
||||
"ringSize": 380,
|
||||
"sensitivity": 1.65,
|
||||
"style": "mirror",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": -1000
|
||||
},
|
||||
"weather": {
|
||||
"enable": false,
|
||||
"expanded": false,
|
||||
"placementStrategy": "mostBusy",
|
||||
"sizeMode": "1x3",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"worldClock": {
|
||||
"clockCount": 4,
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "2x2",
|
||||
"timezones": [
|
||||
"Australia/Sydney",
|
||||
"Asia/Tokyo",
|
||||
"Europe/London",
|
||||
"America/New_York"
|
||||
],
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
"widgetsLocked": false
|
||||
},
|
||||
"bar": {
|
||||
"autoHide": {
|
||||
"enable": false,
|
||||
"hoverRegionWidth": 2,
|
||||
"pushWindows": false,
|
||||
"showWhenPressingSuper": {
|
||||
"delay": 140,
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"borderless": "transparent",
|
||||
"bottom": false,
|
||||
"centerOnlyReserveFrame": false,
|
||||
"cornerStyle": 3,
|
||||
"divider": {
|
||||
"spacing": 20,
|
||||
"style": "rect"
|
||||
},
|
||||
"dynamicIsland": {
|
||||
"leftWidget": "clockWidget",
|
||||
"rightWidget": "systemIcons",
|
||||
"showMediaControls": false,
|
||||
"visualizerStyle": "wave"
|
||||
},
|
||||
"floatStyleShadow": true,
|
||||
"followFrameColor": false,
|
||||
"frameColor": "secondaryContainer",
|
||||
"frameThickness": 4,
|
||||
"groupColor": "layer1",
|
||||
"indicators": {
|
||||
"notifications": {
|
||||
"showUnreadCount": true
|
||||
}
|
||||
},
|
||||
"layouts": {
|
||||
"leftLayout": [
|
||||
"leftSidebarButton",
|
||||
"activeWindow",
|
||||
"resources"
|
||||
],
|
||||
"middleLayout": [
|
||||
"media",
|
||||
"clockWidget",
|
||||
"workspaces",
|
||||
"utilButtons",
|
||||
"weatherBar",
|
||||
"batteryIndicator"
|
||||
],
|
||||
"rightLayout": [
|
||||
"sysTray",
|
||||
"updatesCount",
|
||||
"bluetooth",
|
||||
"systemIcons"
|
||||
]
|
||||
},
|
||||
"media": {
|
||||
"alwaysVisible": false,
|
||||
"maxWidth": 280,
|
||||
"minWidth": 100,
|
||||
"onlyTitle": false,
|
||||
"preferredPlayer": "",
|
||||
"showLyrics": false
|
||||
},
|
||||
"resources": {
|
||||
"alwaysShowCpu": true,
|
||||
"alwaysShowCpuTemp": true,
|
||||
"alwaysShowDisk": false,
|
||||
"alwaysShowRam": true,
|
||||
"alwaysShowSwap": false,
|
||||
"cpuWarningThreshold": 90,
|
||||
"memoryWarningThreshold": 95,
|
||||
"showValue": true,
|
||||
"style": "outline",
|
||||
"swapWarningThreshold": 85
|
||||
},
|
||||
"screenList": [],
|
||||
"showBackground": true,
|
||||
"showFrame": false,
|
||||
"tooltips": {
|
||||
"clickToShow": false,
|
||||
"enable": true
|
||||
},
|
||||
"topLeftIcon": "spark",
|
||||
"utilButtons": {
|
||||
"isRecording": false,
|
||||
"showColorPicker": false,
|
||||
"showDarkModeToggle": false,
|
||||
"showKeyboardToggle": true,
|
||||
"showMicToggle": true,
|
||||
"showPerformanceProfileToggle": true,
|
||||
"showScreenRecord": false,
|
||||
"showScreenSnip": false,
|
||||
"showWallpaperToggle": false
|
||||
},
|
||||
"verbose": true,
|
||||
"vertical": false,
|
||||
"workspaces": {
|
||||
"alwaysShowNumbers": true,
|
||||
"indicatorStyle": "dot",
|
||||
"monochromeIcons": true,
|
||||
"numberMap": [],
|
||||
"showAppIcons": true,
|
||||
"showNumberDelay": 200,
|
||||
"shown": 10,
|
||||
"useNerdFont": false
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"locale": "en-GB"
|
||||
},
|
||||
"conflictKiller": {
|
||||
"autoKillNotificationDaemons": true,
|
||||
"autoKillTrays": true
|
||||
},
|
||||
"crosshair": {
|
||||
"code": "0;P;d;1;0l;10;0o;2;1b;0"
|
||||
},
|
||||
"custom": {
|
||||
"colorizeIcon": true,
|
||||
"distroIcon": "google-gemini-symbolic"
|
||||
},
|
||||
"dock": {
|
||||
"enable": true,
|
||||
"height": 60,
|
||||
"hoverRegionHeight": 2,
|
||||
"hoverToReveal": true,
|
||||
"ignoredAppRegexes": [],
|
||||
"monochromeIcons": false,
|
||||
"pinnedApps": [
|
||||
"org.kde.dolphin",
|
||||
"zen"
|
||||
],
|
||||
"pinnedOnStartup": false,
|
||||
"showAppsButton": true,
|
||||
"showBackground": true,
|
||||
"showMedia": true,
|
||||
"showPinButton": true
|
||||
},
|
||||
"hacks": {
|
||||
"arbitraryRaceConditionDelay": 20
|
||||
},
|
||||
"hyprland": {
|
||||
"animations": {
|
||||
"animation": "normal",
|
||||
"enable": true
|
||||
},
|
||||
"decoration": {
|
||||
"activeOpacity": 1,
|
||||
"blur": {
|
||||
"enabled": false,
|
||||
"passes": 3,
|
||||
"size": 1
|
||||
},
|
||||
"inactiveOpacity": 0.95,
|
||||
"rounding": 22,
|
||||
"shadow": {
|
||||
"enabled": true,
|
||||
"range": 4
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
"borderColor": {
|
||||
"activeOpacity": 0.47,
|
||||
"activeRole": "layer0Border",
|
||||
"enable": false,
|
||||
"inactiveOpacity": 0.2,
|
||||
"inactiveRole": "layer0Border"
|
||||
},
|
||||
"borderSize": 1,
|
||||
"gapsIn": 2,
|
||||
"gapsOut": 5
|
||||
},
|
||||
"idle": {
|
||||
"lock": 300,
|
||||
"screenOff": 600,
|
||||
"suspend": 10800
|
||||
}
|
||||
},
|
||||
"interactions": {
|
||||
"deadPixelWorkaround": {
|
||||
"enable": false
|
||||
},
|
||||
"scrolling": {
|
||||
"fasterTouchpadScroll": false,
|
||||
"mouseScrollDeltaThreshold": 120,
|
||||
"mouseScrollFactor": 120,
|
||||
"touchpadScrollFactor": 450
|
||||
}
|
||||
},
|
||||
"launcher": {
|
||||
"pinnedApps": [
|
||||
"org.kde.dolphin",
|
||||
"kitty",
|
||||
"cmake-gui"
|
||||
]
|
||||
},
|
||||
"light": {
|
||||
"antiFlashbang": {
|
||||
"enable": false
|
||||
},
|
||||
"night": {
|
||||
"automatic": false,
|
||||
"colorTemperature": 5559,
|
||||
"from": "19:00",
|
||||
"to": "06:30"
|
||||
}
|
||||
},
|
||||
"lock": {
|
||||
"blur": {
|
||||
"enable": false,
|
||||
"extraZoom": 1.1,
|
||||
"radius": 100,
|
||||
"size": 20
|
||||
},
|
||||
"centerClock": true,
|
||||
"launchOnStartup": false,
|
||||
"materialShapeChars": true,
|
||||
"security": {
|
||||
"requirePasswordToPower": true,
|
||||
"unlockKeyring": true
|
||||
},
|
||||
"showLockedText": true,
|
||||
"showMedia": true,
|
||||
"showToolbars": true,
|
||||
"showWidgets": false,
|
||||
"useHyprlock": false
|
||||
},
|
||||
"media": {
|
||||
"filterDuplicatePlayers": true
|
||||
},
|
||||
"notifications": {
|
||||
"position": "top_right",
|
||||
"timeout": 7000
|
||||
},
|
||||
"osd": {
|
||||
"timeout": 3000
|
||||
},
|
||||
"osk": {
|
||||
"layout": "qwerty_full",
|
||||
"pinnedOnStartup": false
|
||||
},
|
||||
"overlay": {
|
||||
"clickthroughOpacity": 0.8,
|
||||
"darkenScreen": false,
|
||||
"floatingImage": {
|
||||
"imageSource": "https://media.tenor.com/H5U5bJzj3oAAAAAi/kukuru.gif",
|
||||
"scale": 0.5
|
||||
},
|
||||
"openingZoomAnimation": true
|
||||
},
|
||||
"overview": {
|
||||
"centerIcons": true,
|
||||
"columns": 5,
|
||||
"enable": true,
|
||||
"orderBottomUp": false,
|
||||
"orderRightLeft": false,
|
||||
"rows": 2,
|
||||
"scale": 0.18,
|
||||
"style": "default"
|
||||
},
|
||||
"panelFamily": "ii",
|
||||
"policies": {
|
||||
"ai": 1,
|
||||
"weeb": 1
|
||||
},
|
||||
"profile": {
|
||||
"avatarPath": "/home/alvin/Sync_Documents/",
|
||||
"avatarPicture": "/home/alvin/Sync_Documents/Alvin logo original.png",
|
||||
"descriptionText": "::uptime::",
|
||||
"displayName": "",
|
||||
"onlinePresets": true
|
||||
},
|
||||
"regionSelector": {
|
||||
"annotation": {
|
||||
"useSatty": false
|
||||
},
|
||||
"circle": {
|
||||
"padding": 10,
|
||||
"strokeWidth": 6
|
||||
},
|
||||
"rect": {
|
||||
"showAimLines": true
|
||||
},
|
||||
"targetRegions": {
|
||||
"content": true,
|
||||
"contentRegionOpacity": 0.8,
|
||||
"layers": true,
|
||||
"opacity": 0.3,
|
||||
"selectionPadding": 5,
|
||||
"showLabel": false,
|
||||
"windows": true
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"historyLength": 60,
|
||||
"updateInterval": 3100
|
||||
},
|
||||
"settings": {
|
||||
"borderColor": "layer0Border",
|
||||
"borderSize": 1,
|
||||
"collapsedSections": [],
|
||||
"style": "default"
|
||||
},
|
||||
"sidebar": {
|
||||
"ai": {
|
||||
"textFadeIn": false
|
||||
},
|
||||
"banner": true,
|
||||
"bannerImage": "",
|
||||
"booru": {
|
||||
"allowNsfw": true,
|
||||
"defaultProvider": "yandere",
|
||||
"limit": 20,
|
||||
"zerochan": {
|
||||
"username": "theodorer61"
|
||||
}
|
||||
},
|
||||
"bottomGroup": false,
|
||||
"cornerOpen": {
|
||||
"bottom": true,
|
||||
"bottomLeftAction": "sidebarLeftOpen",
|
||||
"bottomRightAction": "sidebarRightOpen",
|
||||
"clickless": false,
|
||||
"clicklessCornerEnd": true,
|
||||
"clicklessCornerVerticalOffset": 1,
|
||||
"cornerRegionHeight": 5,
|
||||
"cornerRegionWidth": 250,
|
||||
"enable": true,
|
||||
"valueScroll": true,
|
||||
"visualize": false
|
||||
},
|
||||
"keepRightSidebarLoaded": false,
|
||||
"media": {
|
||||
"artColors": false,
|
||||
"artShape": "Oval",
|
||||
"blurredBackground": false,
|
||||
"enable": true,
|
||||
"shapeArt": false,
|
||||
"showLyrics": true
|
||||
},
|
||||
"mediaPlayer": false,
|
||||
"quickSliders": {
|
||||
"enable": true,
|
||||
"showBrightness": true,
|
||||
"showMic": true,
|
||||
"showVolume": true
|
||||
},
|
||||
"quickToggles": {
|
||||
"android": {
|
||||
"columns": 6,
|
||||
"toggles": [
|
||||
{
|
||||
"size": 2,
|
||||
"type": "network"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "bluetooth"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "audio"
|
||||
},
|
||||
{
|
||||
"size": 1,
|
||||
"type": "idleInhibitor"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "mic"
|
||||
},
|
||||
{
|
||||
"size": 1,
|
||||
"type": "nightLight"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "powerProfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
"style": "android"
|
||||
},
|
||||
"translator": {
|
||||
"delay": 300,
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"tray": {
|
||||
"filterPassive": true,
|
||||
"invertPinnedItems": true,
|
||||
"monochromeIcons": false,
|
||||
"pinnedItems": [
|
||||
"Fcitx"
|
||||
],
|
||||
"showItemId": false
|
||||
},
|
||||
"wallpaperSelector": {
|
||||
"changeInterval": 0,
|
||||
"closeAfterSelection": true,
|
||||
"columns": 4,
|
||||
"liveWallpapersPath": "",
|
||||
"showBlurBackground": false,
|
||||
"showHomePath": true,
|
||||
"showSearchbar": true,
|
||||
"sortMode": "time",
|
||||
"useSystemFileDialog": false,
|
||||
"userPath": "/home/alvin/Pictures/Wallpapers/"
|
||||
},
|
||||
"windows": {
|
||||
"centerTitle": true,
|
||||
"showTitlebar": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,676 @@
|
||||
{
|
||||
"appearance": {
|
||||
"extraBackgroundTint": true,
|
||||
"fakeScreenRounding": 0,
|
||||
"fonts": {
|
||||
"expressive": "Space Grotesk",
|
||||
"iconNerd": "JetBrains Mono NF",
|
||||
"main": "Google Sans Flex",
|
||||
"monospace": "JetBrains Mono NF",
|
||||
"numbers": "Google Sans Flex",
|
||||
"reading": "Readex Pro",
|
||||
"title": "Google Sans Flex"
|
||||
},
|
||||
"palette": {
|
||||
"accentColor": "",
|
||||
"type": "scheme-tonal-spot"
|
||||
},
|
||||
"transparency": {
|
||||
"automatic": true,
|
||||
"backgroundTransparency": 0.11,
|
||||
"contentTransparency": 0.57,
|
||||
"enable": false
|
||||
},
|
||||
"wallpaperTheming": {
|
||||
"enableAppsAndShell": true,
|
||||
"enableQtApps": true,
|
||||
"enableTerminal": true,
|
||||
"terminalGenerationProps": {
|
||||
"forceDarkMode": false,
|
||||
"harmonizeThreshold": 100,
|
||||
"harmony": 0.5,
|
||||
"termFgBoost": 0.35
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"bluetooth": "kcmshell6 kcm_bluetooth",
|
||||
"changePassword": "kitty -1 --hold=yes fish -i -c 'passwd'",
|
||||
"manageUser": "kcmshell6 kcm_users",
|
||||
"network": "kcmshell6 kcm_networkmanagement",
|
||||
"networkEthernet": "kcmshell6 kcm_networkmanagement",
|
||||
"taskManager": "plasma-systemmonitor --page-name Processes",
|
||||
"terminal": "kitty -1",
|
||||
"update": "kitty -1 --hold=yes fish -i -c 'pkexec pacman -Syu'",
|
||||
"volumeMixer": "~/.config/hypr/hyprland/scripts/launch_first_available.sh \"pavucontrol-qt\" \"pavucontrol\""
|
||||
},
|
||||
"background": {
|
||||
"blurRadius": 32,
|
||||
"centeredWallpaper": false,
|
||||
"centeredWallpaperColor": "primaryContainer",
|
||||
"centeredWallpaperOnlyWhenLocked": false,
|
||||
"centeredWallpaperShape": "Cookie7Sided",
|
||||
"centeredWallpaperSize": 400,
|
||||
"enableWallpaperPreview": false,
|
||||
"hideWhenFullscreen": true,
|
||||
"lockWall": "",
|
||||
"parallax": {
|
||||
"autoVertical": false,
|
||||
"enableSidebar": true,
|
||||
"enableWorkspace": true,
|
||||
"vertical": false,
|
||||
"widgetsFactor": 1.2,
|
||||
"workspaceZoom": 1.07
|
||||
},
|
||||
"screenList": [],
|
||||
"showBlur": true,
|
||||
"showGrid": true,
|
||||
"showSnapLines": true,
|
||||
"splitRatio": "25",
|
||||
"splitSide": "right",
|
||||
"thumbnailPath": "",
|
||||
"wallpaperAnimation": "random",
|
||||
"wallpaperPath": "/home/alvin/Pictures/Wallpapers/Dynamic-Wallpapers/Dark/japan-street-Dark.png",
|
||||
"widgets": {
|
||||
"blurRadius": 32,
|
||||
"blurWidgets": false,
|
||||
"calendar": {
|
||||
"enable": true,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "2x2",
|
||||
"x": 1596,
|
||||
"y": 744,
|
||||
"z": 4
|
||||
},
|
||||
"clock": {
|
||||
"color": "",
|
||||
"cookie": {
|
||||
"aiStyling": false,
|
||||
"constantlyRotate": false,
|
||||
"dateInClock": true,
|
||||
"dateStyle": "bubble",
|
||||
"dialNumberStyle": "full",
|
||||
"hourHandStyle": "hollow",
|
||||
"hourMarks": false,
|
||||
"minuteHandStyle": "medium",
|
||||
"secondHandStyle": "line",
|
||||
"sides": 12,
|
||||
"timeIndicators": true,
|
||||
"useSineCookie": false
|
||||
},
|
||||
"digital": {
|
||||
"adaptiveAlignment": true,
|
||||
"animateChange": true,
|
||||
"font": {
|
||||
"family": "Google Sans Flex",
|
||||
"roundness": 1.1290989771359807,
|
||||
"size": 129.49608904933814,
|
||||
"weight": 350,
|
||||
"width": 99.44532190132371
|
||||
},
|
||||
"showDate": true,
|
||||
"vertical": false
|
||||
},
|
||||
"enable": true,
|
||||
"pixel": {
|
||||
"orientation": "vertical"
|
||||
},
|
||||
"placementStrategy": "leastBusy",
|
||||
"quote": {
|
||||
"enable": true,
|
||||
"followClock": false,
|
||||
"text": "Kawaī neko o zenbu tabe chaimasu."
|
||||
},
|
||||
"showOnlyWhenLocked": false,
|
||||
"style": "digital",
|
||||
"styleLocked": "cookie",
|
||||
"x": 1423,
|
||||
"y": 383,
|
||||
"z": 3
|
||||
},
|
||||
"customImage": {
|
||||
"enable": true,
|
||||
"path": "",
|
||||
"placementStrategy": "free",
|
||||
"shape": "Cookie4Sided",
|
||||
"size": 200,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"customText": {
|
||||
"alignment": "center",
|
||||
"color": "",
|
||||
"content": "Hello world",
|
||||
"enable": true,
|
||||
"fontFamily": "Caveat",
|
||||
"fontSize": 72,
|
||||
"placementStrategy": "free",
|
||||
"shadow": true,
|
||||
"x": 400,
|
||||
"y": 300,
|
||||
"z": 0
|
||||
},
|
||||
"images": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"media": {
|
||||
"backgroundShape": "Cookie4Sided",
|
||||
"enable": true,
|
||||
"placementStrategy": "free",
|
||||
"showControls": true,
|
||||
"showLyrics": false,
|
||||
"showTitles": true,
|
||||
"sizeMode": "1x3",
|
||||
"x": 744,
|
||||
"y": 876,
|
||||
"z": 1
|
||||
},
|
||||
"notes": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"resources": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"vertical": false,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"shadow": true,
|
||||
"sticker": {
|
||||
"enable": false,
|
||||
"items": [],
|
||||
"outlineColor": "#ffffff",
|
||||
"outlineWidth": 8,
|
||||
"path": "",
|
||||
"placementStrategy": "free",
|
||||
"rotation": 0,
|
||||
"size": 200,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"timers": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"vertical": false,
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"todo": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"userCard": {
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "1x2",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"visualizer": {
|
||||
"colorSource": "theme",
|
||||
"enable": true,
|
||||
"height": 342,
|
||||
"placementStrategy": "free",
|
||||
"ringSize": 380,
|
||||
"sensitivity": 1.65,
|
||||
"style": "mirror",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": -1000
|
||||
},
|
||||
"weather": {
|
||||
"enable": false,
|
||||
"expanded": false,
|
||||
"placementStrategy": "mostBusy",
|
||||
"sizeMode": "1x3",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
},
|
||||
"worldClock": {
|
||||
"clockCount": 4,
|
||||
"enable": false,
|
||||
"placementStrategy": "free",
|
||||
"sizeMode": "2x2",
|
||||
"timezones": [
|
||||
"Australia/Sydney",
|
||||
"Asia/Tokyo",
|
||||
"Europe/London",
|
||||
"America/New_York"
|
||||
],
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
"widgetsLocked": false
|
||||
},
|
||||
"bar": {
|
||||
"autoHide": {
|
||||
"enable": false,
|
||||
"hoverRegionWidth": 2,
|
||||
"pushWindows": false,
|
||||
"showWhenPressingSuper": {
|
||||
"delay": 140,
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"borderless": "transparent",
|
||||
"bottom": false,
|
||||
"centerOnlyReserveFrame": false,
|
||||
"cornerStyle": 3,
|
||||
"divider": {
|
||||
"spacing": 20,
|
||||
"style": "rect"
|
||||
},
|
||||
"dynamicIsland": {
|
||||
"leftWidget": "clockWidget",
|
||||
"rightWidget": "systemIcons",
|
||||
"showMediaControls": false,
|
||||
"visualizerStyle": "wave"
|
||||
},
|
||||
"floatStyleShadow": true,
|
||||
"followFrameColor": false,
|
||||
"frameColor": "secondaryContainer",
|
||||
"frameThickness": 4,
|
||||
"groupColor": "layer1",
|
||||
"indicators": {
|
||||
"notifications": {
|
||||
"showUnreadCount": true
|
||||
}
|
||||
},
|
||||
"layouts": {
|
||||
"leftLayout": [
|
||||
"leftSidebarButton",
|
||||
"activeWindow",
|
||||
"resources"
|
||||
],
|
||||
"middleLayout": [
|
||||
"media",
|
||||
"clockWidget",
|
||||
"workspaces",
|
||||
"utilButtons",
|
||||
"weatherBar"
|
||||
],
|
||||
"rightLayout": [
|
||||
"sysTray",
|
||||
"updatesCount",
|
||||
"bluetooth",
|
||||
"systemIcons"
|
||||
]
|
||||
},
|
||||
"media": {
|
||||
"alwaysVisible": false,
|
||||
"maxWidth": 280,
|
||||
"minWidth": 100,
|
||||
"onlyTitle": false,
|
||||
"preferredPlayer": "",
|
||||
"showLyrics": false
|
||||
},
|
||||
"resources": {
|
||||
"alwaysShowCpu": true,
|
||||
"alwaysShowCpuTemp": true,
|
||||
"alwaysShowDisk": false,
|
||||
"alwaysShowRam": true,
|
||||
"alwaysShowSwap": false,
|
||||
"cpuWarningThreshold": 90,
|
||||
"memoryWarningThreshold": 95,
|
||||
"showValue": true,
|
||||
"style": "outline",
|
||||
"swapWarningThreshold": 85
|
||||
},
|
||||
"screenList": [],
|
||||
"showBackground": true,
|
||||
"showFrame": false,
|
||||
"tooltips": {
|
||||
"clickToShow": false,
|
||||
"enable": true
|
||||
},
|
||||
"topLeftIcon": "spark",
|
||||
"utilButtons": {
|
||||
"isRecording": false,
|
||||
"showColorPicker": false,
|
||||
"showDarkModeToggle": false,
|
||||
"showKeyboardToggle": false,
|
||||
"showMicToggle": true,
|
||||
"showPerformanceProfileToggle": true,
|
||||
"showScreenRecord": false,
|
||||
"showScreenSnip": false,
|
||||
"showWallpaperToggle": false
|
||||
},
|
||||
"verbose": true,
|
||||
"vertical": false,
|
||||
"workspaces": {
|
||||
"alwaysShowNumbers": true,
|
||||
"indicatorStyle": "dot",
|
||||
"monochromeIcons": true,
|
||||
"numberMap": [],
|
||||
"showAppIcons": true,
|
||||
"showNumberDelay": 200,
|
||||
"shown": 10,
|
||||
"useNerdFont": false
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"locale": "en-GB"
|
||||
},
|
||||
"conflictKiller": {
|
||||
"autoKillNotificationDaemons": true,
|
||||
"autoKillTrays": true
|
||||
},
|
||||
"crosshair": {
|
||||
"code": "0;P;d;1;0l;10;0o;2;1b;0"
|
||||
},
|
||||
"custom": {
|
||||
"colorizeIcon": true,
|
||||
"distroIcon": "google-gemini-symbolic"
|
||||
},
|
||||
"dock": {
|
||||
"enable": true,
|
||||
"height": 60,
|
||||
"hoverRegionHeight": 2,
|
||||
"hoverToReveal": true,
|
||||
"ignoredAppRegexes": [],
|
||||
"monochromeIcons": false,
|
||||
"pinnedApps": [
|
||||
"org.kde.dolphin",
|
||||
"zen"
|
||||
],
|
||||
"pinnedOnStartup": false,
|
||||
"showAppsButton": true,
|
||||
"showBackground": true,
|
||||
"showMedia": true,
|
||||
"showPinButton": true
|
||||
},
|
||||
"hacks": {
|
||||
"arbitraryRaceConditionDelay": 20
|
||||
},
|
||||
"hyprland": {
|
||||
"animations": {
|
||||
"animation": "normal",
|
||||
"enable": true
|
||||
},
|
||||
"decoration": {
|
||||
"activeOpacity": 1,
|
||||
"blur": {
|
||||
"enabled": false,
|
||||
"passes": 3,
|
||||
"size": 1
|
||||
},
|
||||
"inactiveOpacity": 0.95,
|
||||
"rounding": 22,
|
||||
"shadow": {
|
||||
"enabled": true,
|
||||
"range": 4
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
"borderColor": {
|
||||
"activeOpacity": 0.47,
|
||||
"activeRole": "layer0Border",
|
||||
"enable": false,
|
||||
"inactiveOpacity": 0.2,
|
||||
"inactiveRole": "layer0Border"
|
||||
},
|
||||
"borderSize": 1,
|
||||
"gapsIn": 2,
|
||||
"gapsOut": 5
|
||||
},
|
||||
"idle": {
|
||||
"lock": 300,
|
||||
"screenOff": 600,
|
||||
"suspend": 10800
|
||||
}
|
||||
},
|
||||
"interactions": {
|
||||
"deadPixelWorkaround": {
|
||||
"enable": false
|
||||
},
|
||||
"scrolling": {
|
||||
"fasterTouchpadScroll": false,
|
||||
"mouseScrollDeltaThreshold": 120,
|
||||
"mouseScrollFactor": 120,
|
||||
"touchpadScrollFactor": 450
|
||||
}
|
||||
},
|
||||
"launcher": {
|
||||
"pinnedApps": [
|
||||
"org.kde.dolphin",
|
||||
"kitty",
|
||||
"cmake-gui"
|
||||
]
|
||||
},
|
||||
"light": {
|
||||
"antiFlashbang": {
|
||||
"enable": false
|
||||
},
|
||||
"night": {
|
||||
"automatic": false,
|
||||
"colorTemperature": 5559,
|
||||
"from": "19:00",
|
||||
"to": "06:30"
|
||||
}
|
||||
},
|
||||
"lock": {
|
||||
"blur": {
|
||||
"enable": false,
|
||||
"extraZoom": 1.1,
|
||||
"radius": 100,
|
||||
"size": 20
|
||||
},
|
||||
"centerClock": true,
|
||||
"launchOnStartup": false,
|
||||
"materialShapeChars": true,
|
||||
"security": {
|
||||
"requirePasswordToPower": true,
|
||||
"unlockKeyring": true
|
||||
},
|
||||
"showLockedText": true,
|
||||
"showMedia": true,
|
||||
"showToolbars": true,
|
||||
"showWidgets": false,
|
||||
"useHyprlock": false
|
||||
},
|
||||
"media": {
|
||||
"filterDuplicatePlayers": true
|
||||
},
|
||||
"notifications": {
|
||||
"position": "top_right",
|
||||
"timeout": 7000
|
||||
},
|
||||
"osd": {
|
||||
"timeout": 3000
|
||||
},
|
||||
"osk": {
|
||||
"layout": "qwerty_full",
|
||||
"pinnedOnStartup": false
|
||||
},
|
||||
"overlay": {
|
||||
"clickthroughOpacity": 0.8,
|
||||
"darkenScreen": false,
|
||||
"floatingImage": {
|
||||
"imageSource": "https://media.tenor.com/H5U5bJzj3oAAAAAi/kukuru.gif",
|
||||
"scale": 0.5
|
||||
},
|
||||
"openingZoomAnimation": true
|
||||
},
|
||||
"overview": {
|
||||
"centerIcons": true,
|
||||
"columns": 5,
|
||||
"enable": true,
|
||||
"orderBottomUp": false,
|
||||
"orderRightLeft": false,
|
||||
"rows": 2,
|
||||
"scale": 0.18,
|
||||
"style": "default"
|
||||
},
|
||||
"panelFamily": "ii",
|
||||
"policies": {
|
||||
"ai": 1,
|
||||
"weeb": 1
|
||||
},
|
||||
"profile": {
|
||||
"avatarPath": "",
|
||||
"avatarPicture": "",
|
||||
"descriptionText": "::uptime::",
|
||||
"displayName": "",
|
||||
"onlinePresets": false
|
||||
},
|
||||
"regionSelector": {
|
||||
"annotation": {
|
||||
"useSatty": false
|
||||
},
|
||||
"circle": {
|
||||
"padding": 10,
|
||||
"strokeWidth": 6
|
||||
},
|
||||
"rect": {
|
||||
"showAimLines": true
|
||||
},
|
||||
"targetRegions": {
|
||||
"content": true,
|
||||
"contentRegionOpacity": 0.8,
|
||||
"layers": true,
|
||||
"opacity": 0.3,
|
||||
"selectionPadding": 5,
|
||||
"showLabel": false,
|
||||
"windows": true
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"historyLength": 60,
|
||||
"updateInterval": 3100
|
||||
},
|
||||
"settings": {
|
||||
"borderColor": "layer0Border",
|
||||
"borderSize": 1,
|
||||
"collapsedSections": [],
|
||||
"style": "default"
|
||||
},
|
||||
"sidebar": {
|
||||
"ai": {
|
||||
"textFadeIn": false
|
||||
},
|
||||
"banner": true,
|
||||
"bannerImage": "",
|
||||
"booru": {
|
||||
"allowNsfw": true,
|
||||
"defaultProvider": "yandere",
|
||||
"limit": 20,
|
||||
"zerochan": {
|
||||
"username": "theodorer61"
|
||||
}
|
||||
},
|
||||
"bottomGroup": false,
|
||||
"cornerOpen": {
|
||||
"bottom": true,
|
||||
"bottomLeftAction": "sidebarLeftOpen",
|
||||
"bottomRightAction": "sidebarRightOpen",
|
||||
"clickless": false,
|
||||
"clicklessCornerEnd": true,
|
||||
"clicklessCornerVerticalOffset": 1,
|
||||
"cornerRegionHeight": 5,
|
||||
"cornerRegionWidth": 250,
|
||||
"enable": true,
|
||||
"valueScroll": true,
|
||||
"visualize": false
|
||||
},
|
||||
"keepRightSidebarLoaded": false,
|
||||
"media": {
|
||||
"artColors": false,
|
||||
"artShape": "Oval",
|
||||
"blurredBackground": false,
|
||||
"enable": true,
|
||||
"shapeArt": false,
|
||||
"showLyrics": true
|
||||
},
|
||||
"mediaPlayer": false,
|
||||
"quickSliders": {
|
||||
"enable": true,
|
||||
"showBrightness": true,
|
||||
"showMic": true,
|
||||
"showVolume": true
|
||||
},
|
||||
"quickToggles": {
|
||||
"android": {
|
||||
"columns": 6,
|
||||
"toggles": [
|
||||
{
|
||||
"size": 2,
|
||||
"type": "network"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "bluetooth"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "audio"
|
||||
},
|
||||
{
|
||||
"size": 1,
|
||||
"type": "idleInhibitor"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "mic"
|
||||
},
|
||||
{
|
||||
"size": 1,
|
||||
"type": "nightLight"
|
||||
},
|
||||
{
|
||||
"size": 2,
|
||||
"type": "powerProfile"
|
||||
}
|
||||
]
|
||||
},
|
||||
"style": "android"
|
||||
},
|
||||
"translator": {
|
||||
"delay": 300,
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"tray": {
|
||||
"filterPassive": true,
|
||||
"invertPinnedItems": true,
|
||||
"monochromeIcons": false,
|
||||
"pinnedItems": [
|
||||
"Fcitx"
|
||||
],
|
||||
"showItemId": false
|
||||
},
|
||||
"wallpaperSelector": {
|
||||
"changeInterval": 0,
|
||||
"closeAfterSelection": true,
|
||||
"columns": 4,
|
||||
"liveWallpapersPath": "",
|
||||
"showBlurBackground": false,
|
||||
"showHomePath": true,
|
||||
"showSearchbar": true,
|
||||
"sortMode": "time",
|
||||
"useSystemFileDialog": false,
|
||||
"userPath": "/home/alvin/Pictures/Wallpapers/"
|
||||
},
|
||||
"windows": {
|
||||
"centerTitle": true,
|
||||
"showTitlebar": true
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,30 +0,0 @@
|
||||
[Unit]
|
||||
Description=Network Manager
|
||||
Documentation=man:NetworkManager.conf(5)
|
||||
Wants=NetworkManager-wait-online.service
|
||||
After=sysinit.target network-pre.target
|
||||
Wants=network-pre.target
|
||||
Before=network-online.target remote-fs.target
|
||||
After=network-pre.target
|
||||
BindsTo=sys-subsystem-net-devices.target
|
||||
After=sys-subsystem-net-devices.target
|
||||
|
||||
[Service]
|
||||
Type=dbus
|
||||
BusName=org.freedesktop.NetworkManager
|
||||
ExecCondition=/usr/bin/test -x /usr/bin/NetworkManager
|
||||
ExecReload=/usr/bin/kill -HUP $MAINPID
|
||||
ExecStart=/usr/bin/NetworkManager --no-daemon
|
||||
Restart=on-failure
|
||||
KillMode=process
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
ProtectHome=yes
|
||||
ProtectSystem=yes
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
SystemCallFilter=@system-service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1 @@
|
||||
/usr/lib/systemd/system/NetworkManager.service
|
||||
@@ -48,4 +48,13 @@ Include = /etc/pacman.d/cachyos-mirrorlist
|
||||
|
||||
[chaotic-aur]
|
||||
SigLevel = Required DatabaseOptional
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
|
||||
[local-aur]
|
||||
# Ships on the medium only: packages built at ISO build time (end-4
|
||||
# dependencies, plus localrepo/). Keeps pacstrap from needing the network for
|
||||
# anything that is not in the official repos. alvin-install strips this whole
|
||||
# section from the target system's pacman.conf, because /opt/localrepo exists
|
||||
# only on the live medium.
|
||||
SigLevel = Optional TrustAll
|
||||
Server = file:///opt/localrepo
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# ~/.zshrc - installed into /etc/skel by customize_airootfs.sh (it replaces
|
||||
# grml-zsh-config's skel copy; grml's global /etc/zsh/zshrc still loads first).
|
||||
|
||||
export ZSH="$HOME/.oh-my-zsh"
|
||||
ZSH_THEME="" # starship draws the prompt
|
||||
zstyle ':omz:update' mode reminder # never auto-pull on startup
|
||||
|
||||
# Plugins are pre-cloned into $ZSH/custom/plugins at ISO build time.
|
||||
# zsh-syntax-highlighting must load before zsh-history-substring-search.
|
||||
# zsh-autocomplete is cloned too but left off: it must load before compinit,
|
||||
# which grml's /etc/zsh/zshrc already ran, and it fights zsh-autosuggestions.
|
||||
# To use it anyway, see README.md "zsh-autocomplete partial-load error".
|
||||
plugins=(
|
||||
git
|
||||
fzf-zsh-plugin
|
||||
zsh-eza
|
||||
zsh-autosuggestions
|
||||
zsh-syntax-highlighting
|
||||
zsh-history-substring-search
|
||||
)
|
||||
|
||||
source "$ZSH/oh-my-zsh.sh"
|
||||
|
||||
bindkey '^[[A' history-substring-search-up
|
||||
bindkey '^[[B' history-substring-search-down
|
||||
|
||||
[[ -f ~/.fzf.zsh ]] && source ~/.fzf.zsh
|
||||
|
||||
if command -v starship >/dev/null 2>&1 && [[ "$TERM" != "linux" ]]; then
|
||||
eval "$(starship init zsh)"
|
||||
fi
|
||||
@@ -1,6 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# The chroot's /dev is the kernel's single shared devtmpfs, which only gets the
|
||||
# fd/stdin/stdout/stderr symlinks at boot (from mkinitcpio's init). If the
|
||||
# runner environment lacks them, process substitution (< <(...)) and upstream
|
||||
# tools (pacman-key) fail with "… /dev/fd/63: No such file or directory".
|
||||
# Self-heal here; the CI build-iso workflow does the same before pacstrap so
|
||||
# that mkinitcpio's hook (which pipes through /dev/stdin) works too.
|
||||
for _link in "fd:/proc/self/fd" "stdin:/proc/self/fd/0" \
|
||||
"stdout:/proc/self/fd/1" "stderr:/proc/self/fd/2"; do
|
||||
[[ -e "/dev/${_link%%:*}" || -L "/dev/${_link%%:*}" ]] ||
|
||||
ln -sfn "${_link#*:}" "/dev/${_link%%:*}"
|
||||
done
|
||||
unset _link
|
||||
|
||||
# Populate the keyring in the built root from the keyring bundles
|
||||
# (/usr/share/pacman/keyrings, incl. the copied cachyos keyring).
|
||||
# This mirrors the guide; it runs inside the image you are making.
|
||||
@@ -14,6 +27,17 @@ pacman-key --populate archlinux cachyos chaotic
|
||||
install -m 0644 /root/.live-pacman.conf /etc/pacman.conf
|
||||
rm -f /root/.live-pacman.conf
|
||||
|
||||
# Build stamp staged by profiledef.sh (absent for non-git, non-CI builds):
|
||||
# keep the stamp file itself as a reference, drop the staging copy, and
|
||||
# stamp the live medium's motd so the booted ISO identifies its build (the
|
||||
# installed system keeps the stock package motd - alvin-install never
|
||||
# writes /etc/motd).
|
||||
if [[ -f /root/.iso-build-stamp ]]; then
|
||||
install -m 0644 /root/.iso-build-stamp /etc/alvin-build-stamp
|
||||
cat /etc/alvin-build-stamp >> /etc/motd
|
||||
rm -f /root/.iso-build-stamp
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Pre-install dotfile dependencies into /etc/skel so new users get them
|
||||
# instantly on first login (no network required).
|
||||
@@ -48,6 +72,14 @@ for plugin in "${!OMZ_PLUGINS[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
# grml-zsh-config owns /etc/skel/.zshrc, so shipping ours in
|
||||
# airootfs/etc/skel would make pacstrap abort ("exists in filesystem").
|
||||
# Stage it under /root and overwrite the package's copy here, after pacstrap.
|
||||
if [[ -f /root/.zshrc-skel ]]; then
|
||||
install -m 0644 /root/.zshrc-skel "${SKEL}/.zshrc"
|
||||
rm -f /root/.zshrc-skel
|
||||
fi
|
||||
|
||||
# ── LazyVim (lazy.nvim bootstraps on first launch, but pre-clone the plugin) ─
|
||||
NVIM_DATA="${SKEL}/.local/share/nvim"
|
||||
mkdir -p "${NVIM_DATA}"
|
||||
@@ -57,18 +89,348 @@ if [[ ! -d "${NVIM_DATA}/lazy/lazy.nvim" ]]; then
|
||||
https://github.com/folke/lazy.nvim.git "${NVIM_DATA}/lazy/lazy.nvim"
|
||||
fi
|
||||
|
||||
# ── Fish: ensure default shell is set for the live user ──────────────────────
|
||||
# The live user (usually 'archlinux' or 'root') gets fish as default shell
|
||||
# ── end-4 dots-hyprland, staged for alvin-install (avoids a clone at install) ─
|
||||
DOTS_REPO="/opt/dots-hyprland"
|
||||
if [[ ! -d "${DOTS_REPO}/.git" ]]; then
|
||||
echo "[customize] Cloning end-4 dots-hyprland..."
|
||||
git clone --depth=1 https://github.com/end-4/dots-hyprland.git "${DOTS_REPO}"
|
||||
git -C "${DOTS_REPO}" submodule update --init --depth=1
|
||||
fi
|
||||
|
||||
# end-4 pins quickshell to 41651d7 (Sep 25), which predates quickshell's
|
||||
# Qt 6.12 fix (5d5d498, Oct 3: "use MOC includes instead of opaque pointer
|
||||
# definitions - Fixes linking issues and Qt 6.12 compatibility"). Arch
|
||||
# shipped qt6 6.12.0 into extra on Oct 8; with the old pin qmetatype.h's
|
||||
# "Meta Types must be fully defined" static_assert kills the moc build of
|
||||
# quickshell-hyprland-ipc (failed CI run 137) and the whole ISO build with
|
||||
# it. Force the fixed commit, but ONLY while end-4 still carries the old
|
||||
# pin - once they bump it themselves the guard stops matching and their
|
||||
# pin wins untouched. localrepo/check-quickshell-pin.sh (weekly CI)
|
||||
# verifies both this pin and end-4's stay Qt 6.12-compatible.
|
||||
QS_PKG="${DOTS_REPO}/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD"
|
||||
if [[ -f "${QS_PKG}" ]] && grep -q "^_commit='41651d7" "${QS_PKG}"; then
|
||||
echo "[customize] Bumping end-4 quickshell pin to 11ca60be (Qt 6.12 fix)..."
|
||||
sed -i "s|^_commit='41651d7[^']*'|_commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'|" "${QS_PKG}"
|
||||
fi
|
||||
|
||||
# GCC 16.2 ICEs (segfault in ggc_set_mark) compiling MicroTeX's gtkmm_main.cpp
|
||||
# - the LaTeX binary the package ships - under CachyOS's makepkg.conf
|
||||
# (-march=native -O3 + -flto=auto), which killed CI run 155. Build it with -O2
|
||||
# and no LTO; MicroTeX is tiny, the flags buy nothing. Re-running is a no-op.
|
||||
MTX_PKG="${DOTS_REPO}/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD"
|
||||
if [[ -f "${MTX_PKG}" ]] && ! grep -q '^options=(!lto)' "${MTX_PKG}"; then
|
||||
echo "[customize] Building MicroTeX with -O2 and no LTO (GCC 16 ICE)..."
|
||||
# shellcheck disable=SC2016 # expanded by makepkg, not here
|
||||
sed -i '/^build() {/a\ export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"' "${MTX_PKG}"
|
||||
printf '\noptions=(!lto)\n' >> "${MTX_PKG}"
|
||||
fi
|
||||
|
||||
# ── end4-pC: the default quickshell config, shipped in skel ──────────────────
|
||||
# pctrade/end4-pC is a fork of the ii shell config. It lives next to ii in
|
||||
# ~/.config/quickshell and does not modify it; which one runs is decided by
|
||||
# qsConfig (patched just below). The repo has no submodules.
|
||||
if [[ ! -d "${SKEL}/.config/quickshell/end4-pC" ]]; then
|
||||
echo "[customize] Cloning end4-pC quickshell config into skel..."
|
||||
git clone --depth=1 https://github.com/pctrade/end4-pC.git \
|
||||
"${SKEL}/.config/quickshell/end4-pC"
|
||||
fi
|
||||
|
||||
# ── Default quickshell config: ii -> end4-pC ─────────────────────────────────
|
||||
# alvin-install copies this staged repo onto the target and runs `./setup
|
||||
# install`, whose rsync writes hypr/hyprland/variables.lua into the user's
|
||||
# home - so patching the staged copy is what sets qsConfig on the installed
|
||||
# system. Re-running is a no-op; anything other than ii/end4-pC aborts.
|
||||
VARIABLES_LUA="${DOTS_REPO}/dots/.config/hypr/hyprland/variables.lua"
|
||||
if grep -Fq 'hl.env("qsConfig", "ii")' "${VARIABLES_LUA}"; then
|
||||
echo "[customize] Setting qsConfig to end4-pC..."
|
||||
sed -i 's|hl\.env("qsConfig", "ii")|hl.env("qsConfig", "end4-pC")|' "${VARIABLES_LUA}"
|
||||
fi
|
||||
if ! grep -Fq 'hl.env("qsConfig", "end4-pC")' "${VARIABLES_LUA}"; then
|
||||
echo "[customize] qsConfig is neither ii nor end4-pC in ${VARIABLES_LUA}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Pre-install end-4 dependencies into the image (network heavy, once per ISO)
|
||||
# ============================================================================
|
||||
# ./setup install-deps resolves and *builds* every AUR/source dependency the
|
||||
# dotfiles need (quickshell, MicroTeX, theming, fonts, ...). Doing it here lets
|
||||
# alvin-install run its end-4 step with --skip-alldeps, so an install only
|
||||
# copies files - no AUR clones, no makepkg, no dependency resolution.
|
||||
#
|
||||
# The script refuses to run as root, hence the throwaway builder account.
|
||||
# ============================================================================
|
||||
BUILD_USER="ii-builder"
|
||||
|
||||
if ! id -u "${BUILD_USER}" &>/dev/null; then
|
||||
useradd -m -u 1500 "${BUILD_USER}"
|
||||
fi
|
||||
echo "${BUILD_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99-ii-builder
|
||||
chmod 440 /etc/sudoers.d/99-ii-builder
|
||||
|
||||
# makepkg writes src/, pkg/ and *.pkg.tar.zst into the PKGBUILD directories,
|
||||
# so the tree must be owned by the account that runs it.
|
||||
chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}"
|
||||
|
||||
# yay 13 (libalpm via purego/dyalpm) can SIGSEGV in alpm_release() at exit,
|
||||
# *after* the transaction and its hooks completed (failed CI run: crash in
|
||||
# dyalpm handle.Release). end-4 sees exit 2 and aborts. This shim, put first
|
||||
# on PATH for the install-deps run only, turns a failed plain `yay -S <pkgs>`
|
||||
# into success when every target is installed afterwards (pacman -T); any
|
||||
# other failure keeps yay's exit code. It lives in /tmp and is removed after.
|
||||
YAY_SHIM_DIR="$(mktemp -d /tmp/ii-yay-shim.XXXXXX)"
|
||||
cat > "${YAY_SHIM_DIR}/yay" <<'EOF'
|
||||
#!/bin/bash
|
||||
"${YAY_SHIM_REAL:-/usr/bin/yay}" "$@"
|
||||
rc=$?
|
||||
(( rc == 0 )) && exit 0
|
||||
sync=0
|
||||
targets=()
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
-S|--sync) sync=1 ;;
|
||||
-S*) exit "$rc" ;; # -Ss/-Si/-Syu/...: not a plain install
|
||||
-*) ;;
|
||||
*) targets+=("$a") ;;
|
||||
esac
|
||||
done
|
||||
if (( sync && ${#targets[@]} )) && pacman -T -- "${targets[@]}" >/dev/null; then
|
||||
echo "[yay-shim] yay exited ${rc}, but all ${#targets[@]} targets are installed - continuing" >&2
|
||||
exit 0
|
||||
fi
|
||||
exit "$rc"
|
||||
EOF
|
||||
chmod 755 "${YAY_SHIM_DIR}" "${YAY_SHIM_DIR}/yay"
|
||||
|
||||
# -f: never prompt (ask=false); -s: skip "pacman -Syu";
|
||||
# --skip-plasmaintg: optional Firefox widget that pulls 410 pkgs / 526 MiB of
|
||||
# KDE packages onto a non-KDE system;
|
||||
# stdin: answer end-4's x() failure prompt with "e", so a failed command
|
||||
# aborts the build instead of blocking forever without a TTY.
|
||||
# shellcheck disable=SC2024 # the redirect is meant for the child, not sudo
|
||||
if ! sudo -u "${BUILD_USER}" env HOME="/home/${BUILD_USER}" \
|
||||
GIT_TERMINAL_PROMPT=0 \
|
||||
PATH="${YAY_SHIM_DIR}:/usr/local/sbin:/usr/local/bin:/usr/bin" \
|
||||
bash -c "cd ${DOTS_REPO} && ./setup install-deps -f -s --skip-plasmaintg" \
|
||||
< <(yes e); then
|
||||
rm -rf "${YAY_SHIM_DIR}"
|
||||
echo "[customize] end-4 ./setup install-deps failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
rm -rf "${YAY_SHIM_DIR}"
|
||||
|
||||
# ── Harvest the packages install-deps just built into the in-image local repo ─
|
||||
# The live /etc/pacman.conf points [local-aur] at /opt/localrepo; without the
|
||||
# harvested copies pacstrap cannot resolve these packages at install time and
|
||||
# would silently drop them from the target system.
|
||||
mkdir -p /opt/localrepo
|
||||
shopt -s nullglob
|
||||
|
||||
# <name> <version> from a package's embedded .PKGINFO (update.sh does the same)
|
||||
pkg_meta() {
|
||||
tar -xOf "$1" .PKGINFO 2>/dev/null |
|
||||
awk -F' = ' '/^pkgname = /{n=$2} /^pkgver = /{v=$2} END{print n, v}'
|
||||
}
|
||||
|
||||
mapfile -d '' -t built_pkgs < <(
|
||||
find "${DOTS_REPO}" "/home/${BUILD_USER}/.cache/yay" \
|
||||
-name '*.pkg.tar.zst' -print0 2>/dev/null
|
||||
)
|
||||
|
||||
declare -A built_file=() built_ver=()
|
||||
for p in "${built_pkgs[@]}"; do
|
||||
read -r n v < <(pkg_meta "$p")
|
||||
[[ -n "${n:-}" && -n "${v:-}" ]] || continue
|
||||
built_file["$n"]="$p"
|
||||
built_ver["$n"]="$v"
|
||||
done
|
||||
|
||||
# One entry per package name, and the entry must be the version the image
|
||||
# actually installed - a stale copy with the same name but another version
|
||||
# would let pacstrap install something else than what was tested.
|
||||
for existing in /opt/localrepo/*.pkg.tar.zst; do
|
||||
[[ -e "$existing" ]] || continue
|
||||
read -r n v < <(pkg_meta "$existing")
|
||||
[[ -n "${n:-}" ]] || continue
|
||||
if [[ -n "${built_ver[$n]:-}" ]]; then
|
||||
echo "[customize] Replacing localrepo ${n} ${v} with built ${built_ver[$n]}"
|
||||
rm -f "$existing"
|
||||
fi
|
||||
done
|
||||
|
||||
if ((${#built_file[@]} > 0)); then
|
||||
echo "[customize] Harvesting ${#built_file[@]} package(s) into /opt/localrepo"
|
||||
cp -f -- "${built_file[@]}" /opt/localrepo/
|
||||
# makepkg -Afsi leaves its archives next to the PKGBUILD. They are already
|
||||
# harvested (and installed in this image); keeping them in the dots tree
|
||||
# would ship a second copy inside /opt/dots-hyprland, which alvin-install
|
||||
# copies to ~/.dots-hyprland on every installed system.
|
||||
find "${DOTS_REPO}" -type f -name '*.pkg.tar.zst' -delete
|
||||
fi
|
||||
|
||||
# Rebuild the repo database (CI/update.sh ship a pre-built one alongside the
|
||||
# packages) and refresh the live system's copy of it. No network needed: the
|
||||
# remote repos were synced during pacstrap. No -R: the loop above already
|
||||
# deleted the replaced archives, so repo-add's own cleanup only logged a
|
||||
# spurious "Cannot remove file" ERROR.
|
||||
local_pkgs=(/opt/localrepo/*.pkg.tar.zst)
|
||||
if ((${#local_pkgs[@]} > 0)); then
|
||||
echo "[customize] Rebuilding local-aur db (${#local_pkgs[@]} packages)..."
|
||||
repo-add /opt/localrepo/local-aur.db.tar.gz "${local_pkgs[@]}"
|
||||
mkdir -p /var/lib/pacman/sync
|
||||
cp -fL /opt/localrepo/local-aur.db /var/lib/pacman/sync/local-aur.db
|
||||
if [[ -f /opt/localrepo/local-aur.files ]]; then
|
||||
cp -fL /opt/localrepo/local-aur.files /var/lib/pacman/sync/local-aur.files
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── mono certificate store ──────────────────────────────────────────────────
|
||||
# mono's post_install runs cert-sync during pacstrap, before the
|
||||
# post-transaction hook has generated ca-certificates.crt, so the store ships
|
||||
# empty ("Couldn't retrieve the file") and mono apps reject every TLS cert.
|
||||
if command -v cert-sync &>/dev/null; then
|
||||
echo "[customize] Populating mono certificate store..."
|
||||
cert-sync --quiet /etc/ssl/certs/ca-certificates.crt \
|
||||
|| echo "[customize] WARNING: cert-sync failed, mono store left empty" >&2
|
||||
fi
|
||||
|
||||
# ── makepkg leftovers + throwaway account do not belong in the ISO ──────────
|
||||
if [[ -d "${DOTS_REPO}/sdata/dist-arch" ]]; then
|
||||
find "${DOTS_REPO}/sdata/dist-arch" -mindepth 2 -maxdepth 2 -type d \
|
||||
\( -name src -o -name pkg \) -prune -exec rm -rf {} +
|
||||
fi
|
||||
rm -f /etc/sudoers.d/99-ii-builder
|
||||
|
||||
# Processes still running as BUILD_USER make userdel exit 8 and abort the
|
||||
# build. end-4's ./setup is the usual source: sudo_init_keepalive forks
|
||||
# `(sleep 60; sudo true) &` and its EXIT trap kills only the subshell,
|
||||
# orphaning the sleep child for up to a minute. Mirrors shadow's
|
||||
# user_busy_processes(): match real/effective/saved uid AND require the
|
||||
# process to live inside this build chroot - the runner shares our PID
|
||||
# namespace, and a host process could carry the same uid.
|
||||
#
|
||||
# mkarchiso runs this script via arch-chroot, which mounts proc *before*
|
||||
# `unshare --fork --pid`: /proc entry numbers belong to the outer pid
|
||||
# namespace, while kill(2) resolves pids in our (nested) namespace. Use the
|
||||
# last NSpid field - the pid in our own namespace - for signalling; keep the
|
||||
# /proc entry number for logging, since that is what userdel reports.
|
||||
nspid_fields() {
|
||||
awk -F'\t' '/^NSpid:/{ $1=""; sub(/^ +/, ""); print; exit }' "$1"
|
||||
}
|
||||
|
||||
builder_procs() {
|
||||
local uid broot st p key real eff saved kpid cmd
|
||||
local -a nsf self_nsf=()
|
||||
uid=$(id -u "${BUILD_USER}") || return 0
|
||||
broot=$(stat -Lc '%d:%i' /)
|
||||
read -r -a self_nsf < <(nspid_fields /proc/self/status)
|
||||
for st in /proc/[0-9]*/status; do
|
||||
[[ -r "$st" ]] || continue
|
||||
real=""; eff=""; saved=""
|
||||
while read -r key real eff saved _; do
|
||||
[[ $key == "Uid:" ]] && break
|
||||
done < "$st" || continue
|
||||
[[ $real == "$uid" || $eff == "$uid" || $saved == "$uid" ]] || continue
|
||||
p=${st#/proc/}; p=${p%/status}
|
||||
# Stat of /proc/<pid>/root needs CAP_SYS_PTRACE (or same-uid): exactly
|
||||
# what shadow's userdel uses, so a process we cannot see here is one
|
||||
# userdel cannot flag either. Compare by device+inode; skip on failure.
|
||||
[[ $(stat -Lc '%d:%i' "/proc/$p/root" 2>/dev/null) == "$broot" ]] || continue
|
||||
# NSpid chain length must equal ours, i.e. the process must live in our
|
||||
# pid namespace - only then is its last field killable from here.
|
||||
nsf=()
|
||||
read -r -a nsf < <(nspid_fields "/proc/$p/status") || continue
|
||||
if ((${#nsf[@]} > 0 && ${#nsf[@]} == ${#self_nsf[@]})); then
|
||||
kpid=${nsf[${#nsf[@]} - 1]}
|
||||
else
|
||||
kpid="-"
|
||||
fi
|
||||
cmd=$(tr '\0' ' ' < "/proc/$p/cmdline" 2>/dev/null)
|
||||
printf '%s\t%s\t%s\n' "$p" "$kpid" "$cmd"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
sweep_builder_procs() {
|
||||
local sig=$1 p kpid cmd kerr
|
||||
local -a pids=()
|
||||
while IFS=$'\t' read -r p kpid cmd; do
|
||||
[[ -n $p ]] || continue
|
||||
if [[ -z $kpid || $kpid == "-" ]]; then
|
||||
echo "[customize] ${BUILD_USER} process outside our pid namespace (cannot signal): pid $p: $cmd"
|
||||
continue
|
||||
fi
|
||||
echo "[customize] ${BUILD_USER} process ($sig): pid $p: $cmd"
|
||||
pids+=("$kpid")
|
||||
done < <(builder_procs)
|
||||
if ((${#pids[@]} > 0)); then
|
||||
# Surface signal failures (they used to be swallowed, hiding a
|
||||
# pid-namespace mismatch as a silent no-op sweep).
|
||||
if ! kerr=$(kill -"$sig" "${pids[@]}" 2>&1); then
|
||||
[[ -n $kerr ]] && printf '[customize] kill -%s: %s\n' "$sig" "$kerr"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
sweep_builder_procs TERM
|
||||
sleep 2
|
||||
sweep_builder_procs KILL
|
||||
sleep 1
|
||||
|
||||
if ! userdel -r "${BUILD_USER}"; then
|
||||
rm -rf "/home/${BUILD_USER:?}"
|
||||
if ! userdel "${BUILD_USER}"; then
|
||||
echo "[customize] FATAL: cannot remove user ${BUILD_USER}; leftover processes:" >&2
|
||||
builder_procs >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
chown -R root:root "${DOTS_REPO}"
|
||||
|
||||
# ── Google Sans Flex into skel: install_google_sans_flex() early-returns once
|
||||
# fc-list sees the font, so the install step needs no network ──────────────
|
||||
FONT_DIR="${SKEL}/.local/share/fonts/illogical-impulse-google-sans-flex"
|
||||
if [[ ! -f "${FONT_DIR}/.git/HEAD" ]]; then
|
||||
echo "[customize] Pre-installing Google Sans Flex into skel..."
|
||||
rm -rf "${FONT_DIR}"
|
||||
git clone --depth=1 --recurse-submodules --shallow-submodules \
|
||||
https://github.com/end-4/google-sans-flex "${FONT_DIR}"
|
||||
fi
|
||||
|
||||
# ── uv into skel: CPython 3.12 + the end-4 requirements wheel cache ──────────
|
||||
# install-python-packages() runs `uv venv -p 3.12` and `uv pip install -r ...`
|
||||
# during setup. Arch has no python312 package (uv would download one) and the
|
||||
# pinned requirements are ~250 MiB of wheels, so warm both into skel: the venv
|
||||
# is then created and filled straight from the image.
|
||||
if command -v uv &>/dev/null; then
|
||||
echo "[customize] Pre-warming uv (CPython 3.12 + requirements cache)..."
|
||||
warm_venv="/tmp/uv-warm-venv"
|
||||
rm -rf "${warm_venv}"
|
||||
UV_CACHE_DIR="${SKEL}/.cache/uv" \
|
||||
UV_PYTHON_INSTALL_DIR="${SKEL}/.local/share/uv/python" \
|
||||
uv venv -p 3.12 "${warm_venv}"
|
||||
UV_CACHE_DIR="${SKEL}/.cache/uv" \
|
||||
UV_PYTHON_INSTALL_DIR="${SKEL}/.local/share/uv/python" \
|
||||
uv pip install --python "${warm_venv}/bin/python" \
|
||||
-r "${DOTS_REPO}/sdata/uv/requirements.txt"
|
||||
rm -rf "${warm_venv}"
|
||||
else
|
||||
echo "[customize] uv not found - skipping python/wheel pre-warm."
|
||||
fi
|
||||
|
||||
# ── Fish: default shell for the live root user ───────────────────────────────
|
||||
# (alvin-install creates the installed user with fish as well.)
|
||||
if command -v fish &>/dev/null; then
|
||||
echo "[customize] Setting fish as default shell for skel..."
|
||||
echo "[customize] Setting fish as root's shell on the live medium..."
|
||||
chsh -s /usr/bin/fish root 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ── Ensure skel directories have correct permissions ─────────────────────────
|
||||
chmod 700 "${SKEL}/.config" 2>/dev/null || true
|
||||
find "${SKEL}/.config" -type f -exec chmod 644 {} \; 2>/dev/null || true
|
||||
find "${SKEL}/.config" -type d -exec chmod 755 {} \; 2>/dev/null || true
|
||||
find "${SKEL}/.config" -name '*.sh' -exec chmod 755 {} \; 2>/dev/null || true
|
||||
find "${SKEL}/.local" -type d -exec chmod 755 {} \; 2>/dev/null || true
|
||||
# Only take away group/other write and make everything readable: forcing
|
||||
# files to 644 stripped +x from executables that are not *.sh (python/fish
|
||||
# helpers in the cloned quickshell configs).
|
||||
chmod -R go-w,a+rX "${SKEL}/.config" "${SKEL}/.local" 2>/dev/null || true
|
||||
find "${SKEL}/.config" -name '*.sh' -exec chmod 755 {} + 2>/dev/null || true
|
||||
|
||||
echo "[customize] Dotfile pre-installation complete."
|
||||
Executable
+2213
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
title Arch Linux install medium (%ARCH%, UEFI)
|
||||
sort-key 01
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
title Arch Linux install medium (%ARCH%, UEFI) with speech
|
||||
sort-key 02
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||
|
||||
+4
-4
@@ -46,14 +46,14 @@ timeout_style=menu
|
||||
|
||||
menuentry "Arch Linux install medium (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' {
|
||||
set gfxpayload=keep
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
}
|
||||
|
||||
menuentry "Arch Linux install medium with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' {
|
||||
set gfxpayload=keep
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -23,14 +23,14 @@ timeout_style=menu
|
||||
|
||||
menuentry "Arch Linux install medium (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' {
|
||||
set gfxpayload=keep
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}"
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}"
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
}
|
||||
|
||||
menuentry "Arch Linux install medium with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' {
|
||||
set gfxpayload=keep
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" accessibility=on
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" accessibility=on
|
||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -59,8 +59,12 @@ build_local_pkgbuild() {
|
||||
local pkg_name
|
||||
pkg_name=$(basename "${pkg_dir}")
|
||||
|
||||
if ls "${REPO_DIR}"/illogical-impulse-"${pkg_name}"-*.pkg.tar.zst 1>/dev/null 2>&1; then
|
||||
info "Package already exists in localrepo, skipping: ${pkg_name}"
|
||||
# Skip only while a built package is newer than its PKGBUILD - bumping
|
||||
# e.g. quickshell's _commit must trigger a rebuild, not ship the old one.
|
||||
if [[ -n "$(find "${REPO_DIR}" -maxdepth 1 \
|
||||
-name "illogical-impulse-${pkg_name}-*.pkg.tar.zst" \
|
||||
-newer "${pkg_dir}/PKGBUILD" -print -quit)" ]]; then
|
||||
info "Package up to date in localrepo, skipping: ${pkg_name}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -87,34 +91,36 @@ done
|
||||
info "═══ Step 2: Building AUR dependencies ═══"
|
||||
|
||||
# ── Theming & appearance ─────────────────────────────────────────────────────
|
||||
# Entries in these lists must start at column 0: CI's package check accepts
|
||||
# repo-less packages via `grep -qxF <pkg>` against this file (exact whole-line
|
||||
# match), which silently never matched while the names were indented.
|
||||
AUR_THEMING=(
|
||||
adw-gtk-theme-git
|
||||
darkly-bin
|
||||
ttf-material-symbols-variable-git
|
||||
ttf-readex-pro
|
||||
ttf-rubik-vf
|
||||
ttf-twemoji
|
||||
otf-space-grotesk
|
||||
kde-material-you-colors-git
|
||||
adw-gtk-theme-git
|
||||
darkly-bin
|
||||
ttf-material-symbols-variable-git
|
||||
ttf-readex-pro
|
||||
ttf-rubik-vf
|
||||
ttf-twemoji
|
||||
otf-space-grotesk
|
||||
kde-material-you-colors-git
|
||||
)
|
||||
|
||||
# ── end-4 specific / build-from-source packages ──────────────────────────────
|
||||
# matugen — material you color generation tool
|
||||
# speech-dispatcher — may need AUR build on some setups
|
||||
AUR_TOOLS=(
|
||||
matugen
|
||||
python-rpypostprocess
|
||||
tts
|
||||
matugen
|
||||
python-rpypostprocess
|
||||
tts
|
||||
)
|
||||
|
||||
# ── Packages not in any standard repo ────────────────────────────────────────
|
||||
AUR_MISSING=(
|
||||
asciinema-agg
|
||||
davs2
|
||||
syncthingtray-qt6
|
||||
varia
|
||||
woeusb-ng
|
||||
ttf-ms-fonts
|
||||
asciinema-agg
|
||||
davs2
|
||||
varia
|
||||
woeusb-ng
|
||||
ttf-ms-fonts
|
||||
)
|
||||
|
||||
# ── Large / complex builds (skipped with --quick) ────────────────────────────
|
||||
|
||||
Executable
+184
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verify that every quickshell pin we build against is current enough to
|
||||
# survive the next Arch Qt update.
|
||||
#
|
||||
# Why this exists: CI run 137 (2026-10-08) failed because end-4 pinned
|
||||
# quickshell to 41651d7 (Sep 25), which predates quickshell's Qt 6.12 fix
|
||||
# (5d5d498, Oct 3: "use MOC includes instead of opaque pointer definitions
|
||||
# - Fixes linking issues and Qt 6.12 compatibility"). Arch shipped
|
||||
# qt6 6.12.0 the next day and the pinned commit stopped compiling
|
||||
# ("Meta Types must be fully defined"). customize_airootfs.sh papers over
|
||||
# end-4's stale pin at build time; this script is the tripwire that tells
|
||||
# us when pins - ours OR end-4's - fall behind again BEFORE a build does.
|
||||
#
|
||||
# Checked pins:
|
||||
# 1. localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD (our own pin)
|
||||
# 2. end-4/dots-hyprland sdata/dist-arch PKGBUILD (live from GitHub)
|
||||
#
|
||||
# Checks per pin:
|
||||
# - commit exists upstream
|
||||
# - includes the Qt 6.12 fix (5d5d498)
|
||||
# - not older than QUICKSHELL_PIN_MAX_AGE_DAYS (default 45)
|
||||
#
|
||||
# Exit 1 on any failure so CI (pin-check.yaml) goes red.
|
||||
#
|
||||
# Usage: ./localrepo/check-quickshell-pin.sh
|
||||
set -euo pipefail
|
||||
|
||||
QT612_FIX='5d5d49873fe8cf1f99ddfd5006ceb2057c5c9b13'
|
||||
QS_REPO='https://git.outfoxxed.me/quickshell/quickshell.git'
|
||||
END4_STALE_PIN='41651d7' # the pin customize_airootfs.sh overrides
|
||||
END4_PKGBUILD_URL='https://raw.githubusercontent.com/end-4/dots-hyprland/main/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD'
|
||||
MAX_AGE_DAYS="${QUICKSHELL_PIN_MAX_AGE_DAYS:-45}"
|
||||
|
||||
profile_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
LOCAL_PKGBUILD="${profile_dir}/localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD"
|
||||
|
||||
FAILED=0
|
||||
|
||||
fail() {
|
||||
echo "❌ $1" >&2
|
||||
FAILED=1
|
||||
}
|
||||
|
||||
# ── collect pins ────────────────────────────────────────────────────────────
|
||||
|
||||
pin_of() { # <file-contents-stdin> -> first _commit='...' value
|
||||
sed -n "s/^_commit='\([0-9a-f]\{7,40\}\)'.*/\1/p" | head -n1
|
||||
}
|
||||
|
||||
if [[ ! -f "$LOCAL_PKGBUILD" ]]; then
|
||||
echo "❌ missing ${LOCAL_PKGBUILD}" >&2
|
||||
exit 1
|
||||
fi
|
||||
local_pin=$(pin_of < "$LOCAL_PKGBUILD")
|
||||
if [[ -z "$local_pin" ]]; then
|
||||
echo "❌ no _commit= found in ${LOCAL_PKGBUILD}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
end4_pin=''
|
||||
end4_pkgbuild=''
|
||||
for try in 1 2 3 4 5; do
|
||||
if end4_pkgbuild=$(curl -fsSL --max-time 30 "$END4_PKGBUILD_URL"); then
|
||||
end4_pin=$(printf '%s\n' "$end4_pkgbuild" | pin_of)
|
||||
break
|
||||
fi
|
||||
echo " ⏳ fetch of end-4 PKGBUILD failed (try $try/5), retrying in $(( try * 3 ))s..." >&2
|
||||
sleep $(( try * 3 ))
|
||||
done
|
||||
if [[ -z "$end4_pin" ]]; then
|
||||
fail "could not fetch end-4's quickshell PKGBUILD from GitHub — network or path change?"
|
||||
echo ""
|
||||
echo "Result: FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── clone quickshell (blobless: commits+trees only, fast) ───────────────────
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
cloned=0
|
||||
for try in 1 2 3; do
|
||||
if git clone --filter=blob:none --quiet "$QS_REPO" "$tmp/qs" 2>/dev/null; then
|
||||
cloned=1
|
||||
break
|
||||
fi
|
||||
rm -rf "$tmp/qs"
|
||||
echo " ⏳ clone of quickshell failed (try $try/3), retrying in $(( try * 5 ))s..." >&2
|
||||
sleep $(( try * 5 ))
|
||||
done
|
||||
if [[ "$cloned" -ne 1 ]]; then
|
||||
fail "could not clone ${QS_REPO} — network or repo moved?"
|
||||
echo ""
|
||||
echo "Result: FAILED"
|
||||
exit 1
|
||||
fi
|
||||
QS="$tmp/qs"
|
||||
qs_head=$(git -C "$QS" rev-parse HEAD)
|
||||
qs_head_date=$(git -C "$QS" show -s --format=%cs HEAD)
|
||||
echo "quickshell upstream master: ${qs_head:0:12} (${qs_head_date})"
|
||||
echo ""
|
||||
|
||||
now=$(date +%s)
|
||||
|
||||
# <label> <pin> <mode>: mode "hard" fails on any problem; mode "soft" only
|
||||
# warns, for pins we don't control (end-4's) — UNLESS the problem is one the
|
||||
# customize_airootfs.sh override does not cover, which always fails hard
|
||||
# because it would break the next ISO build.
|
||||
check_pin() {
|
||||
local label=$1 pin=$2 mode=$3
|
||||
local ts age behind bad=0
|
||||
|
||||
if ! git -C "$QS" cat-file -e "${pin}^{commit}" 2>/dev/null; then
|
||||
fail "$label: pin '$pin' does not exist in quickshell upstream"
|
||||
return
|
||||
fi
|
||||
|
||||
local has_fix=0
|
||||
if git -C "$QS" merge-base --is-ancestor "$QT612_FIX" "$pin"; then
|
||||
has_fix=1
|
||||
fi
|
||||
|
||||
ts=$(git -C "$QS" show -s --format=%ct "$pin")
|
||||
age=$(( (now - ts) / 86400 ))
|
||||
behind=$(git -C "$QS" rev-list --count "$pin..HEAD")
|
||||
|
||||
if [[ "$mode" == "hard" ]]; then
|
||||
if [[ "$has_fix" -eq 0 ]]; then
|
||||
fail "$label: pin '$pin' PREDATES the Qt 6.12 fix (${QT612_FIX:0:12}) — will not compile against Qt >= 6.12"
|
||||
bad=1
|
||||
fi
|
||||
if (( age > MAX_AGE_DAYS )); then
|
||||
fail "$label: pin is $age days old (> ${MAX_AGE_DAYS}d), $behind commit(s) behind upstream master — bump it"
|
||||
bad=1
|
||||
fi
|
||||
if [[ "$bad" -eq 0 ]]; then
|
||||
echo " ✅ $label: pin ${pin:0:12} includes the Qt 6.12 fix, age ${age}d (${behind} commit(s) behind master)"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
# soft mode: our build-time override compensates for this pin, so drift is
|
||||
# a heads-up — it cannot break the ISO build on its own.
|
||||
if [[ "$has_fix" -eq 0 ]]; then
|
||||
echo " ⚠️ $label: pin ${pin:0:12} predates the Qt 6.12 fix — covered by the customize_airootfs.sh override, but end-4 should bump it"
|
||||
else
|
||||
echo " ✅ $label: pin ${pin:0:12} includes the Qt 6.12 fix"
|
||||
fi
|
||||
if (( age > MAX_AGE_DAYS )); then
|
||||
echo " ⚠️ $label: pin is $age days old (> ${MAX_AGE_DAYS}d), $behind commit(s) behind master — consider bumping (not enforced, we don't own this pin)"
|
||||
else
|
||||
echo " ✅ $label: pin age ${age}d (${behind} commit(s) behind master), limit ${MAX_AGE_DAYS}d"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── our pin ─────────────────────────────────────────────────────────────────
|
||||
|
||||
echo "our pin (localrepo/end4-pkgbuilds):"
|
||||
check_pin "ours" "$local_pin" hard
|
||||
|
||||
# ── end-4's pin ─────────────────────────────────────────────────────────────
|
||||
|
||||
echo ""
|
||||
echo "end-4's pin (dots-hyprland sdata/dist-arch):"
|
||||
# end-4's pin is only safe if OUR override covers it: customize_airootfs.sh
|
||||
# rewrites exactly the 41651d7 pin. Any other Qt-unsafe end-4 pin sails
|
||||
# straight into the ISO build unpatched → hard failure.
|
||||
if [[ "$end4_pin" == "$END4_STALE_PIN"* ]]; then
|
||||
echo " ℹ️ end-4 still pins ${end4_pin:0:12} — customize_airootfs.sh override is active"
|
||||
check_pin "end-4" "$end4_pin" soft
|
||||
else
|
||||
check_pin "end-4" "$end4_pin" hard
|
||||
echo " ℹ️ end-4 has moved off ${END4_STALE_PIN} to ${end4_pin:0:12}"
|
||||
echo " → if it now passes, consider retiring the override guard in"
|
||||
echo " customize_airootfs.sh and syncing our own pin to match."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ "$FAILED" -ne 0 ]]; then
|
||||
echo "Result: FAILED"
|
||||
exit 1
|
||||
fi
|
||||
echo "Result: OK — all quickshell pins are Qt 6.12-safe and within ${MAX_AGE_DAYS}d of upstream"
|
||||
@@ -18,6 +18,8 @@ depends=(
|
||||
makedepends=("git" "cmake")
|
||||
source=("git+${url}.git")
|
||||
sha256sums=("SKIP")
|
||||
# -O3 + LTO (CachyOS makepkg.conf) make GCC 16.2 ICE on gtkmm_main.cpp
|
||||
options=(!lto)
|
||||
|
||||
prepare() {
|
||||
cd $_pkgname
|
||||
@@ -26,6 +28,7 @@ prepare() {
|
||||
}
|
||||
|
||||
build() {
|
||||
export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"
|
||||
cd $_pkgname
|
||||
cmake -B build -S . -DCMAKE_BUILD_TYPE=None
|
||||
cmake --build build
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_commit='7511545ee20664e3b8b8d3322c0ffe7567c56f7a'
|
||||
_commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'
|
||||
# Useful links:
|
||||
# https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master
|
||||
# https://aur.archlinux.org/packages/quickshell-git
|
||||
@@ -8,7 +8,7 @@ _prefix='illogical-impulse'
|
||||
_pkgname=quickshell
|
||||
pkgname="$_prefix-$_pkgname-git"
|
||||
pkgver=0.1.0.r1
|
||||
pkgrel=8
|
||||
pkgrel=9
|
||||
pkgdesc="$_pkgname-git pinned commit and extra deps for $_prefix"
|
||||
arch=(x86_64 aarch64)
|
||||
url='https://git.outfoxxed.me/quickshell/quickshell'
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -22,11 +22,27 @@ mapfile -t pkgs < <(find "${repo_dir}" -maxdepth 1 -name '*.pkg.tar.zst' -print
|
||||
|
||||
if [[ ${#pkgs[@]} -eq 0 ]]; then
|
||||
echo "No .pkg.tar.zst files in ${repo_dir}" >&2
|
||||
# keep the medium's copy honest too - an emptied localrepo/ must not
|
||||
# leave the previous packages shipping on the next build
|
||||
rm -rf "${profile_dir}/airootfs/opt/localrepo"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
repo-add -R "${repo_dir}/${repo_name}.db.tar.gz" "${pkgs[@]}"
|
||||
|
||||
# Sync the medium's copy: the ISO ships its own [local-aur] at
|
||||
# airootfs/opt/localrepo (pacman.conf on the live system points there, and
|
||||
# pacstrap resolves these packages from it at install time).
|
||||
img_repo="${profile_dir}/airootfs/opt/localrepo"
|
||||
mkdir -p "${img_repo}"
|
||||
rm -f "${img_repo}"/*.pkg.tar.zst "${img_repo}/${repo_name}".db \
|
||||
"${img_repo}/${repo_name}".db.tar.gz "${img_repo}/${repo_name}".files \
|
||||
"${img_repo}/${repo_name}".files.tar.gz
|
||||
cp -f "${pkgs[@]}" "${img_repo}/"
|
||||
# explicit names: a .db* glob would also ship repo-add's *.tar.gz.old backups
|
||||
cp -fL "${repo_dir}/${repo_name}".{db,db.tar.gz,files,files.tar.gz} "${img_repo}/"
|
||||
echo "Medium copy ready: ${img_repo}"
|
||||
|
||||
new_names=()
|
||||
for p in "${pkgs[@]}"; do
|
||||
name=$(tar -xOf "${p}" .PKGINFO | sed -n 's/^pkgname = //p' | head -1)
|
||||
|
||||
+40
-25
@@ -3,8 +3,8 @@ alsa-utils
|
||||
amd-ucode
|
||||
ansible
|
||||
archinstall
|
||||
archlinux-keyring
|
||||
arch-install-scripts
|
||||
archlinux-keyring
|
||||
asciinema
|
||||
asciinema-agg
|
||||
ast-grep
|
||||
@@ -24,20 +24,21 @@ bolt
|
||||
brightnessctl
|
||||
brltty
|
||||
btop
|
||||
bullet
|
||||
btrfs-progs
|
||||
bullet
|
||||
bully
|
||||
cachyos-keyring
|
||||
cachyos-mirrorlist
|
||||
catppuccin-cursors-macchiato
|
||||
cava
|
||||
celt
|
||||
chaotic-keyring
|
||||
chaotic-mirrorlist
|
||||
cifs-utils
|
||||
clamav
|
||||
cliphist
|
||||
clonezilla
|
||||
cloud-init
|
||||
cliphist
|
||||
cmake
|
||||
cmatrix
|
||||
code
|
||||
@@ -46,16 +47,17 @@ cpio
|
||||
cronie
|
||||
cryptsetup
|
||||
cups
|
||||
curl
|
||||
darkhttpd
|
||||
davs2
|
||||
dconf-editor
|
||||
ddcutil
|
||||
ddrescue
|
||||
dhcpcd
|
||||
dialog
|
||||
diffutils
|
||||
dmidecode
|
||||
dmraid
|
||||
ddcutil
|
||||
dnsmasq
|
||||
dolphin-plugins
|
||||
dosfstools
|
||||
@@ -67,14 +69,12 @@ efibootmgr
|
||||
electron
|
||||
element-desktop
|
||||
espeakup
|
||||
eternalterminal
|
||||
ethtool
|
||||
exfatprogs
|
||||
f2fs-tools
|
||||
faac
|
||||
faad2
|
||||
fish
|
||||
flac
|
||||
fontconfig
|
||||
fastfetch
|
||||
fatresize
|
||||
fcitx5
|
||||
@@ -87,8 +87,11 @@ ffmpegthumbnailer
|
||||
ffmpegthumbs
|
||||
figlet
|
||||
firefox
|
||||
fish
|
||||
flac
|
||||
flatpak
|
||||
foliate
|
||||
fontconfig
|
||||
foot-terminfo
|
||||
foremost
|
||||
fortune-mod
|
||||
@@ -96,22 +99,23 @@ fprintd
|
||||
fsarchiver
|
||||
fuse-overlayfs
|
||||
fzf
|
||||
geoclue
|
||||
ghostty
|
||||
ghostty-terminfo
|
||||
git
|
||||
geoclue
|
||||
glib2-devel
|
||||
gnome-disk-utility
|
||||
gnome-font-viewer
|
||||
gnome-online-accounts
|
||||
gnome-power-manager
|
||||
go-yq
|
||||
gpart
|
||||
gparted
|
||||
go-yq
|
||||
gpm
|
||||
gptfdisk
|
||||
grml-zsh-config
|
||||
grub
|
||||
grub-customizer
|
||||
gst-plugins-base
|
||||
gst-plugins-ugly
|
||||
gst-plugin-va
|
||||
@@ -129,11 +133,12 @@ htop
|
||||
hyperv
|
||||
hypridle
|
||||
hyprlock
|
||||
hyprpolkitagent
|
||||
hyprpicker
|
||||
hyprpolkitagent
|
||||
hyprshot
|
||||
hyprsunset
|
||||
icu
|
||||
illogical-impulse-bibata-modern-classic-bin
|
||||
intel-ucode
|
||||
inxi
|
||||
irssi
|
||||
@@ -162,21 +167,20 @@ kwalletmanager
|
||||
lazygit
|
||||
ldns
|
||||
less
|
||||
lftp
|
||||
libass
|
||||
libdbusmenu-gtk3
|
||||
libdecor
|
||||
libevdev
|
||||
libportal
|
||||
libportal-gtk4
|
||||
libqalculate
|
||||
libpipewire
|
||||
lftp
|
||||
libfido2
|
||||
libfprint
|
||||
libisoburn
|
||||
libpipewire
|
||||
libportal
|
||||
libportal-gtk4
|
||||
libqalculate
|
||||
libtorrent
|
||||
libusb-compat
|
||||
linux
|
||||
linux-atm
|
||||
linux-cachyos
|
||||
linux-cachyos-headers
|
||||
@@ -213,14 +217,14 @@ mplayer
|
||||
mpv-mpris
|
||||
mtools
|
||||
nano
|
||||
ncurses
|
||||
nbd
|
||||
ncurses
|
||||
ndisc6
|
||||
neofetch
|
||||
neovim
|
||||
net-tools
|
||||
network-manager-applet
|
||||
networkmanager
|
||||
network-manager-applet
|
||||
nfs-utils
|
||||
nikto
|
||||
nilfs-utils
|
||||
@@ -267,22 +271,23 @@ ppp
|
||||
pptpclient
|
||||
pv
|
||||
pwgen
|
||||
python-aiohttp
|
||||
python-distro
|
||||
python-hatchling
|
||||
python-installer
|
||||
python-packaging
|
||||
python-pip
|
||||
python-pyqt6
|
||||
python-pyquery
|
||||
python-ufonormalizer
|
||||
python-uv
|
||||
python-virtualenv
|
||||
python-aiohttp
|
||||
python-packaging
|
||||
qalculate-gtk
|
||||
qemu-full
|
||||
qemu-guest-agent
|
||||
qt5-quickcontrols2
|
||||
qt6ct
|
||||
qt6-5compat
|
||||
qt6ct
|
||||
qt6-imageformats
|
||||
qt6-multimedia
|
||||
qt6-positioning
|
||||
@@ -295,6 +300,7 @@ qt6-wayland
|
||||
rclone
|
||||
refind
|
||||
reflector
|
||||
rpi-imager-git
|
||||
rsync
|
||||
rxvt-unicode-terminfo
|
||||
samba
|
||||
@@ -312,17 +318,19 @@ shelly
|
||||
shelly-flatpak-backend
|
||||
shfmt
|
||||
smartmontools
|
||||
snapd
|
||||
sof-firmware
|
||||
sox
|
||||
songrec
|
||||
sox
|
||||
speech-dispatcher
|
||||
squashfs-tools
|
||||
sshfs
|
||||
ssh-import-id
|
||||
starship
|
||||
sudo
|
||||
swaync
|
||||
syncthing
|
||||
syncthingtray-qt6
|
||||
syncthingtray
|
||||
syntax-highlighting
|
||||
syslinux
|
||||
systemd-resolvconf
|
||||
@@ -357,6 +365,7 @@ ttf-material-symbols-variable-git
|
||||
ttf-ms-fonts
|
||||
ttf-readex-pro
|
||||
ttf-twemoji
|
||||
ttf-victor-mono
|
||||
tty-clock
|
||||
tumbler
|
||||
udftools
|
||||
@@ -364,6 +373,7 @@ uget
|
||||
umockdev
|
||||
unrar
|
||||
upower
|
||||
upscayl-bin
|
||||
uriparser
|
||||
usbguard
|
||||
usb_modeswitch
|
||||
@@ -377,6 +387,10 @@ vim
|
||||
virt-manager
|
||||
virtualbox-guest-utils-nox
|
||||
vpnc
|
||||
vulkan-intel
|
||||
vulkan-nouveau
|
||||
vulkan-radeon
|
||||
wallust
|
||||
wayland-protocols
|
||||
wine
|
||||
winetricks
|
||||
@@ -384,15 +398,16 @@ wireless-regdb
|
||||
wireless_tools
|
||||
woeusb-ng
|
||||
wpa_supplicant
|
||||
wvdial
|
||||
wtype
|
||||
wvdial
|
||||
xarchiver
|
||||
xclip
|
||||
xdg-utils
|
||||
xdg-user-dirs
|
||||
xdg-utils
|
||||
xdotool
|
||||
xfsprogs
|
||||
xl2tpd
|
||||
xwaylandvideobridge
|
||||
yay
|
||||
ydotool
|
||||
yt-dlp
|
||||
|
||||
+1
-3
@@ -76,7 +76,6 @@ LocalFileSigLevel = Optional
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[cachyos]
|
||||
SigLevel = Optional TrustAll
|
||||
Include = /etc/pacman.d/cachyos-mirrorlist
|
||||
|
||||
[core]
|
||||
@@ -98,12 +97,11 @@ Include = /etc/pacman.d/mirrorlist
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[chaotic-aur]
|
||||
SigLevel = Optional TrustAll
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
|
||||
[local-aur]
|
||||
SigLevel = Optional TrustAll
|
||||
Server = file:///home/alvin/alvinarch/localrepo
|
||||
Server = file:///home/alvin/AlvinArch/localrepo
|
||||
|
||||
# An example of a custom package repository. See the pacman manpage for
|
||||
# tips on creating your own repositories.
|
||||
|
||||
@@ -20,6 +20,37 @@ file_permissions=(
|
||||
["/root/.automated_script.sh"]="0:0:755"
|
||||
["/root/.gnupg"]="0:0:700"
|
||||
["/usr/local/bin/choose-mirror"]="0:0:755"
|
||||
["/usr/local/bin/alvin-install"]="0:0:755"
|
||||
["/usr/local/bin/Installation_guide"]="0:0:755"
|
||||
["/usr/local/bin/livecd-sound"]="0:0:755"
|
||||
)
|
||||
|
||||
# ── Build stamp (staged file, never committed) ────────────────────────────────
|
||||
# airootfs/root/.iso-build-stamp lands at /root/.iso-build-stamp on the ISO;
|
||||
# customize_airootfs.sh keeps it as /etc/alvin-build-stamp and appends it to
|
||||
# the live medium's /etc/motd (the installed system's motd is left alone).
|
||||
# Only stamp builds from a git
|
||||
# checkout or CI — a random source-tarball build would record a bogus commit.
|
||||
# Sourced by mkarchiso under `set -eu`, hence every expansion is defaulted.
|
||||
_stamp_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
_stamp_file="${_stamp_dir}/airootfs/root/.iso-build-stamp"
|
||||
rm -f "${_stamp_file}"
|
||||
if [[ -e "${_stamp_dir}/.git" || -n "${CI:-}" || -n "${GITEA_ACTIONS:-}" ]]; then
|
||||
_stamp_build="$(date --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y-%m-%d)"
|
||||
# git ≥2.46 honours -c safe.directory even when mkarchiso runs as root
|
||||
# over a user-owned checkout; older gits ignore it (protected-config
|
||||
# scope) and fall through to the "unknown" line below instead of failing
|
||||
# the build (`|| true` keeps set -e happy either way).
|
||||
_stamp_commit="$(git -C "${_stamp_dir}" -c safe.directory="${_stamp_dir}" \
|
||||
log -1 --format='%cs|%h' 2>/dev/null || true)"
|
||||
if [[ -n "${_stamp_commit}" && "${_stamp_commit}" == *"|"* ]]; then
|
||||
_stamp_hash="${_stamp_commit##*|}"
|
||||
printf 'AlvinArch build %s | commit %s | %s\n' \
|
||||
"${_stamp_build}" "${_stamp_commit%%|*}" "${_stamp_hash:0:4}" \
|
||||
> "${_stamp_file}"
|
||||
else
|
||||
printf 'AlvinArch build %s | commit unknown\n' \
|
||||
"${_stamp_build}" > "${_stamp_file}"
|
||||
fi
|
||||
fi
|
||||
unset _stamp_dir _stamp_file _stamp_build _stamp_commit _stamp_hash
|
||||
|
||||
@@ -4,8 +4,8 @@ Boot the Arch Linux install medium using NBD.
|
||||
It allows you to install Arch Linux or perform system maintenance.
|
||||
ENDTEXT
|
||||
MENU LABEL Arch Linux install medium (%ARCH%, NBD)
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% archiso_nbd_srv=${pxeserver} cms_verify=y
|
||||
SYSAPPEND 3
|
||||
|
||||
@@ -15,8 +15,8 @@ Boot the Arch Linux live medium using NFS.
|
||||
It allows you to install Arch Linux or perform system maintenance.
|
||||
ENDTEXT
|
||||
MENU LABEL Arch Linux install medium (%ARCH%, NFS)
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
APPEND archisobasedir=%INSTALL_DIR% archiso_nfs_srv=${pxeserver}:/run/archiso/bootmnt cms_verify=y
|
||||
SYSAPPEND 3
|
||||
|
||||
@@ -26,7 +26,7 @@ Boot the Arch Linux live medium using HTTP.
|
||||
It allows you to install Arch Linux or perform system maintenance.
|
||||
ENDTEXT
|
||||
MENU LABEL Arch Linux install medium (%ARCH%, HTTP)
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
APPEND archisobasedir=%INSTALL_DIR% archiso_http_srv=http://${pxeserver}/ cms_verify=y
|
||||
SYSAPPEND 3
|
||||
|
||||
@@ -4,8 +4,8 @@ Boot the Arch Linux install medium on BIOS.
|
||||
It allows you to install Arch Linux or perform system maintenance.
|
||||
ENDTEXT
|
||||
MENU LABEL Arch Linux install medium (%ARCH%, BIOS)
|
||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||
|
||||
# Accessibility boot option
|
||||
@@ -15,6 +15,6 @@ Boot the Arch Linux install medium on BIOS with speakup screen reader.
|
||||
It allows you to install Arch Linux or perform system maintenance with speech feedback.
|
||||
ENDTEXT
|
||||
MENU LABEL Arch Linux install medium (%ARCH%, BIOS) with ^speech
|
||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-cachyos
|
||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-cachyos.img
|
||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||
|
||||
Reference in New Issue
Block a user