diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td new file mode 100644 index 0000000..188239c --- /dev/null +++ b/.github/VOUCHED.td @@ -0,0 +1,27 @@ +# Trust list for PR builds. +# +# A pull request only builds packages (and spins up builder droplets) when +# its author is trusted: repository collaborators are trusted automatically +# and do not need listing; external contributors listed here are trusted +# too. Anyone else gets the plan only, until a maintainer either adds them +# here or applies the "build-approved" label to that one PR. The label also +# releases GitHub's approval hold for that PR's build and test workflows. +# It remains effective while attached, without vouching for the author's +# other PRs. An explicit denouncement cannot be overridden by the label. +# +# Syntax: +# github:username +# -github:username reason for denouncement +# +# Keep entries sorted alphabetically. +github:bjarneo +github:DanWahlin +github:dhh +github:f-trycua +github:HANCORE-linux +github:kwilczynski +github:ryanrhughes +github:scottjones +github:spencerbull +github:tcballard +github:tobi diff --git a/.github/scripts/approve-pr-workflows.cjs b/.github/scripts/approve-pr-workflows.cjs new file mode 100644 index 0000000..0ee32b3 --- /dev/null +++ b/.github/scripts/approve-pr-workflows.cjs @@ -0,0 +1,78 @@ +const BUILD = '.github/workflows/build-pr.yml'; +const TESTS = '.github/workflows/test.yml'; + +module.exports = async function approve({ github, context, core, vouchStatus, + sleep = ms => new Promise(resolve => setTimeout(resolve, ms)), attempts = 36 }) { + // Missing/failed vouch lookups must not become approval. Denouncements + // remain absolute, just as they are in the package build gate. + if (!['unknown', 'bot', 'collaborator', 'vouched'].includes(vouchStatus)) { + throw new Error(`Cannot approve workflows: vouch status is ${vouchStatus || 'missing'}.`); + } + + const expected = context.payload.pull_request; + const eventTime = Date.parse(expected.updated_at); + if (!Number.isFinite(eventTime)) throw new Error('Missing PR event timestamp.'); + const approved = new Set(); + let precedingBuild; + + const stillApproved = async () => { + const { data: pr } = await github.rest.pulls.get({ + ...context.repo, pull_number: expected.number, + }); + return pr.state === 'open' && pr.head.sha === expected.head.sha && + pr.labels.some(label => label.name === 'build-approved'); + }; + + // The label and PR-run events arrive independently. Wait for the build + // belonging to this event, rather than returning after approving an older + // run and leaving the new label-triggered run stuck behind GitHub's gate. + for (let attempt = 0; attempt < attempts; attempt++) { + if (attempt) await sleep(5000); + if (!await stillApproved()) { + core.info('PR closed, head changed, or build-approved removed; stopping.'); + return; + } + + const all = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + ...context.repo, event: 'pull_request', head_sha: expected.head.sha, per_page: 100, + }); + const runs = all.filter(run => + run.event === 'pull_request' && run.head_sha === expected.head.sha && + run.head_repository?.id === expected.head.repo.id && run.head_branch === expected.head.ref && + [BUILD, TESTS].includes(run.path) && + // Fork runs awaiting approval often have no pull_requests entries. + (!run.pull_requests?.length || run.pull_requests.some(pr => pr.number === expected.number)) + ).sort((a, b) => a.id - b.id); + + const newestBuild = runs.findLast(run => run.path === BUILD); + if (!newestBuild || !(Date.parse(newestBuild.created_at) >= eventTime) || + !runs.some(run => run.path === TESTS && + (context.payload.action === 'labeled' || Date.parse(run.created_at) >= eventTime))) continue; + + if (precedingBuild) { + const { data: run } = await github.rest.actions.getWorkflowRun({ + ...context.repo, run_id: precedingBuild, + }); + // Approve older builds first, and let them acquire concurrency before + // releasing a newer build. Otherwise an older queued run could start + // last and cancel the label-triggered build that carries approval. + if (!['in_progress', 'completed'].includes(run.status) || run.conclusion === 'action_required') continue; + precedingBuild = undefined; + } + + const pending = runs.filter(run => run.conclusion === 'action_required' && !approved.has(run.id) && + // If the newest build already runs (e.g. a maintainer approved it), + // don't resurrect an obsolete hold that could cancel that newer run. + (run.path !== BUILD || run.id === newestBuild.id || newestBuild.conclusion === 'action_required')); + if (!pending.length) return; + const run = pending[0]; + // Recheck after the API reads, immediately before exercising write access. + if (!await stillApproved()) return; + await github.rest.actions.approveWorkflowRun({ ...context.repo, run_id: run.id }); + approved.add(run.id); + core.info(`Approved ${run.path} run ${run.id} for PR #${expected.number}.`); + if (run.path === BUILD) precedingBuild = run.id; + if (pending.length === 1) return; + } + throw new Error('Timed out waiting for PR workflows. Remove and reapply build-approved to retry.'); +}; diff --git a/.github/workflows/approve-pr.yml b/.github/workflows/approve-pr.yml new file mode 100644 index 0000000..996fdc9 --- /dev/null +++ b/.github/workflows/approve-pr.yml @@ -0,0 +1,46 @@ +name: Approve PR workflows + +# A pull_request workflow cannot approve itself: GitHub can hold it before +# any job starts. This workflow only runs trusted default-branch code and +# releases the ordinary, unprivileged PR workflows after build approval. +on: + pull_request_target: + types: [opened, synchronize, reopened, labeled] + +permissions: + contents: read + pull-requests: read + actions: write + +concurrency: + group: approve-pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + approve: + # Match build-pr.yml's events, including other labels applied while this + # PR still carries build-approved: each labeled event creates a build. + if: contains(github.event.pull_request.labels.*.name, 'build-approved') + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + # Never check out the PR head or its merge ref with this write token. + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + persist-credentials: false + - id: vouch + uses: mitchellh/vouch/action/check-user@f23dbb5e745334f97414ec70463ce7301071a661 # v1 + with: + user: ${{ github.event.pull_request.user.login }} + allow-fail: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Approve this PR's pending build and test runs + uses: actions/github-script@v7 + env: + VOUCH_STATUS: ${{ steps.vouch.outputs.status }} + with: + script: | + const approve = require('./.github/scripts/approve-pr-workflows.cjs'); + await approve({ github, context, core, vouchStatus: process.env.VOUCH_STATUS }); diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml new file mode 100644 index 0000000..ab0bec4 --- /dev/null +++ b/.github/workflows/build-pr.yml @@ -0,0 +1,254 @@ +name: Build changed packages + +# Build every package directory a PR touches, one job per package per arch, on +# the self-hosted droplet pool. Artifacts are unsigned; publish.yml signs and +# publishes them on merge. +# +# Tooling runs from the base branch; a PR supplies only pkgbuilds/. The +# vouch gate limits who may spend compute; this limits what their PR can run. + +# No paths filter: approved PRs must report the required `result` even when +# no package directory changed. Those PRs get an empty matrix and a passing +# result in seconds; unapproved PRs wait for maintainer approval. +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + workflow_dispatch: + inputs: + packages: + description: "Space-separated package directories to build" + required: true + +concurrency: + group: build-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + # Builds cost real machines, so they run only for trusted authors: + # collaborators, anyone in .github/VOUCHED.td (read from the default + # branch, so a PR cannot vouch for itself), or a PR a maintainer has + # labelled "build-approved". Everyone else gets this job's plan output + # while the required `result` stays pending until a maintainer approves. + changes: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.list.outputs.matrix }} + count: ${{ steps.gate.outputs.count }} + trusted: ${{ steps.gate.outputs.trusted }} + vouch_status: ${{ steps.vouch.outputs.status }} + empty: ${{ steps.list.outputs.empty }} + steps: + # Same rule as the build job: bin/build-matrix comes from the base + # branch tip, the package directories from the PR head. + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref || github.sha }} + fetch-depth: 0 + persist-credentials: false + - if: github.event_name == 'pull_request' + run: | + git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.head.sha }}" + git checkout "${{ github.event.pull_request.head.sha }}" -- pkgbuilds/ + # Bootstrap: the PR that introduces this tooling has a base without + # it. Take the plan helper from the PR head in that one case; it + # runs on a hosted runner and only prints a plan. + if [[ ! -x bin/build-matrix ]]; then + git checkout "${{ github.event.pull_request.head.sha }}" -- bin/build-matrix helpers/ + echo "::notice::base branch has no bin/build-matrix; using the PR's copy for planning" + fi + - id: vouch + if: github.event_name == 'pull_request' + uses: mitchellh/vouch/action/check-user@f23dbb5e745334f97414ec70463ce7301071a661 # v1 + with: + user: ${{ github.event.pull_request.user.login }} + allow-fail: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - id: approval + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const { data: pr } = await github.rest.pulls.get({ + ...context.repo, pull_number: context.payload.pull_request.number, + }); + // Approving or rerunning a held run keeps its original event, + // which may predate the label. Read the current approval instead. + core.setOutput('approved', pr.state === 'open' && + pr.head.sha === context.payload.pull_request.head.sha && + pr.labels.some(label => label.name === 'build-approved')); + # One matrix entry per package per architecture. Every package builds + # once, against edge; the channels it ships to on merge are carried + # along for information. A filename means one set of bytes. + - id: list + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [[ -n "${{ github.event.inputs.packages }}" ]]; then + names="${{ github.event.inputs.packages }}" + else + names=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- pkgbuilds \ + | awk -F/ '$1=="pkgbuilds" && NF>2 {print $2}' | sort -u) + fi + matrix=$(printf '%s\n' $names | bin/build-matrix) + # A package directory whose exact tree already has a build artifact + # (label --, uploaded only after a successful + # build) is not built again. Pushing a fix for one package to a PR + # that touches fifty rebuilds one, not fifty; publish.yml finds the + # same artifacts on merge. workflow_dispatch is an explicit request + # and always builds. + if [[ "${{ github.event_name }}" == pull_request ]]; then + head="${{ github.event.pull_request.head.sha }}" + kept=(); reused=() + while read -r entry; do + package=$(jq -r .package <<<"$entry"); arch=$(jq -r .arch <<<"$entry") + label="$package-$arch-$(git rev-parse "$head:pkgbuilds/$package")" + found=$(curl -fsS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=$label&per_page=5" \ + | jq -r '[.artifacts[] | select(.expired|not)] | length' || echo 0) + if (( found > 0 )); then reused+=("$label"); else kept+=("$entry"); fi + done < <(jq -c '.include[]' <<<"$matrix") + matrix=$(printf '%s\n' "${kept[@]}" | jq -sc '{include: .}') + if (( ${#reused[@]} )); then + printf '==> already built, reusing the artifact: %s\n' "${reused[@]}" + { echo "Reused existing build artifacts (${#reused[@]}):"; printf -- '- %s\n' "${reused[@]}"; } >> "$GITHUB_STEP_SUMMARY" + fi + fi + echo "matrix=$matrix" >> "$GITHUB_OUTPUT" + echo "planned=$(jq '.include | length' <<<"$matrix")" >> "$GITHUB_OUTPUT" + jq -r '.include[] | "\(.package) \(.arch) -> \(.channels)"' <<<"$matrix" + # A PR whose diff against its base is empty changes nothing: its + # content already landed some other way (a sync PR beat it, or a + # merge from master swallowed it). Merging it would record a change + # that isn't one. Flag it so `result` fails rather than passes. + if [[ "${{ github.event_name }}" == pull_request ]]; then + total=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | wc -l) + echo "empty=$([[ $total -eq 0 ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "files changed vs base: $total" + else + echo "empty=false" >> "$GITHUB_OUTPUT" + fi + - id: gate + env: + STATUS: ${{ github.event_name == 'workflow_dispatch' && 'dispatch' || steps.vouch.outputs.status }} + AUTHOR: ${{ github.event.pull_request.user.login }} + APPROVED: ${{ steps.approval.outputs.approved || 'false' }} + PLANNED: ${{ steps.list.outputs.planned }} + run: | + case "$STATUS" in + bot|collaborator|vouched|dispatch) trusted=true ;; + # A denouncement is absolute: the label cannot override it. + denounced) trusted=false ;; + unknown) trusted=$APPROVED ;; + *) trusted=false ;; + esac + echo "trusted=$trusted" >> "$GITHUB_OUTPUT" + if [[ $trusted == true ]]; then + echo "count=$PLANNED" >> "$GITHUB_OUTPUT" + echo "Author $AUTHOR is trusted ($STATUS); building $PLANNED package(s)." + else + echo "count=0" >> "$GITHUB_OUTPUT" + echo "::warning::Author $AUTHOR is not trusted ($STATUS). $PLANNED package build(s) planned but not run." + if [[ $STATUS == denounced ]]; then + echo "::warning::The author is denounced in .github/VOUCHED.td; the build-approved label does not apply." + else + echo "::warning::A maintainer can add the author to .github/VOUCHED.td, or apply the 'build-approved' label to build this PR." + fi + fi + + build: + needs: changes + if: needs.changes.outputs.count != '0' + runs-on: [self-hosted, omarchy-builder] + timeout-minutes: 180 + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.changes.outputs.matrix) }} + steps: + # Tooling from base: everything that executes on this droplet's host + # (bin/, helpers/, build/) comes from the base branch. Only the PR's + # package directories are overlaid. A PR can therefore change what + # gets built, never how the runner builds it. A PR that changes both + # tooling and a package builds the package with the OLD tooling; land + # the tooling first. workflow_dispatch has no PR and runs as checked out. + # The base branch tip, not the event's base.sha: that sha is a snapshot + # taken at the PR's last push, so a tooling fix on master would never + # reach an open PR until someone pushed to it (seen on the daily sync + # PR after the artifact packing fix landed). + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref || github.sha }} + persist-credentials: false + - name: Overlay the PR's package directories onto base tooling + if: github.event_name == 'pull_request' + run: | + set -euo pipefail + git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.head.sha }}" + git checkout "${{ github.event.pull_request.head.sha }}" -- pkgbuilds/ + echo "tooling: $(git rev-parse --short HEAD) (base) packages: ${{ github.event.pull_request.head.sha }} (PR head)" + git status --short | head + - name: Build ${{ matrix.package }} (${{ matrix.arch }}, ships to ${{ matrix.channels }}) + id: build + env: + CONTAINER_ENGINE: docker + run: bin/build --mirror edge --arch ${{ matrix.arch }} --package ${{ matrix.package }} + # The artifact label carries the package directory's git tree hash so + # the publish step can find the build for exactly the tree that merged. + # The package file inside keeps makepkg's standard name untouched. + # The artifact label uses the PR head's tree for this package: that is + # the tree that merges, and what publish looks up. + - name: Tree hash + id: tree + run: echo "hash=$(git rev-parse "${{ github.event.pull_request.head.sha || github.sha }}:pkgbuilds/${{ matrix.package }}")" >> "$GITHUB_OUTPUT" + # The upload action rejects a path containing ':', which is how makepkg + # names a package with an epoch. The files ride inside packages.tar + # (helpers/artifact-helpers.sh); publish.yml unpacks it. Only a + # successful build uploads: the artifact's existence is what lets the + # planner above and publish.yml skip rebuilding this exact tree. + - name: Pack artifact + id: pack + run: | + source helpers/artifact-helpers.sh + pack_packages build-output/edge/${{ matrix.arch }} packages.tar + tar -tvf packages.tar + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.package }}-${{ matrix.arch }}-${{ steps.tree.outputs.hash }} + path: packages.tar + if-no-files-found: error + retention-days: 7 + + # `result` is required by branch protection. An unvouched author awaiting + # approval gets a differently named informational check, leaving `result` + # unreported (pending). Skipping or passing a job named `result` would count + # as satisfying the requirement even though no build was authorized. + # Actual planning/build failures and denouncements still report `result`. + result: + name: ${{ needs.changes.result == 'success' && needs.changes.outputs.trusted == 'false' && needs.changes.outputs.vouch_status == 'unknown' && needs.changes.outputs.empty == 'false' && 'Awaiting build approval' || 'result' }} + needs: [changes, build] + if: always() + runs-on: ubuntu-latest + steps: + - run: | + echo "trusted=${{ needs.changes.outputs.trusted }} build=${{ needs.build.result }}" + if [[ "${{ needs.changes.result }}" != "success" ]]; then + echo "::error::Build planning or the trust check failed. See the changes job." + exit 1 + fi + # Nothing to merge: the PR's diff against its base is empty. Its + # change already landed elsewhere. Close it rather than merge it. + if [[ "${{ needs.changes.outputs.empty }}" == "true" ]]; then + echo "::error::This PR changes no files relative to its base. Its content is already on the target branch; close it instead of merging." + exit 1 + fi + if [[ "${{ needs.changes.outputs.trusted }}" == "false" && "${{ needs.changes.outputs.vouch_status }}" == "unknown" && "${{ needs.changes.outputs.empty }}" == "false" ]]; then + echo "::notice::Awaiting maintainer build approval. Apply 'build-approved' to this PR or vouch for the author in .github/VOUCHED.td." + echo "Package builds are waiting for maintainer approval. Apply **build-approved** to this PR to start them. The required **result** check remains pending." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + if [[ "${{ needs.changes.outputs.trusted }}" != "true" ]]; then + echo "::error::Builds are blocked: the author is denounced or the trust result is invalid. The build-approved label cannot override this." + exit 1 + fi + [[ "${{ needs.build.result }}" == "success" || "${{ needs.build.result }}" == "skipped" ]] diff --git a/.github/workflows/builder-images.yml b/.github/workflows/builder-images.yml new file mode 100644 index 0000000..db2bb08 --- /dev/null +++ b/.github/workflows/builder-images.yml @@ -0,0 +1,118 @@ +name: Refresh builder images + +on: + schedule: + - cron: '23 4 * * *' + push: + branches: [master] + paths: + - build/** + - bin/builder-image + - helpers/paths.sh + - helpers/docker-helpers.sh + - tests/build-isolation.sh + - .github/workflows/builder-images.yml + workflow_dispatch: + pull_request: + paths: + - build/** + - bin/builder-image + - helpers/paths.sh + - helpers/docker-helpers.sh + - tests/build-isolation.sh + - .github/workflows/builder-images.yml + +# Complete each refresh before another can replace its tested image tags. +concurrency: + group: builder-images-${{ github.event.pull_request.number || 'master' }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + # Exercise proposed image changes on native runners with a read-only token. + # Publishing is a separate master-only job with its own write permission. + validate: + if: github.event_name == 'pull_request' + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + env: + CONTAINER_ENGINE: docker + CANDIDATE_IMAGE: omarchy-builder-candidate:${{ matrix.arch }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Build a fresh environment + run: bin/builder-image build --arch "${{ matrix.arch }}" --mirror edge --tag "$CANDIDATE_IMAGE" --fresh + - name: Test isolated package builds + env: + TEST_BUILDER_IMAGE: ${{ env.CANDIDATE_IMAGE }} + run: tests/build-isolation.sh + + refresh: + if: github.repository == 'omacom/omarchy-pkgs' && github.ref == 'refs/heads/master' + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + permissions: + contents: read + packages: write + env: + CONTAINER_ENGINE: docker + REGISTRY_IMAGE: ghcr.io/omacom/omarchy-pkg-builder + CANDIDATE_IMAGE: omarchy-builder-candidate:${{ matrix.arch }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Build a fresh environment + run: bin/builder-image build --arch "${{ matrix.arch }}" --mirror edge --tag "$CANDIDATE_IMAGE" --fresh + - name: Test isolated package builds + env: + TEST_BUILDER_IMAGE: ${{ env.CANDIDATE_IMAGE }} + run: tests/build-isolation.sh + - name: Publish tested image + env: + GH_TOKEN: ${{ github.token }} + GH_ACTOR: ${{ github.actor }} + DOCKER_CONFIG: ${{ runner.temp }}/builder-registry-auth + run: | + set -euo pipefail + mkdir -p "$DOCKER_CONFIG" + trap 'rm -rf "$DOCKER_CONFIG"' EXIT + printf '%s' "$GH_TOKEN" | docker login ghcr.io --username "$GH_ACTOR" --password-stdin + key=$(bin/builder-image key --arch "${{ matrix.arch }}" --mirror edge) + version="$REGISTRY_IMAGE:$key-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" + docker tag "$CANDIDATE_IMAGE" "$version" + docker push "$version" + # GHCR creates new packages private. Do not advertise an image to + # fork PRs until it is public. This is a one-time package setting. + anonymous_config=$(mktemp -d "$RUNNER_TEMP/builder-anonymous.XXXXXX") + if ! DOCKER_CONFIG="$anonymous_config" docker manifest inspect "$version" >/dev/null; then + rm -rf "$anonymous_config" + echo "::error::Make the omacom/omarchy-pkg-builder GHCR package public, then rerun this job. The previous matching image remains selected." + exit 1 + fi + rm -rf "$anonymous_config" + docker tag "$CANDIDATE_IMAGE" "$REGISTRY_IMAGE:$key" + docker push "$REGISTRY_IMAGE:$key" + digest=$(docker image inspect "$version" --format '{{index .RepoDigests 0}}') + printf '### Builder image (%s)\n\nInput key: `%s`\n\nImage: `%s`\n' \ + "${{ matrix.arch }}" "$key" "$digest" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a61642d --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,326 @@ +name: Publish merged packages + +# On every push to master: for each package directory the push touched and +# each architecture it supports, find the PR build artifact for exactly that +# tree (label = --), or build it now when there is +# none, then publish that one artifact into every channel the package ships +# to. One build, one file, several databases: a filename means one set of +# bytes everywhere, and channels are views over a shared pool. +# +# Secrets live in the "publish" environment, restricted to master: +# GPG_PRIVATE_KEY, GPG_PASSPHRASE the channel signing key +# R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ENDPOINT +# OMARCHY_PUBLISH_PREFIX (environment variable, not secret) points a proof +# run at a scratch prefix inside the live bucket; empty means the real +# channel paths. + +on: + push: + branches: [master] + paths: ["pkgbuilds/**"] + workflow_dispatch: + inputs: + packages: + description: "Space-separated package directories to publish from master" + required: true + +# Merges serialize. Two publishes into one channel at once would race on +# the database; queued is fine, cancelled is not. +concurrency: + group: publish + cancel-in-progress: false + +jobs: + changes: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.list.outputs.matrix }} + count: ${{ steps.list.outputs.count }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - id: list + run: | + if [[ -n "${{ github.event.inputs.packages }}" ]]; then + names="${{ github.event.inputs.packages }}" + else + names=$(git diff --name-only "${{ github.event.before }}..${{ github.sha }}" -- pkgbuilds \ + | awk -F/ '$1=="pkgbuilds" && NF>2 {print $2}' | sort -u) + fi + matrix=$(printf '%s\n' $names | bin/build-matrix) + echo "matrix=$matrix" >> "$GITHUB_OUTPUT" + echo "count=$(jq '.include | length' <<<"$matrix")" >> "$GITHUB_OUTPUT" + jq -r '.include[] | "\(.package) \(.arch) -> \(.channels)"' <<<"$matrix" + + # One job for the whole merge. It collects every PR artifact for the + # merged tree (building only what has none), then walks each channel and + # architecture slot exactly once: pull that database, add every package + # that belongs in it, upload. Six slots, six round trips, however many + # packages the merge carried. One process is the only writer, so there + # is no race between packages; the run-level concurrency group above + # keeps one merge from overlapping the next. + publish: + needs: changes + if: needs.changes.outputs.count != '0' + runs-on: [self-hosted, omarchy-builder] + environment: publish + timeout-minutes: 240 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + # Every matrix entry, as a file the shell steps can loop over: + # package arch channels publish_arches + - name: Plan + run: | + jq -r '.include[] | "\(.package) \(.arch) \(.channels|gsub(" ";",")) \(.publish_arches|gsub(" ";","))"' \ + <<'EOF_MATRIX' > plan.txt + ${{ needs.changes.outputs.matrix }} + EOF_MATRIX + cat plan.txt + + # Fetch each package's PR artifact into build-output/edge//, or + # build it when no artifact exists for exactly this tree. An artifact + # carries its package files inside packages.tar (see build-pr.yml and + # helpers/artifact-helpers.sh: the upload action rejects the colon in + # an epoch filename). + - name: Collect artifacts + env: + GH_TOKEN: ${{ github.token }} + CONTAINER_ENGINE: docker + run: | + set -uo pipefail + source helpers/artifact-helpers.sh + # sources.jsonl: where each package's files came from, or that the + # build failed. A failed build ends the run before any publish, and + # the record says so instead of the report job finding nothing. + : > sources.jsonl + failed=0 + while read -r package arch channels publish_arches; do + hash=$(git rev-parse "HEAD:pkgbuilds/$package") + label="$package-$arch-$hash" + found=$(curl -fsS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=$label&per_page=5" \ + | jq -r '[.artifacts[] | select(.expired|not)] | sort_by(.created_at) | last | .archive_download_url // empty') + mkdir -p "build-output/edge/$arch" + if [[ -n "$found" ]]; then + echo "==> $label: PR artifact" + rm -rf /tmp/artifact; mkdir -p /tmp/artifact + if curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -o /tmp/artifact.zip "$found" \ + && unzip -oq /tmp/artifact.zip -d /tmp/artifact \ + && unpack_packages /tmp/artifact "build-output/edge/$arch"; then + jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"pr-artifact"}' >> sources.jsonl + else + jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"artifact-download-failed"}' >> sources.jsonl; failed=1; break + fi + else + # bin/build plans against the public channel first. If the + # channel already holds master's version there is nothing to + # build and nothing to publish: a re-run for a package that + # turned out to be fine. Record it and move on. + plan=$(CONTAINER_ENGINE=docker bin/build --dry-run --mirror edge --arch "$arch" --package "$package" 2>&1 | grep -E '^==> Plan complete' || true) + # "Packages that would build:" followed by nothing means none. + if [[ -n "$plan" && -z "$(sed -E 's/.*would build: *//' <<<"$plan" | tr -d '[:space:]')" ]]; then + echo "==> $label: already published at master's version, nothing to do" + jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"already-published"}' >> sources.jsonl + continue + fi + echo "==> $label: no artifact for this tree, building" + if OMARCHY_KEEP_BUILD_WORKSPACE=1 bin/build --mirror edge --arch "$arch" --package "$package"; then + jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"built"}' >> sources.jsonl + else + jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"build-failed"}' >> sources.jsonl; failed=1; break + fi + fi + done < plan.txt + ls -1 build-output/edge/*/*.pkg.tar.zst 2>/dev/null || true + if (( failed )); then + # Write the record now; the publish step will not run. + jq -n --arg sha "${{ github.sha }}" --arg run "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ + --arg prefix "${OMARCHY_PUBLISH_PREFIX:-}" --arg event "${{ github.event_name }}" \ + --slurpfile sources sources.jsonl --slurpfile plan <(jq -c '.include[]' <<<'${{ needs.changes.outputs.matrix }}' | jq -sc .) \ + '{time: (now|todate), commit:$sha, run:$run, event:$event, target: (if $prefix=="" then "live" else $prefix end), plan:$plan[0], sources:$sources, slots:[]}' \ + > publish-record.json + cat publish-record.json + exit 1 + fi + + - name: Publish + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + RCLONE_CONFIG_R2_TYPE: s3 + RCLONE_CONFIG_R2_PROVIDER: Cloudflare + # The token is scoped to the bucket; it may not CreateBucket, and + # rclone's existence check is a CreateBucket in disguise. + RCLONE_CONFIG_R2_NO_CHECK_BUCKET: "true" + RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} + RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} + RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} + OMARCHY_PUBLISH_PREFIX: ${{ vars.OMARCHY_PUBLISH_PREFIX }} + # repo-add, gpg and bsdtar are Arch tools; run the publish inside the + # builder image (host-native, edge) with the workspace mounted. + run: | + set -euo pipefail + if ! compgen -G "build-output/edge/*/*.pkg.tar.zst" >/dev/null; then + echo "Nothing to publish: every requested package is already published at master's version." + jq -n --arg sha "${{ github.sha }}" --arg run "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ + --arg prefix "${OMARCHY_PUBLISH_PREFIX:-}" --arg event "${{ github.event_name }}" \ + --slurpfile sources sources.jsonl --slurpfile plan <(jq -c '.include[]' <<<'${{ needs.changes.outputs.matrix }}' | jq -sc .) \ + '{time: (now|todate), commit:$sha, run:$run, event:$event, target: (if $prefix=="" then "live" else $prefix end), plan:$plan[0], sources:$sources, slots:[]}' \ + > publish-record.json + cat publish-record.json + exit 0 + fi + docker image inspect omarchy-pkg-builder:latest-x86_64-edge >/dev/null 2>&1 \ + || docker buildx build --load -t omarchy-pkg-builder:latest-x86_64-edge --build-arg MIRROR=edge build + + # Group the merge's files by the (channel, architecture) slot each + # belongs to. A package's files live under build-output/edge// and are named --.pkg.tar.zst; a + # split package's outputs share the pkgbase's directory, so match + # on the artifact list rather than the name. + # pkgbase is read inside the builder image: the Ubuntu host has no + # bsdtar. One container call maps every file to its pkgbase. + docker run --rm -v "$PWD:/w:ro" -w /w omarchy-pkg-builder:latest-x86_64-edge bash -c ' + for f in build-output/edge/*/*.pkg.tar.zst; do + printf "%s %s\n" "$f" "$(bsdtar -xOf "$f" .PKGINFO | awk -F" = " "\$1==\"pkgbase\"{print \$2}")" + done' > pkgbase.txt + declare -A slot_files=() + while read -r package arch channels publish_arches; do + for f in build-output/edge/"$arch"/*.pkg.tar.zst; do + # Only files this package produced (its PKGINFO pkgbase). + [[ $(awk -v f="$f" '$1==f{print $2}' pkgbase.txt) == "$package" ]] || continue + for mirror in ${channels//,/ }; do + for parch in ${publish_arches//,/ }; do + slot_files["$mirror/$parch"]+="$f " + done + done + done + done < plan.txt + + # Deterministic slot order: edge before rc before stable, x86_64 + # before aarch64, so a failure leaves the earlier rings consistent. + # Every slot's outcome goes into publish-record.json for the report + # job: what was published, where, from which artifact, and whether + # the slot succeeded. A failing slot stops the loop (set -e) but the + # record still shows everything before it landed. + : > slots.jsonl + record_slot() { jq -nc --arg m "$1" --arg a "$2" --arg st "$3" --arg files "$4" \ + '{mirror:$m, arch:$a, status:$st, packages:[$files | split(" ") | .[] | select(length>0) | sub(".*/";"") | sub("\\.pkg\\.tar\\.zst$";"")]}' >> slots.jsonl; } + status=0 + for mirror in edge rc stable; do + for parch in x86_64 aarch64; do + files=${slot_files["$mirror/$parch"]:-} + [[ -n "$files" ]] || continue + echo "==> $mirror/$parch: $files" + if docker run --rm \ + -e GPG_PRIVATE_KEY -e GPG_PASSPHRASE -e OMARCHY_PUBLISH_PREFIX \ + -e RCLONE_CONFIG_R2_TYPE -e RCLONE_CONFIG_R2_PROVIDER -e RCLONE_CONFIG_R2_ENDPOINT -e RCLONE_CONFIG_R2_NO_CHECK_BUCKET \ + -e RCLONE_CONFIG_R2_ACCESS_KEY_ID -e RCLONE_CONFIG_R2_SECRET_ACCESS_KEY \ + -v "$PWD:/w:ro" -w /w \ + omarchy-pkg-builder:latest-x86_64-edge \ + bin/publish-artifact --remote R2:omarchy-pkgs --mirror "$mirror" --arch "$parch" $files; then + record_slot "$mirror" "$parch" published "$files" + else + record_slot "$mirror" "$parch" failed "$files" + status=1 + break 2 + fi + done + done + jq -n --arg sha "${{ github.sha }}" --arg run "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ + --arg prefix "${OMARCHY_PUBLISH_PREFIX:-}" --arg event "${{ github.event_name }}" \ + --slurpfile slots slots.jsonl --slurpfile sources sources.jsonl --slurpfile plan <(jq -c '.include[]' <<<'${{ needs.changes.outputs.matrix }}' | jq -sc .) \ + '{time: (now|todate), commit:$sha, run:$run, event:$event, target: (if $prefix=="" then "live" else $prefix end), plan:$plan[0], sources:$sources, slots:$slots}' \ + > publish-record.json + cat publish-record.json + exit $status + + - name: Keep the publish record + if: always() + uses: actions/upload-artifact@v4 + with: + name: publish-record-${{ github.run_id }} + path: publish-record.json + retention-days: 90 + + # Tell people what happened. A comment on the merged PR (found by the + # merge commit, so squash and rebase merges work too) and a line appended + # to a running JSON log in the bucket, next to the packages it describes, + # so the history is public and can be rendered later. + report: + needs: [changes, publish] + if: always() && needs.publish.result != 'skipped' + runs-on: ubuntu-latest + environment: publish + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/download-artifact@v4 + with: + name: publish-record-${{ github.run_id }} + - name: Render + id: render + run: | + jq -r --arg outcome "${{ needs.publish.result }}" ' + def pkgs: [.packages[] | select(test("-debug-")|not)] | join(", "); + def src: if .source=="pr-artifact" then "PR artifact" elif .source=="built" then "built here" elif .source=="already-published" then "already published, skipped" else "**" + .source + "**" end; + "### Publish " + (if $outcome=="success" then "succeeded" else "FAILED" end) + + " → **" + .target + "**" + (if .target!="live" then " (proof prefix, not live)" else "" end), + "", + "Packages: " + ([.sources[] | .package + " (" + .arch + ", " + src + ")"] | join("; ")), + "", + (if (.slots|length) > 0 then (.slots[] | "- `" + .mirror + "/" + .arch + "`: " + (if .status=="published" then "" else "**" + .status + "** " end) + pkgs) + elif ([.sources[] | select(.source=="already-published")] | length) == (.sources|length) then "_Nothing to publish: already at master'"'"'s version everywhere._" + else "_Nothing was published._" end), + "", + (if ([.sources[] | select(.source|test("failed"))] | length) > 0 then "_A build failed, so no channel was touched._\n" + elif (.plan|length) > (.slots|length) then "_Some planned slots did not run because an earlier slot failed._\n" else "" end), + "Commit " + .commit[0:7] + " · [run](" + .run + ")" + ' publish-record.json > comment.md + cat comment.md + - name: Append to the publish log in the bucket + env: + RCLONE_CONFIG_R2_TYPE: s3 + RCLONE_CONFIG_R2_PROVIDER: Cloudflare + RCLONE_CONFIG_R2_NO_CHECK_BUCKET: "true" + RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} + RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} + RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} + run: | + curl -fsSL https://downloads.rclone.org/rclone-current-linux-amd64.zip -o rclone.zip && unzip -jq rclone.zip '*/rclone' && chmod +x rclone + # One JSON object per line, newest last. Served at + # https://pkgs.omarchy.org/publish-log.jsonl + ./rclone copy R2:omarchy-pkgs/publish-log.jsonl . --s3-no-head 2>/dev/null || : > publish-log.jsonl + jq -c . publish-record.json >> publish-log.jsonl + ./rclone copyto publish-log.jsonl R2:omarchy-pkgs/publish-log.jsonl --s3-no-head + echo "log now has $(wc -l < publish-log.jsonl) entries" + + - name: Comment on the merged PR + # Only for a push: the merge commit names its PR. A dispatch runs + # from master's head, whose PR merged something else entirely, so + # commenting there would attach this run's report to the wrong PR. + if: github.event_name == 'push' + env: + GH_TOKEN: ${{ github.token }} + run: | + pr=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" --jq '.[0].number // empty') + if [[ -n "$pr" ]]; then + gh pr comment "$pr" -R "${{ github.repository }}" --body-file comment.md + echo "commented on #$pr" + else + echo "no PR for ${{ github.sha }} (manual dispatch?); skipping PR comment" + fi + result: + needs: [changes, publish] + if: always() + runs-on: ubuntu-latest + steps: + - run: | + echo "publish result: ${{ needs.publish.result }}" + [[ "${{ needs.publish.result }}" == "success" || "${{ needs.publish.result }}" == "skipped" ]] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b64690..29d18b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,11 @@ name: Tests +# PR-only. Publishing on push has its own workflow and is what verifies the +# merged tree: it resolves every package against the live channel and refuses +# a filename that already exists with different bytes, so two PRs cannot land +# the same version twice. A post-merge test run would only repeat the PR's. on: pull_request: - push: - branches: [master] workflow_dispatch: jobs: @@ -30,6 +32,12 @@ jobs: with: persist-credentials: false + - name: Test PR workflow approval + run: node --test tests/pr-workflow-approval.cjs + + - name: Test builder images + run: node --test tests/builder-image.cjs + # An Arch container for vercmp: version ordering has to be decided by # the same comparator pacman uses on users' machines. - name: Run self-tests @@ -40,10 +48,16 @@ jobs: archlinux:base-devel bash -lc ' set -euo pipefail pacman -Syu --noconfirm git jq python libarchive + python tests/oma-service-removal.py python tests/upstream-watch.py ./bin/sync-upstream self-test ./bin/sync-rebuilds --self-test ./bin/omarchy-pkgs self-test ./bin/omarchy-release self-test ./tests/partial-release.sh + ./tests/published-build-plan.sh + ./tests/controller.sh + ./tests/artifact-helpers.sh + pacman -S --noconfirm --quiet rclone >/dev/null + ./tests/publish-artifact.sh ' diff --git a/.gitignore b/.gitignore index 7b1f63b..65cb4ad 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ pkgbuilds/yay/yay/ # Python helpers and offline tests __pycache__/ +.release-verification/ diff --git a/README.md b/README.md index 5ff0edc..8823595 100644 --- a/README.md +++ b/README.md @@ -825,6 +825,45 @@ using real containers and pacman transactions. It uses the prepared builder image, or an image named by `TEST_BUILDER_IMAGE`; CI builds the small fixture image in `tests/build-isolation.Dockerfile`. +### Daily builder images + +`Refresh builder images` builds fresh `edge` environments daily at 04:23 UTC, +when their inputs change on `master`, and on manual dispatch. x86_64 and +aarch64 build on native GitHub-hosted runners, without occupying the DO +package-builder pool. Each candidate must pass `tests/build-isolation.sh`, +including real package builds, before publication to +`ghcr.io/omacom/omarchy-pkg-builder`. Only `master` in this repository can +publish; PR workflows cannot replace the shared images. +PRs that change image inputs also build and test both candidates on native +runners, with a read-only token and no registry publication. + +The compatibility tag contains the architecture, mirror, and a hash of the +entire `build/` context, including executable bits and symlink targets but +excluding checkout timestamps and ownership. This deliberately invalidates +images when mounted build scripts change too. `v1` identifies the image build +contract; change it if the invocation or compatibility rules change. Each +successful refresh also gets a run-specific tag for diagnosis and rollback. +A failed build, isolation test, or push leaves the previous compatible image +selected. Scheduled builds use `--pull --no-cache` so unchanged Dockerfiles +still pick up fresh Arch packages. + +To build and test a candidate locally: + +```bash +bin/builder-image key --arch x86_64 --mirror edge +bin/builder-image build --arch x86_64 --mirror edge --tag builder-candidate:test --fresh +CONTAINER_ENGINE=docker TEST_BUILDER_IMAGE=builder-candidate:test tests/build-isolation.sh +``` + +The workflow uses its repository `GITHUB_TOKEN` with `packages: write`; no +registry PAT is needed. **First publication needs one package setting:** GHCR +creates the package private. In the `omacom/omarchy-pkg-builder` package +settings, change visibility to **Public**, then rerun the failed refresh job. +The workflow checks anonymous registry access before advancing the compatible +tag, so fork PRs will not be directed to an image they cannot pull. Subsequent +refreshes preserve that package visibility. This change only produces images; +package jobs keep their existing behavior until image consumption is enabled. + ## Version Management Packages are only rebuilt if: @@ -844,6 +883,22 @@ The repository includes GitHub workflows and systemd services for automated rele 1. **sync-upstream.yml** (Every 6 hours): Watches direct upstream feeds and updates owned recipes. Successful package updates reach a PR even if another feed fails; failed recipes stay untouched and the workflow remains red. 2. **sync-rebuilds.yml** (Every 6 hours): Bumps pkgrel for packages whose `rebuild_on` dependencies have moved in the official repositories and opens a PR. +To approve builds for an unvouched contributor's PR, apply **`build-approved`**. +Until approval, the PR shows **Awaiting build approval** and its required +`result` check stays pending, keeping the PR blocked from merging without +reporting a failed build. Actual build failures and denouncements still fail. +Applying the label triggers a package build and automatically releases GitHub's +pending build and test workflows for that PR's current commit. The approval workflow +runs only trusted default-branch code; package builds and tests stay in the +ordinary PR workflows. It may take a few minutes for GitHub to register and +release all the runs. + +The label stays effective for that PR while attached, including later commits; +it does not vouch for the author's other PRs. Removing it stops further label +approvals, but does not cancel runs already released. An explicit denouncement +in `.github/VOUCHED.td` still blocks builds. If the approval workflow times out, +remove and reapply the label to retry. + #### Systemd Services All four units run **every 5 minutes**, staggered by a minute each, so a push diff --git a/bin/build b/bin/build index 728852b..765e071 100755 --- a/bin/build +++ b/bin/build @@ -92,6 +92,8 @@ while [[ $# -gt 0 ]]; do echo " OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output and reuse packages already there" echo " OMARCHY_SKIP_BUILDER_IMAGE=1 use the existing builder image instead of building it" echo " OMARCHY_DEFER_RUNTIME_DEPS=true build the omarchy pair with --nodeps (transaction verified later)" + echo " OMARCHY_PUBLISHED_REPO_URL= channel to plan and resolve against when no local tree exists" + echo " (default https://pkgs.omarchy.org; empty disables the fallback)" echo "" exit 0 ;; @@ -256,6 +258,7 @@ DOCKER_ARGS=( -e MIRROR="$MIRROR" -e PACKAGES="$PACKAGES" -e OMARCHY_RC_PINS="${OMARCHY_RC_PINS:-}" + -e OMARCHY_PUBLISHED_REPO_URL="${OMARCHY_PUBLISHED_REPO_URL-https://pkgs.omarchy.org}" -e DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS" -e BUILD_PLAN_DIR=/build-plan -v "$PLAN_DIR:/build-plan" diff --git a/bin/build-matrix b/bin/build-matrix new file mode 100755 index 0000000..e772349 --- /dev/null +++ b/bin/build-matrix @@ -0,0 +1,54 @@ +#!/bin/bash +# Print the PR build matrix for a set of package directories as JSON: one +# entry per package per supported architecture. Every package builds exactly +# once, against edge, and that one artifact is what every channel ships: +# channels are databases over a shared pool of files, and a filename must +# mean one set of bytes. "channels" lists where the artifact is published on +# merge: edge for everything, plus rc and stable immediately for the fast +# ring. Eligibility comes from package_builds_for_mirror, the rule the +# release host uses, so CI and the host cannot disagree. +# +# Usage: build-matrix [--arch |all] ... +# Reads package names on stdin when none are given. With no --arch, every +# architecture in CI_ARCHES (default "x86_64 aarch64") the package supports. +# Output: {"include":[{"package":"x","arch":"x86_64","channels":"edge rc stable","publish_arches":"x86_64"},...]} +# arch is where it builds; publish_arches lists every architecture +# database the file goes into (all of them for arch=any). +set -euo pipefail +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +source "$BUILD_ROOT/helpers/paths.sh" +source "$BUILD_ROOT/helpers/package-metadata.sh" + +ARCHES=${CI_ARCHES:-x86_64 aarch64} +if [[ "${1:-}" == --arch ]]; then [[ $2 == all ]] || ARCHES=$2; shift 2; fi +for a in $ARCHES; do require_valid_arch "$a"; done + +if (( $# )); then names=("$@"); else mapfile -t names; fi + +entries=() +for name in "${names[@]}"; do + [[ -n "$name" ]] || continue + pkgdir="$PKGBUILDS_DIR/$name" + [[ -d "$pkgdir" ]] || continue + # skip_build packages still build on their own PR (explicit --package + # semantics); the host's unscoped runs are what skip them. + channels="" + for mirror in $VALID_MIRRORS; do + package_builds_for_mirror "$pkgdir" "$mirror" && channels="$channels $mirror" + done + channels=${channels# } + [[ -n "$channels" ]] || continue + # An arch=any package produces one architecture-independent file, so it + # builds once, on the first architecture, and that file serves every + # channel database of every architecture. + if [[ " $(package_arches "$pkgdir" "${ARCHES%% *}") " == *" any "* ]]; then + entries+=("$(jq -nc --arg p "$name" --arg a "${ARCHES%% *}" --arg c "$channels" --arg pa "$ARCHES" '{package:$p, arch:$a, channels:$c, publish_arches:$pa}')") + continue + fi + for arch in $ARCHES; do + package_supports_arch "$pkgdir" "$arch" || continue + entries+=("$(jq -nc --arg p "$name" --arg a "$arch" --arg c "$channels" '{package:$p, arch:$a, channels:$c, publish_arches:$a}')") + done +done + +printf '%s\n' "${entries[@]}" | jq -sc '{include: .}' diff --git a/bin/builder-image b/bin/builder-image new file mode 100755 index 0000000..f23f64b --- /dev/null +++ b/bin/builder-image @@ -0,0 +1,66 @@ +#!/bin/bash +# Build a reusable package environment from this checkout's own inputs. +set -euo pipefail + +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +source "$BUILD_ROOT/helpers/paths.sh" + +usage() { + echo "Usage: bin/builder-image {key|build} [--arch x86_64|aarch64] [--mirror edge|rc|stable] [--tag IMAGE] [--fresh]" +} + +command=${1:-} +[[ $# -eq 0 ]] || shift +tag="" +fresh=false +while (( $# )); do + case "$1" in + --arch) ARCH=${2:?Missing architecture}; shift 2 ;; + --mirror) MIRROR=${2:?Missing mirror}; shift 2 ;; + --tag) tag=${2:?Missing image tag}; shift 2 ;; + --fresh) fresh=true; shift ;; + *) usage >&2; exit 1 ;; + esac +done +require_valid_arch "$ARCH" +validate_mirror "$MIRROR" || { echo "Invalid mirror: $MIRROR" >&2; exit 1; } +case "$command" in key|build) ;; *) usage >&2; exit 1 ;; esac +if [[ $command == key && ( -n $tag || $fresh == true ) ]]; then + usage >&2 + exit 1 +fi + +# Include the whole build context, conservatively including mounted build +# scripts too. Normalize timestamps and ownership so fresh checkouts agree; +# retain file contents, names, executable bits and symlink targets. Bump v1 +# if the image build invocation or this compatibility contract changes. +hash=$(tar --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \ + --format=gnu -cf - -C "$BUILD_DIR" . | sha256sum | cut -d' ' -f1) +key="v1-$ARCH-$MIRROR-$hash" +if [[ $command == key ]]; then + echo "$key" + exit 0 +fi + +source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" +check_engine +platform=$(get_platform_arg "$ARCH") +tag=${tag:-omarchy-pkg-builder:latest-$ARCH-$MIRROR} +revision=$(git -C "$BUILD_ROOT" rev-parse HEAD 2>/dev/null || echo unknown) +args=("$platform" --build-arg "MIRROR=$MIRROR" + --label "org.omarchy.builder.key=$key" + --label "org.opencontainers.image.source=https://github.com/omacom/omarchy-pkgs" + --label "org.opencontainers.image.revision=$revision" + --label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + --tag "$tag" --file "$BUILD_DIR/Dockerfile") +if [[ $fresh == true ]]; then + # A daily build must refresh Arch even when its Dockerfile has not changed. + args+=(--no-cache) + if [[ $CONTAINER_ENGINE == docker ]]; then args+=(--pull); else args+=(--pull=always); fi +fi +if [[ $CONTAINER_ENGINE == docker ]]; then + docker buildx build --load "${args[@]}" "$BUILD_DIR" +else + podman build "${args[@]}" "$BUILD_DIR" +fi diff --git a/bin/check-versions b/bin/check-versions index 925570f..6f0558f 100755 --- a/bin/check-versions +++ b/bin/check-versions @@ -172,8 +172,8 @@ check_package() { # it because that exact filename is already published with different bytes. # If the artifact for this version already exists in the channel, there is # nothing to build regardless of which direction the versions differ. - if compgen -G "$REPO_ROOT/$mirror/$ARCH/${pkg}-${pkgbuild_version}-*.pkg.tar."[!s]* >/dev/null 2>&1; then - print_warning "$pkg $pkgbuild_version is already published — this checkout is behind the channel; not queueing" + if package_version_is_published "$REPO_ROOT/$mirror/$ARCH" "$pkg" "$pkgbuild_version" "$ARCH"; then + print_warning "$pkg $pkgbuild_version is already published; not queueing" return 1 fi diff --git a/bin/publish-artifact b/bin/publish-artifact new file mode 100755 index 0000000..32a4909 --- /dev/null +++ b/bin/publish-artifact @@ -0,0 +1,118 @@ +#!/bin/bash +# Publish built packages into one channel of the remote repository, +# incrementally and immutably. +# +# publish-artifact --mirror --arch +# +# What it does, in order: +# 1. pull the channel's current database from the remote +# 2. refuse if any package filename already exists on the remote +# 3. sign each package (GPG_PRIVATE_KEY / GPG_PASSPHRASE) +# 4. repo-add the packages into the pulled database (replaces the entry +# for that name; nothing else in the channel is touched) +# 5. upload packages, then signatures, then the database last +# +# Never overwrites: uploads use --ignore-existing for packages and the +# pre-check in step 2 makes a same-name collision a hard failure rather than +# a silent skip. The database is the only object rewritten, and it is +# uploaded only after every file it references is present. +# +# The remote is an rclone remote (REMOTE, default the production one); +# OMARCHY_PUBLISH_PREFIX can point a proof run at a scratch prefix. +set -euo pipefail +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/paths.sh" + +REMOTE=${REMOTE:-pkgs.omarchy.org:omarchy-pkgs} +PREFIX=${OMARCHY_PUBLISH_PREFIX:-} +FILES=() +while [[ $# -gt 0 ]]; do + case $1 in + --mirror) MIRROR=$2; require_valid_mirror "$MIRROR"; shift 2 ;; + --arch) ARCH=$2; require_valid_arch "$ARCH"; shift 2 ;; + --remote) REMOTE=$2; shift 2 ;; + -h|--help) sed -n '2,22p' "$0"; exit 0 ;; + -*) print_error "Unknown option: $1"; exit 1 ;; + *) FILES+=("$1"); shift ;; + esac +done +(( ${#FILES[@]} )) || { print_error "No package files given"; exit 1; } +: "${GPG_PRIVATE_KEY:?}"; GPG_PASSPHRASE=${GPG_PASSPHRASE-} + +DEST="$REMOTE/${PREFIX:+$PREFIX/}$MIRROR/$ARCH" +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT +print_header "Publish to $DEST" + +# --- 0. sanity: every file is a package, named as makepkg names it --------- +for f in "${FILES[@]}"; do + [[ -f "$f" && "$f" == *.pkg.tar.* && "$f" != *.sig ]] || { print_error "Not a package file: $f"; exit 1; } + name=$(bsdtar -xOf "$f" .PKGINFO | awk -F' = ' '$1=="pkgname"{print $2}') + ver=$(bsdtar -xOf "$f" .PKGINFO | awk -F' = ' '$1=="pkgver"{print $2}') + pkgarch=$(bsdtar -xOf "$f" .PKGINFO | awk -F' = ' '$1=="arch"{print $2}') + [[ "$(basename "$f")" == "$name-$ver-$pkgarch.pkg.tar."* ]] || { + print_error "Filename does not match PKGINFO ($name-$ver-$pkgarch): $(basename "$f")"; exit 1; } + [[ "$pkgarch" == any || "$pkgarch" == "$ARCH" ]] || { print_error "$f is $pkgarch, publishing to $ARCH"; exit 1; } +done + +# --- 1. pull the current database ----------------------------------------- +mkdir -p "$WORK/repo" +listing=$(rclone lsf "$DEST/" --s3-no-head 2>/dev/null || true) +if grep -q '^omarchy.db.tar.zst$' <<<"$listing"; then + rclone copy "$DEST/omarchy.db.tar.zst" "$WORK/repo" --s3-no-head + rclone copy "$DEST/omarchy.files.tar.zst" "$WORK/repo" --s3-no-head 2>/dev/null || true + print_info "Pulled current database ($(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$') entries)" +else + print_warning "No database at $DEST — creating a new one" +fi + +# --- 2. same-name collisions ---------------------------------------------- +# A filename must mean one set of bytes across every channel. The same file +# reaching a channel that already holds it (a fast-ring publish after edge, +# a re-run, a later promotion) is fine: it is skipped on upload and only the +# database entry is added. Different bytes under a name the channel already +# has is the one thing this must never do. +for f in "${FILES[@]}"; do + b=$(basename "$f") + grep -qxF "$b" <<<"$listing" || continue + remote_sum=$(rclone hashsum md5 "$DEST/$b" --s3-no-head 2>/dev/null | awk '{print $1}') + local_sum=$(md5sum "$f" | awk '{print $1}') + if [[ -n "$remote_sum" && "$remote_sum" == "$local_sum" ]]; then + print_info "Already published with identical bytes, adding to the database only: $b" + else + print_error "Already published with DIFFERENT bytes, refusing to overwrite: $b" + echo " Bump pkgrel; published filenames are immutable." + exit 1 + fi +done + +# --- 3. sign --------------------------------------------------------------- +export GNUPGHOME="$WORK/gnupg"; mkdir -m 700 "$GNUPGHOME" +echo "$GPG_PRIVATE_KEY" | gpg --batch --quiet --import +KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '$1=="sec"{print $5; exit}') +[[ -n "$KEY_ID" ]] || { print_error "No secret key imported"; exit 1; } +for f in "${FILES[@]}"; do + cp "$f" "$WORK/repo/" + gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \ + --detach-sign --no-armor --local-user "$KEY_ID" "$WORK/repo/$(basename "$f")" + print_step "signed $(basename "$f")" +done + +# --- 4. repo-add (replaces the entry for each pkgname) --------------------- +( cd "$WORK/repo" && repo-add --quiet omarchy.db.tar.zst "${FILES[@]/#*\//}" ) +ln -sf omarchy.db.tar.zst "$WORK/repo/omarchy.db" +ln -sf omarchy.files.tar.zst "$WORK/repo/omarchy.files" +print_info "Database now has $(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$') entries" + +# --- 5. upload: packages, signatures, database last ----------------------- +rclone copy "$WORK/repo" "$DEST" --s3-no-head --ignore-existing --filter '- *.sig' --filter '+ *.pkg.tar.*' --filter '- *' +rclone copy "$WORK/repo" "$DEST" --s3-no-head --ignore-existing --filter '+ *.pkg.tar.*.sig' --filter '- *' +# Re-verify every referenced file is really there before the db goes up. +listing=$(rclone lsf "$DEST/" --s3-no-head) +for f in "${FILES[@]}"; do + b=$(basename "$f") + grep -qxF "$b" <<<"$listing" && grep -qxF "$b.sig" <<<"$listing" || { print_error "Upload incomplete: $b"; exit 1; } +done +rclone copy "$WORK/repo" "$DEST" --s3-no-head --checksum --copy-links --filter '+ omarchy.db*' --filter '+ omarchy.files*' --filter '- *' +print_success "Published ${#FILES[@]} package(s) to $DEST" diff --git a/build/Dockerfile b/build/Dockerfile index af1bb5e..2a81a62 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -129,6 +129,7 @@ RUN pacman -Syu --noconfirm && \ wget \ curl \ jq \ + rclone \ gnupg && \ pacman -Scc --noconfirm && \ rm -rf /var/cache/pacman/pkg/* @@ -146,7 +147,8 @@ RUN useradd -m -G wheel -s /bin/bash builder && \ # be skipped at signing. Pin the extension so both architectures match. RUN sed -i 's/^#MAKEFLAGS=.*/MAKEFLAGS="-j$(nproc)"/' /etc/makepkg.conf && \ sed -i 's/^COMPRESSZST=.*/COMPRESSZST=(zstd -c -z -q --threads=0 -)/' /etc/makepkg.conf && \ - sed -i "s|^PKGEXT=.*|PKGEXT='.pkg.tar.zst'|" /etc/makepkg.conf + sed -i "s|^PKGEXT=.*|PKGEXT='.pkg.tar.zst'|" /etc/makepkg.conf && \ + sed -i 's|^#\?PACKAGER=.*|PACKAGER="Omarchy "|' /etc/makepkg.conf # Pacman wrapper that auto-resolves package conflicts (e.g. rustup vs rust). # makepkg only passes --noconfirm to pacman, which defaults to 'N' on conflict diff --git a/build/build.sh b/build/build.sh index 96500e7..65d6017 100755 --- a/build/build.sh +++ b/build/build.sh @@ -26,6 +26,29 @@ DEFER_RUNTIME_DEPS=${DEFER_RUNTIME_DEPS:-false} source "$HELPERS_DIR/package-metadata.sh" +# Where the channel's published database is read from for planning. On the +# repository host it is the published tree itself. Anywhere else (a CI runner, +# a fresh clone) that tree is absent, so the database is fetched from the +# public channel and the same URL serves as pacman's dependency repository. +# Set OMARCHY_PUBLISHED_REPO_URL= (empty) to disable the remote fallback. +PUBLISHED_REPO_URL=${OMARCHY_PUBLISHED_REPO_URL-https://pkgs.omarchy.org} +PUBLISHED_DB_DIR="$FINAL_OUTPUT_DIR" +PUBLISHED_REPO_SERVER="" +if [[ ! -f "$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" && ! -f "$FINAL_OUTPUT_DIR/omarchy.db" && -n "$PUBLISHED_REPO_URL" ]]; then + remote_channel="$PUBLISHED_REPO_URL/$MIRROR/$ARCH" + remote_db_dir=$(mktemp -d /tmp/omarchy-published.XXXXXX) || exit 1 + # Cache-bust: the channel sits behind a CDN that serves a stale database + # for a while after a sync. + if curl -fsSL "$remote_channel/omarchy.db.tar.zst?$(date +%s)" -o "$remote_db_dir/omarchy.db.tar.zst"; then + PUBLISHED_DB_DIR="$remote_db_dir" + PUBLISHED_REPO_SERVER="$remote_channel" + echo "==> No local published tree; planning against $remote_channel" + else + rm -rf "$remote_db_dir" + echo "==> No local published tree and $remote_channel is unavailable; treating the channel as empty" + fi +fi + if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then echo "DEFER_RUNTIME_DEPS must be true or false" >&2 exit 1 @@ -118,10 +141,15 @@ if [[ "$DRY_RUN" != true ]]; then fi touch "$BUILD_PLAN_DIR/repository-initialized" || exit 1 - # Add omarchy repo if it has a database (stable packages) + # Add omarchy repo if it has a database (stable packages). The local tree + # is trusted as-is; the public channel is verified against the omarchy + # keyring the image already carries. if [[ -f "$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" ]] || [[ -f "$FINAL_OUTPUT_DIR/omarchy.db" ]]; then sudo sed -i "/^\[core\]$/i [omarchy]\nSigLevel = Optional TrustAll\nServer = file://$FINAL_OUTPUT_DIR\n" /etc/pacman.conf echo " -> omarchy (priority 2): $FINAL_OUTPUT_DIR" + elif [[ -n "$PUBLISHED_REPO_SERVER" ]]; then + sudo sed -i "/^\[core\]$/i [omarchy]\nSigLevel = Required DatabaseOptional\nServer = $PUBLISHED_REPO_SERVER\n" /etc/pacman.conf + echo " -> omarchy (priority 2): $PUBLISHED_REPO_SERVER" fi # Sync pacman database @@ -159,10 +187,10 @@ LOCAL_VERSION_CACHE_LOADED=false LOCAL_VERSION_CACHE_DB="" load_local_versions() { - local db="$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" + local db="$PUBLISHED_DB_DIR/omarchy.db.tar.zst" if [[ ! -f "$db" ]]; then - db="$FINAL_OUTPUT_DIR/omarchy.db" + db="$PUBLISHED_DB_DIR/omarchy.db" fi [[ -f "$db" ]] || return 0 @@ -531,9 +559,17 @@ check_needs_build() { if [[ "$local_version" == "$pkgbuild_version" ]]; then return 1 # Already up to date - else - return 0 # Needs building fi + + # Match check-versions: a retained archive is already published even when + # the DB now indexes a newer release (for example, 4.0.4rc1 vs 4.0.3). + # Rebuilding it would produce different bytes under an immutable filename. + if package_version_is_published "$FINAL_OUTPUT_DIR" "$pkg" "$pkgbuild_version" "$ARCH"; then + echo " + $pkg $pkgbuild_version - archive already published; skipping rebuild" + return 1 + fi + + return 0 # Needs building } # Collect packages that should be built for the selected mirror diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000..0e53988 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,79 @@ +# CI spike: build PRs on ephemeral DigitalOcean droplets + +Status: spike. Nothing here publishes. The repository host keeps building and +signing on merge exactly as before. + +## Pieces + +- `.github/workflows/build-pr.yml` — on a PR touching `pkgbuilds/**`, one job + per changed package on runners labelled `omarchy-builder`. Uploads the + unsigned `.pkg.tar.zst` as a workflow artifact (7 days). +- `runner-cloud-init.yaml` — Ubuntu 24.04 user-data: docker + buildx, the + GitHub runner registered `--ephemeral`, runs one job, powers off. +- `controller.sh` — systemd timer every minute on a small always-on droplet. + Polls for queued jobs with our label, creates one g5-32vcpu-64gb-50gb droplet (ric1) per job up + to `MAX_DROPLETS`, deletes droplets that are powered off or older than + `MAX_AGE_MINUTES`. No inbound endpoint. Plain curl against both APIs, no + doctl and no gh: a token in the environment cannot pick the wrong account + the way a saved doctl context can. Needs curl and jq. + `tests/controller.sh` exercises every decision against canned responses. +- `controller-box/` — the always-on droplet: unit, timer, env template, + cloud-init, and `create.sh` to stand it up with one API call. + +## Standing up the controller box + + DIGITALOCEAN_TOKEN= GITHUB_TOKEN= \ + REPO=omacom/omarchy-pkgs ci/controller-box/create.sh + +The GitHub PAT is fine-grained, scoped to the one repo: Actions read, +Administration read+write (registration tokens). The DO token is baked into +the box's env file, so it is the account that pays for builder droplets. +Watch it with `journalctl -u omarchy-controller -f` on the box. + +## What the spike proved (2026-09-17, fork ryanrhughes/omarchy-pkgs) + +- `bin/build` works from a bare clone: with no local published tree it + plans against and resolves from `https://pkgs.omarchy.org//`. +- Droplet create → runner registered: ~70 s. omarchy-fish PR job: 2 min + including the builder image build. Droplet powers off after the job. +- linux-omarchy on a c-32 droplet: 30 min wall clock for the build job + (23:39 → 00:09), 254 MB artifact. Cold start ~90 s before the job began. +- A PR whose PKGBUILD fails to build turns the required check red and GitHub + refuses the merge (`mergeStateStatus=BLOCKED`, `gh pr merge` refuses + without `--admin`). +- Controller: one queued job + one busy droplet ⇒ creates exactly one more; + reaps powered-off droplets on the next tick. + +## Not done (required before this touches the real repo) + +- Tooling from base: check out master's `bin/ helpers/ build/` and overlay + only the PR's `pkgbuilds/`; today a PR can edit the build script + and it runs on the droplet. The vouch gate limits who can do that, not + what they can do. +- DigitalOcean cloud firewall on the `omarchy-builder` tag: no inbound, no + egress to private ranges or the metadata address. +- A fine-grained GitHub token for the real repository (the one on the + controller box is scoped to the fork), and the publish environment's + secrets set there. +- Disable the host's auto-release timers for any channel CI publishes to, + so two writers never touch one database. + +## Done since the spike README was first written + +- Controller as a systemd timer on its own droplet, plain curl, self-test. +- Build once against edge; one artifact per package per architecture, + published into every channel it belongs to (fast ring: all three at + once). arch=any builds once for every architecture database. +- Publish is incremental and immutable: pull the channel db, refuse + different bytes under an existing name, accept identical bytes, upload + packages then signatures then the db. +- aarch64 under QEMU with credential-preserving binfmt. +- Vouch gate: collaborators, `.github/VOUCHED.td`, or the `build-approved` + label; denounced authors cannot be overridden by the label. +- Tests run on PRs only; `result`, `self-tests`, `build-isolation` are the + required checks with strict up-to-date branches. + +## Cleanup + + doctl compute droplet list --tag-name omarchy-builder + doctl compute droplet delete -f diff --git a/ci/controller-box/cloud-init.yaml b/ci/controller-box/cloud-init.yaml new file mode 100644 index 0000000..fda8c43 --- /dev/null +++ b/ci/controller-box/cloud-init.yaml @@ -0,0 +1,45 @@ +#cloud-config +# The always-on controller droplet (smallest size is fine). Clones the repo +# for ci/controller.sh, installs the unit and timer, and starts polling. +# +# Substitute before use: +# __REPO_URL__ https://github.com/omacom/omarchy-pkgs.git +# __BRANCH__ branch carrying ci/ (master once merged) +# __ENV_B64__ base64 of a filled-in controller.env.example +# __SSH_KEYS_JSON__ JSON array of public keys authorized for root +package_update: true +packages: [curl, jq, git] + +# Root stays reachable by key so the journal can be read. Two things stand +# in the way on DO images: disable_root rewrites root's keys into a stub, and +# with no account ssh key attached DO expires root's password, which makes +# sshd refuse every non-interactive session with "password change required". +disable_root: false +chpasswd: + expire: false +ssh_authorized_keys: __SSH_KEYS_JSON__ + +users: + - name: controller + shell: /bin/bash + +write_files: + # defer: write after the users module has created the controller group, + # otherwise chown to root:controller fails and the unit cannot read this. + - path: /etc/omarchy-controller.env + permissions: "0640" + owner: root:controller + encoding: b64 + defer: true + content: __ENV_B64__ + +runcmd: + - chage -d "$(date +%F)" -M -1 root + - chown root:controller /etc/omarchy-controller.env && chmod 0640 /etc/omarchy-controller.env + - git clone --depth 1 --branch __BRANCH__ __REPO_URL__ /opt/omarchy-pkgs + - mkdir -p /run/omarchy-controller && chown controller:controller /run/omarchy-controller + - echo "d /run/omarchy-controller 0755 controller controller -" > /etc/tmpfiles.d/omarchy-controller.conf + # runcmd is executed by /bin/sh: no brace expansion. + - cp /opt/omarchy-pkgs/ci/controller-box/omarchy-controller.service /opt/omarchy-pkgs/ci/controller-box/omarchy-controller.timer /etc/systemd/system/ + - systemctl daemon-reload + - systemctl enable --now omarchy-controller.timer diff --git a/ci/controller-box/controller.env.example b/ci/controller-box/controller.env.example new file mode 100644 index 0000000..37ae372 --- /dev/null +++ b/ci/controller-box/controller.env.example @@ -0,0 +1,15 @@ +# /etc/omarchy-controller.env — mode 0600, owned by root, read by systemd. +DIGITALOCEAN_TOKEN=dop_v1_... +# Fine-grained PAT scoped to the repo: Actions: read, Administration: read+write +GITHUB_TOKEN=github_pat_... +REPO=omacom/omarchy-pkgs +LABEL=omarchy-builder +TAG=omarchy-builder +REGION=ric1 +SIZE=g5-32vcpu-64gb-50gb +MAX_DROPLETS=6 +MAX_AGE_MINUTES=200 +LOCK=/run/omarchy-controller/lock +# Operator public keys for root on every builder droplet (JSON array). +# create.sh fills this from the operators' GitHub keys. +SSH_KEYS_JSON=[] diff --git a/ci/controller-box/create.sh b/ci/controller-box/create.sh new file mode 100755 index 0000000..9ec4c3d --- /dev/null +++ b/ci/controller-box/create.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Create the controller droplet with plain curl. Run from a laptop, once. +# +# DIGITALOCEAN_TOKEN=... GITHUB_TOKEN=... ci/controller-box/create.sh [branch] +# +# The DO token given here is baked into the box's env file, so it must be the +# token for the account that should pay for builder droplets. +set -euo pipefail +here=$(dirname "$0") +: "${DIGITALOCEAN_TOKEN:?}" "${GITHUB_TOKEN:?}" +REPO=${REPO:-omacom/omarchy-pkgs} +BRANCH=${1:-master} +REGION=${REGION:-ric1} +NAME=${NAME:-omarchy-controller} +# Optional DO ssh key ids as a JSON array, e.g. SSH_KEYS='[123]', for reading +# the journal while bringing the box up. Not needed once it works. +SSH_KEYS=${SSH_KEYS:-[]} +# Public keys authorized for root: the operators' GitHub keys, fetched at +# creation so the box never depends on an ssh_key API scope. Override with +# ADMIN_GITHUB_USERS. +ADMIN_GITHUB_USERS=${ADMIN_GITHUB_USERS:-ryanrhughes dhh} +ssh_keys_json=$(for u in $ADMIN_GITHUB_USERS; do curl -fsS "https://github.com/$u.keys"; done | jq -R . | jq -sc .) +[[ $(jq length <<<"$ssh_keys_json") -gt 0 ]] || { echo "no ssh keys fetched for $ADMIN_GITHUB_USERS" >&2; exit 1; } + +env_file=$(sed -e "s|^DIGITALOCEAN_TOKEN=.*|DIGITALOCEAN_TOKEN=$DIGITALOCEAN_TOKEN|" \ + -e "s|^GITHUB_TOKEN=.*|GITHUB_TOKEN=$GITHUB_TOKEN|" \ + -e "s|^REPO=.*|REPO=$REPO|" \ + -e "s|^SSH_KEYS_JSON=.*|SSH_KEYS_JSON=$ssh_keys_json|" "$here/controller.env.example") +userdata=$(sed -e "s|__REPO_URL__|https://github.com/$REPO.git|" -e "s|__BRANCH__|$BRANCH|" \ + -e "s|__ENV_B64__|$(printf '%s\n' "$env_file" | base64 -w0)|" \ + -e "s|__SSH_KEYS_JSON__|$ssh_keys_json|" "$here/cloud-init.yaml") +body=$(jq -n --arg name "$NAME" --arg region "$REGION" --arg ud "$userdata" --argjson keys "$SSH_KEYS" \ + '{name:$name, region:$region, size:"s-1vcpu-1gb", image:"ubuntu-24-04-x64", tags:["omarchy-controller"], user_data:$ud, ssh_keys:$keys}') + +# Refuse to create a second one. +existing=$(curl -fsS -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ + "https://api.digitalocean.com/v2/droplets?tag_name=omarchy-controller" | jq '.droplets | length') +if (( existing > 0 )); then echo "a controller droplet already exists" >&2; exit 1; fi + +curl -fsS -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" \ + -X POST -d "$body" https://api.digitalocean.com/v2/droplets | jq -r '"created \(.droplet.name) id=\(.droplet.id)"' diff --git a/ci/controller-box/omarchy-controller.service b/ci/controller-box/omarchy-controller.service new file mode 100644 index 0000000..12d2e9c --- /dev/null +++ b/ci/controller-box/omarchy-controller.service @@ -0,0 +1,12 @@ +[Unit] +Description=Provision ephemeral omarchy-builder runner droplets for queued jobs +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +User=controller +EnvironmentFile=/etc/omarchy-controller.env +ExecStart=/opt/omarchy-pkgs/ci/controller.sh +# The reaper's safety net is time, not state; a hung tick must not hold the lock. +TimeoutStartSec=240 diff --git a/ci/controller-box/omarchy-controller.timer b/ci/controller-box/omarchy-controller.timer new file mode 100644 index 0000000..0534b68 --- /dev/null +++ b/ci/controller-box/omarchy-controller.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Run the omarchy-builder controller every minute + +[Timer] +OnBootSec=1min +OnUnitActiveSec=1min +AccuracySec=5s + +[Install] +WantedBy=timers.target diff --git a/ci/controller.sh b/ci/controller.sh new file mode 100755 index 0000000..cc60950 --- /dev/null +++ b/ci/controller.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# Droplet-per-job controller for the omarchy-builder runner pool. +# +# Run from a systemd timer every minute on a small always-on droplet. No +# inbound endpoint: it polls GitHub for queued jobs wanting our label, creates +# one ephemeral droplet per job (up to MAX_DROPLETS), and deletes droplets +# that have powered off or exceeded MAX_AGE_MINUTES. The reaper does not +# trust its own bookkeeping: it lists by tag and acts on what DigitalOcean +# reports. +# +# Talks to both APIs with curl. No doctl: its saved contexts silently choose +# an account; a token in the environment cannot. Needs curl and jq. +# +# Environment: +# DIGITALOCEAN_TOKEN DO API token for the account that pays for droplets +# GITHUB_TOKEN fine-grained PAT: Actions read, Administration write +# REPO owner/name +set -euo pipefail + +REPO=${REPO:?owner/name} +: "${DIGITALOCEAN_TOKEN:?}" "${GITHUB_TOKEN:?}" +LABEL=${LABEL:-omarchy-builder} +TAG=${TAG:-omarchy-builder} +REGION=${REGION:-ric1} +SIZE=${SIZE:-g5-32vcpu-64gb-50gb} +IMAGE=${IMAGE:-ubuntu-24-04-x64} +MAX_DROPLETS=${MAX_DROPLETS:-4} +MAX_AGE_MINUTES=${MAX_AGE_MINUTES:-200} +RUNNER_VERSION=${RUNNER_VERSION:-2.337.0} +CLOUD_INIT=${CLOUD_INIT:-$(dirname "$0")/runner-cloud-init.yaml} +# Operator public keys authorized on every builder (JSON array of strings). +# The box's env file carries them; empty means no root login. +SSH_KEYS_JSON=${SSH_KEYS_JSON:-[]} +LOCK=${LOCK:-/tmp/omarchy-controller.lock} + +log() { echo "$(date '+%F %T') $*"; } + +# The only two places the outside world is touched. The self-test overrides +# both, so every decision below is exercised against canned responses. +do_api() { # do_api [curl args...] + local path=$1; shift + curl -fsS -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ + -H "Content-Type: application/json" "https://api.digitalocean.com/v2/$path" "$@" +} +gh_api() { # gh_api [curl args...] + local path=$1; shift + curl -fsS -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" "https://api.github.com/$path" "$@" +} + +# --- reap ------------------------------------------------------------------ +reap() { + local now id status created age + now=$(date +%s) + while read -r id status created; do + [[ -n "$id" ]] || continue + age=$(( (now - $(date -d "$created" +%s)) / 60 )) + if [[ $status == off ]] || (( age > MAX_AGE_MINUTES )); then + log "deleting droplet $id (status=$status age=${age}m)" + do_api "droplets/$id" -X DELETE + fi + done < <(do_api "droplets?tag_name=$TAG&per_page=200" | + jq -r '.droplets[] | "\(.id) \(.status) \(.created_at)"') +} + +# --- demand ---------------------------------------------------------------- +queued_jobs() { + local run + gh_api "repos/$REPO/actions/runs?status=queued&per_page=50" --get \ + | jq -r '.workflow_runs[].id' | + while read -r run; do + gh_api "repos/$REPO/actions/runs/$run/jobs" \ + | jq -r --arg l "$LABEL" '.jobs[] | select(.status=="queued") | select(.labels | index($l)) | .id' + done | wc -l +} + +live_droplets() { + do_api "droplets?tag_name=$TAG&per_page=200" | jq '[.droplets[] | select(.status != "off")] | length' +} + +busy_runners() { + gh_api "repos/$REPO/actions/runners?per_page=100" \ + | jq --arg l "$LABEL" '[.runners[] | select(.busy) | select(any(.labels[]; .name == $l))] | length' +} + +# --- create ---------------------------------------------------------------- +create_droplet() { + local token userdata name body + token=$(gh_api "repos/$REPO/actions/runners/registration-token" -X POST | jq -r .token) + userdata=$(sed -e "s|__REPO__|$REPO|g" -e "s|__RUNNER_TOKEN__|$token|g" \ + -e "s|__RUNNER_LABELS__|$LABEL|g" -e "s|__RUNNER_VERSION__|$RUNNER_VERSION|g" \ + -e "s|__SSH_KEYS_JSON__|$SSH_KEYS_JSON|" "$CLOUD_INIT") + name="$TAG-$(date +%s)-$RANDOM" + body=$(jq -n --arg name "$name" --arg region "$REGION" --arg size "$SIZE" --arg image "$IMAGE" \ + --arg tag "$TAG" --arg ud "$userdata" \ + '{name:$name, region:$region, size:$size, image:$image, tags:[$tag], user_data:$ud, monitoring:false}') + log "creating $name ($SIZE)" + do_api droplets -X POST -d "$body" | jq -r '"created droplet \(.droplet.id)"' +} + +controller_tick() { + reap + local queued live busy available need room + queued=$(queued_jobs) + live=$(live_droplets) + busy=$(busy_runners) + # A live droplet whose runner is busy is spoken for. Only droplets still + # booting or listening can absorb a queued job. + available=$(( live - busy )); (( available < 0 )) && available=0 + need=$(( queued - available )) + (( need > 0 )) || return 0 + room=$(( MAX_DROPLETS - live )) + (( need > room )) && need=$room + if (( need <= 0 )); then + log "at cap ($live/$MAX_DROPLETS, $busy busy) with $queued queued" + return 0 + fi + local i + for (( i = 0; i < need; i++ )); do create_droplet; done +} + +if [[ "${CONTROLLER_LIBRARY_ONLY:-}" != 1 ]]; then + exec 9>"$LOCK"; flock -n 9 || exit 0 + controller_tick +fi diff --git a/ci/runner-cloud-init.yaml b/ci/runner-cloud-init.yaml new file mode 100644 index 0000000..c05e32e --- /dev/null +++ b/ci/runner-cloud-init.yaml @@ -0,0 +1,81 @@ +#cloud-config +# Ephemeral GitHub Actions runner for omarchy-pkgs package builds. +# +# Boots an Ubuntu droplet, installs docker and the runner, registers ONCE with +# --ephemeral, runs exactly one job, then powers off. The controller (or the +# reaper) deletes the powered-off droplet. Nothing here holds a long-lived +# credential: the registration token is single-use and expires in an hour. +# +# Substitute before use: +# __REPO__ owner/name +# __RUNNER_TOKEN__ registration token (gh api -X POST repos/O/R/actions/runners/registration-token) +# __RUNNER_LABELS__ e.g. omarchy-builder +# __RUNNER_VERSION__ e.g. 2.329.0 + +# Operators can reach a builder by key while it lives; it powers off after +# one job anyway. Keys are substituted by the controller (__SSH_KEYS_JSON__). +disable_root: false +chpasswd: + expire: false +ssh_authorized_keys: __SSH_KEYS_JSON__ + +package_update: true +packages: + - docker.io + - docker-buildx + - unzip + - git + - curl + - jq + - rsync + +users: + - name: runner + groups: [docker] + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + +write_files: + # defer: write after users/groups exist, so /home/runner is created by + # useradd (owned by runner) rather than by this module as root. + - path: /home/runner/start.sh + permissions: "0755" + owner: runner:runner + defer: true + content: | + #!/bin/bash + set -euo pipefail + cd /home/runner + mkdir -p actions-runner && cd actions-runner + arch=$(uname -m); [[ $arch == x86_64 ]] && arch=x64 + curl -fsSL -o runner.tgz \ + "https://github.com/actions/runner/releases/download/v__RUNNER_VERSION__/actions-runner-linux-${arch}-__RUNNER_VERSION__.tar.gz" + tar xzf runner.tgz && rm runner.tgz + ./config.sh --unattended --ephemeral \ + --url "https://github.com/__REPO__" \ + --token "__RUNNER_TOKEN__" \ + --name "do-$(hostname)" \ + --labels "__RUNNER_LABELS__" \ + --replace + ./run.sh + # One job done. Power off; the controller deletes powered-off droplets. + sudo poweroff + +runcmd: + # With no account ssh key attached, DO expires root's password, and sshd + # then refuses every non-interactive session. Clear it first so operators + # can read the logs of a builder that never registers. + - chage -d "$(date +%F)" -M -1 root + - systemctl enable --now docker + # aarch64 builds run under user-mode emulation (DO has no arm droplets). + # Register QEMU with the F and C flags via tonistiigi/binfmt, exactly as + # helpers/docker-helpers.sh setup_qemu does. Ubuntu's qemu-user-static + # registers without C, so sudo inside the emulated container fails with + # "effective uid is not 0"; multiarch/qemu-user-static is abandoned at QEMU + # 7.2, under which qmake's compiler probe returns nothing on current gcc + # ("failed to parse default include paths", PR #517). Pin the emulator + # version: the tag is the only thing that decides what every aarch64 build + # runs under. Best-effort: an x86-only job never needs it. + - docker run --rm --privileged docker.io/tonistiigi/binfmt:qemu-v10.2.3-68 --uninstall qemu-aarch64 --install arm64 || true + - chown -R runner:runner /home/runner + - sudo -u runner /home/runner/start.sh > /home/runner/runner.log 2>&1 diff --git a/docs/upstream-sources.md b/docs/upstream-sources.md index 9276bd2..e57f546 100644 --- a/docs/upstream-sources.md +++ b/docs/upstream-sources.md @@ -168,6 +168,8 @@ in `origin` and has no effect on release selection. These packages were already excluded from automatic AUR updates. The migration preserves that policy. +`linux-firmware-cirrus` is a deliberate hold: a self-retiring shim that ships Arch's linux-firmware-cirrus 20260910-2 payload to stable while stable's Arch snapshot is on 20260810-2 (Dell XPS 13 DX13260 / 1028:0e54 speaker firmware). It is versioned 20260810-3 so the genuine Arch package supersedes it as soon as the snapshot advances; bumping it to the Arch version would defeat that. Delete the recipe once stable's snapshot carries linux-firmware >= 20260910. + ## Package-specific boundaries - NVIDIA watches remain on the 580 driver branch. diff --git a/helpers/artifact-helpers.sh b/helpers/artifact-helpers.sh new file mode 100644 index 0000000..3d8bb4f --- /dev/null +++ b/helpers/artifact-helpers.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Package files cross from a PR build to publish.yml as one GitHub Actions +# artifact. actions/upload-artifact rejects any path containing ':', and a +# package with an epoch is named `name-1:ver-rel-arch.pkg.tar.zst` by +# makepkg. So the files ride inside a tar with a plain name and keep their +# own names untouched: pacman clients and bin/publish-artifact both rely on +# the filename matching PKGINFO. +# +# Both functions run under the workflow's `bash -e`: nothing in them may +# return non-zero except the final failure. + +# package_files : the *.pkg.tar.zst directly in , one per line. +# Signatures and the scratch database next to them are not packages. +package_files() { + local f + for f in "$1"/*.pkg.tar.zst; do + [[ -e "$f" ]] && printf '%s\n' "$f" + done + return 0 +} + +# pack_packages : every package in into . +pack_packages() { + local dir=$1 out=$2 files=() + mapfile -t files < <(package_files "$dir") + (( ${#files[@]} )) || { echo "pack_packages: no *.pkg.tar.zst in $dir" >&2; return 1; } + tar -cf "$out" -C "$dir" -- "${files[@]##*/}" +} + +# unpack_packages : the packages an unzipped artifact +# carried, into . Packed artifacts hold packages.tar; artifacts from +# builds before packing hold the bare files. The bare form can go once +# those artifacts have expired (7-day retention). +unpack_packages() { + local src=$1 dest=$2 files=() + mkdir -p "$dest" + if [[ -f "$src/packages.tar" ]]; then + tar -xf "$src/packages.tar" -C "$dest" + return 0 + fi + mapfile -t files < <(package_files "$src") + (( ${#files[@]} )) || { echo "unpack_packages: nothing to unpack in $src" >&2; return 1; } + cp -- "${files[@]}" "$dest/" +} diff --git a/helpers/docker-helpers.sh b/helpers/docker-helpers.sh index cc18222..22c8004 100644 --- a/helpers/docker-helpers.sh +++ b/helpers/docker-helpers.sh @@ -91,8 +91,19 @@ setup_qemu() { exit 1 fi - # Register emulators for builds whose target differs from the host. - if ! "$CONTAINER_ENGINE" run --rm --privileged docker.io/multiarch/qemu-user-static --reset -p yes --credential yes >/dev/null 2>&1; then + # Register emulators for builds whose target differs from the host, with + # the F and C flags (tonistiigi/binfmt always sets both). The image tag pins + # the QEMU version every emulated build runs under; multiarch/qemu-user-static + # stopped at QEMU 7.2, which breaks qmake's compiler probe on current gcc. + # Keep ci/runner-cloud-init.yaml on the same tag. Uninstall first: install + # leaves an existing registration (an older emulator) in place and exits 0. + local platform_arch + case "$target_arch" in + aarch64) platform_arch=arm64 ;; + x86_64) platform_arch=amd64 ;; + *) platform_arch="$target_arch" ;; + esac + if ! "$CONTAINER_ENGINE" run --rm --privileged docker.io/tonistiigi/binfmt:qemu-v10.2.3-68 --uninstall "qemu-$target_arch" --install "$platform_arch" >/dev/null 2>&1; then print_error "Failed to set up QEMU emulation" exit 1 fi diff --git a/helpers/package-metadata.sh b/helpers/package-metadata.sh index a3ad047..ce46e92 100644 --- a/helpers/package-metadata.sh +++ b/helpers/package-metadata.sh @@ -191,6 +191,18 @@ package_supports_arch() { esac } +# The channel DB indexes only its newest version, but older published archives +# remain immutable. Both the scheduler and build planner must skip an existing +# filename even when the checkout differs from the version currently indexed. +package_version_is_published() { + local repo_dir="$1" package="$2" version="$3" target="$4" path + for path in "$repo_dir/$package-$version-$target.pkg.tar."* \ + "$repo_dir/$package-$version-any.pkg.tar."*; do + [[ -f "$path" && "$path" != *.sig ]] && return 0 + done + return 1 +} + # Channel membership: where a package may be published. Packages without a # `channels` key are members of every channel (they flow edge -> rc -> stable). package_has_channels() { diff --git a/pkgbuilds/aether/PKGBUILD b/pkgbuilds/aether/PKGBUILD index 8927657..cef077c 100644 --- a/pkgbuilds/aether/PKGBUILD +++ b/pkgbuilds/aether/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Bjarne Øverli pkgname=aether -pkgver=4.29.8 +pkgver=4.30.0 pkgrel=1 pkgdesc='Desktop theming application - extract colors from wallpapers and apply cohesive themes' arch=('x86_64' 'aarch64') @@ -10,9 +10,9 @@ depends=('webkit2gtk-4.1' 'gtk3') source=("aether-${pkgver}.tar.gz::https://github.com/omacom/aether/archive/refs/tags/v${pkgver}.tar.gz") source_x86_64=("aether-linux-amd64-${pkgver}::https://github.com/omacom/aether/releases/download/v${pkgver}/aether-linux-amd64") source_aarch64=("aether-linux-arm64-${pkgver}::https://github.com/omacom/aether/releases/download/v${pkgver}/aether-linux-arm64") -sha256sums=('b83e0eeb1332b4ed655389a051d5b9b14a3e109968b7f278005e52c5e69a1e9c') -sha256sums_x86_64=('d3d2d07b32da7a495221ed271ee66f4a1e344c91dcec9b267ec0a74ab6e36462') -sha256sums_aarch64=('ffcfd23d0375a3f0c0ce014821cc5e1670f2e061c35e991340e75352dac9b731') +sha256sums=('f67c8d2c6f27f67a755bc279ece5ddb194f1bb165648280b9a7be86904d36ff5') +sha256sums_x86_64=('75bda600ddd3ecab3338de5c0c5d5e2c9f08cfc0c465b63f8e6cb9c5cb60d68e') +sha256sums_aarch64=('a91d800736def74d86e19d8acbecc4bda3d7c3e64fb95273f809707104c3a5bc') noextract=("aether-linux-amd64-${pkgver}" "aether-linux-arm64-${pkgver}") package() { diff --git a/pkgbuilds/avd-fw/PKGBUILD b/pkgbuilds/avd-fw/PKGBUILD index b24e65e..401e118 100644 --- a/pkgbuilds/avd-fw/PKGBUILD +++ b/pkgbuilds/avd-fw/PKGBUILD @@ -5,9 +5,9 @@ pkgname=avd-fw pkgver=0.1 pkgrel=1 pkgdesc='Open replacement firmware for the Apple Video Decoder (AVD) on Apple Silicon' -# The images are raw Cortex-M3 firmware, identical whatever host builds them, -# so this is an 'any' package (as linux-firmware is) rather than an aarch64 one. -arch=('any') +# This recipe uses the native ARM linker and serves Apple Silicon systems. +# Restrict both builds and publication to aarch64. +arch=('aarch64') url='https://github.com/AsahiLinux/avd-fw' license=('MIT') # clang cross-compiles to arm-none-eabi out of the box, so no arm-none-eabi diff --git a/pkgbuilds/bambustudio-bin/.omarchy/package.json b/pkgbuilds/bambustudio-bin/.omarchy/package.json new file mode 100644 index 0000000..ffa5b62 --- /dev/null +++ b/pkgbuilds/bambustudio-bin/.omarchy/package.json @@ -0,0 +1,17 @@ +{ + "source": "local", + "release_ring": "fast", + "origin": { + "aur": "bambustudio-bin", + "commit": "b962c12d14873f94669e0e256a444f957b1843cd" + }, + "upstream": { + "watch": { + "regex": "https://api.github.com/repos/bambulab/BambuStudio/releases/latest", + "pattern": "\"name\"\\s*:\\s*\"BambuStudio_ubuntu24\\.04-v(?P[0-9]+(?:\\.[0-9]+)*)-(?P[0-9]+)\\.AppImage\"", + "variables": { + "_build": "{build}" + } + } + } +} diff --git a/pkgbuilds/bambustudio-bin/BambuStudio.desktop b/pkgbuilds/bambustudio-bin/BambuStudio.desktop new file mode 100644 index 0000000..74ab062 --- /dev/null +++ b/pkgbuilds/bambustudio-bin/BambuStudio.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=Bambu Studio +GenericName=3D Printing Software +Comment=Slicer for Bambu Lab and other 3D printers +Exec=/usr/bin/bambu-studio %U +Icon=BambuStudio +Terminal=false +Type=Application +Categories=Graphics;3DGraphics;Engineering; +MimeType=x-scheme-handler/bambustudio;x-scheme-handler/bambustudioopen;model/stl;model/3mf;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf; +Keywords=3D;Printing;Slicer;gcode;stl;3mf; +StartupWMClass=bambu-studio diff --git a/pkgbuilds/bambustudio-bin/PKGBUILD b/pkgbuilds/bambustudio-bin/PKGBUILD new file mode 100644 index 0000000..f1a7c64 --- /dev/null +++ b/pkgbuilds/bambustudio-bin/PKGBUILD @@ -0,0 +1,48 @@ +# Maintainer: goll +# Contributor: George Woodall +pkgname=bambustudio-bin +pkgver=02.08.02.61 +pkgrel=1 +pkgdesc="PC Software for BambuLab's 3D printers" +arch=("x86_64") +url="https://github.com/bambulab/BambuStudio" +license=('AGPL-3.0-only') +conflicts=('bambustudio' 'bambustudio-git') +depends=('cairo' 'dbus' 'fontconfig' 'gcc-libs' 'glib2' 'glibc' + 'gst-libav' 'gst-plugins-base-libs' 'gstreamer' 'gtk3' 'libglvnd' + 'libx11' 'mesa' 'pango' 'wayland' 'webkit2gtk-4.1') +makedepends=('7zip') +options=('!strip' '!debug') +# The upstream watch updates the timestamp together with pkgver. +_build=20260820225108 +source=("bambustudio-${pkgver}.AppImage::https://github.com/bambulab/BambuStudio/releases/download/v${pkgver}/BambuStudio_ubuntu24.04-v${pkgver}-${_build}.AppImage" + "BambuStudio.desktop" + "bambu-studio") +noextract=("bambustudio-${pkgver}.AppImage") +sha256sums=( + 'd501b103fac5424513ec0e8d6bc145fb30719de2c7d94d7320d723740c81a7fd' + 'f10718a8b201cad64800746fe8167ccc032c545d05f7ad8caa99eb5fb975f2a1' + 'a3a5c8f6a8b287e42b93957e9602621923c766e0b6a3f10c14eca10b023b15f2' +) + +prepare() { + # Read the embedded SquashFS without executing or modifying the AppImage. + rm -rf "$srcdir/squashfs-root" + 7z x "$srcdir/bambustudio-${pkgver}.AppImage" -o"$srcdir/squashfs-root" >/dev/null +} + +package() { + cd "$srcdir/squashfs-root" + install -Dm755 AppRun "$pkgdir/opt/$pkgname/AppRun" + cp -a bin resources "$pkgdir/opt/$pkgname/" + + local icon size + for icon in usr/share/icons/hicolor/*/apps/BambuStudio.png; do + size="${icon#usr/share/icons/hicolor/}" + install -Dm644 "$icon" "$pkgdir/usr/share/icons/hicolor/$size" + done + + install -Dm755 "$srcdir/bambu-studio" "$pkgdir/usr/bin/bambu-studio" + install -Dm644 "$srcdir/BambuStudio.desktop" \ + "$pkgdir/usr/share/applications/BambuStudio.desktop" +} diff --git a/pkgbuilds/bambustudio-bin/bambu-studio b/pkgbuilds/bambustudio-bin/bambu-studio new file mode 100755 index 0000000..d1d3fb9 --- /dev/null +++ b/pkgbuilds/bambustudio-bin/bambu-studio @@ -0,0 +1,2 @@ +#!/bin/bash +exec "/opt/bambustudio-bin/AppRun" "$@" diff --git a/pkgbuilds/claude-code/PKGBUILD b/pkgbuilds/claude-code/PKGBUILD index 700db29..10c950a 100644 --- a/pkgbuilds/claude-code/PKGBUILD +++ b/pkgbuilds/claude-code/PKGBUILD @@ -4,7 +4,7 @@ # Automation repository: https://github.com/fabifont/claude-code-aur pkgname=claude-code -pkgver=2.1.272 +pkgver=2.1.278 pkgrel=1 pkgdesc="An agentic coding tool that lives in your terminal" arch=('x86_64' 'aarch64') @@ -29,8 +29,8 @@ source_x86_64=("claude-${pkgver}-x86_64::https://downloads.claude.ai/claude-code source_aarch64=("claude-${pkgver}-aarch64::https://downloads.claude.ai/claude-code-releases/${pkgver}/linux-arm64/claude") sha256sums=('SKIP') -sha256sums_x86_64=('d81396a668eb76fbddb49a2a5841f1b5d7af96b4c1f6500ced92f2c988f5bcd4') -sha256sums_aarch64=('214a90efdd16ee0ea81132ffecced588dba394d178cc494f285ba04b5288c8de') +sha256sums_x86_64=('5c4735937844e84f8a93306e841a5b0e12252909b07870f789b190468da147ab') +sha256sums_aarch64=('7de6cab134e48321148e30182c98614118e8f4666819412bead45865190b34ed') package() { install -Dm755 "${srcdir}/claude-${pkgver}-${CARCH}" "${pkgdir}/opt/claude-code/bin/claude" diff --git a/pkgbuilds/claude-desktop/PKGBUILD b/pkgbuilds/claude-desktop/PKGBUILD index c7728fb..919b043 100644 --- a/pkgbuilds/claude-desktop/PKGBUILD +++ b/pkgbuilds/claude-desktop/PKGBUILD @@ -6,7 +6,7 @@ # repository's package index. pkgname=claude-desktop -pkgver=1.52386.6 +pkgver=2.2553.1 pkgrel=1 pkgdesc="Official Claude desktop app with Claude Code" arch=('x86_64' 'aarch64') @@ -63,8 +63,8 @@ source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}") source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}") noextract=("${_deb_x86_64}" "${_deb_aarch64}") sha256sums=('edfdbc63b65891ef7c481b07086c7e630fc102c042b6ed65331a52fcaf72b14a') -sha256sums_x86_64=('2e83a76c6ed9187671bfe80664fc6d59840171f4a2a81f408662c879a67f4e0a') -sha256sums_aarch64=('882f4a52a86b07ecff989d8db87c5ec292d43f21d0a6557d3e8b01e113b18190') +sha256sums_x86_64=('6700fdd84e77a6b8c93912c2f69eb5d1e40fa99bcd9d37f438f809ef2a6fe6f8') +sha256sums_aarch64=('0003a6f9605a210f03c38670d62cd59c71153c2702aa4427e4cabe2e2e5f3390') package() { cd "${srcdir}" diff --git a/pkgbuilds/crush-bin/PKGBUILD b/pkgbuilds/crush-bin/PKGBUILD index 6c3d0ba..6a95b22 100644 --- a/pkgbuilds/crush-bin/PKGBUILD +++ b/pkgbuilds/crush-bin/PKGBUILD @@ -3,7 +3,7 @@ # Maintainer: caarlos0 pkgname='crush-bin' -pkgver=0.94.2 +pkgver=0.96.0 pkgrel=1 pkgdesc='A powerful terminal-based AI assistant for developers, providing intelligent coding assistance directly in your terminal.' url='https://charm.sh/crush' @@ -13,16 +13,16 @@ provides=('crush') conflicts=('crush') source_aarch64=("${pkgname}_${pkgver}_aarch64.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_arm64.tar.gz") -sha256sums_aarch64=('3a9d00d135632b6b3f8821814ceb81512839d346d628ebac23a445b126e1f51c') +sha256sums_aarch64=('667062a39d499506b0fe151148f8d7a1c5cb5722902080d44bb3dd2ddafbf5c1') source_armv7h=("${pkgname}_${pkgver}_armv7h.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_armv7.tar.gz") -sha256sums_armv7h=('0c183c369af79a5e37e45cc98df2093e3381c1fd4c7fb3dc204d3c765f96f138') +sha256sums_armv7h=('1de4c1ccb237743e4debb8c302df612fcef5c9b3b5378f5b65c8c6f8bc15cbfa') source_i686=("${pkgname}_${pkgver}_i686.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_i386.tar.gz") -sha256sums_i686=('d7b54a61bd112ba8c98b1ddb2229e93d53688d2bed8794cb9e3c6055489620ad') +sha256sums_i686=('4ec66431565de5721afb7afdd99e45ff6bc9c7e667bd18d9696ea7c5622e158d') source_x86_64=("${pkgname}_${pkgver}_x86_64.tar.gz::https://github.com/charmbracelet/crush/releases/download/v${pkgver}/crush_${pkgver}_Linux_x86_64.tar.gz") -sha256sums_x86_64=('50df13841b617956690d3ca2881ed8601798e557ff187513ab7daff355621dc3') +sha256sums_x86_64=('5b33303a404acacf761c027e9fa9e69d4d2dd050c2690abe40877c49574b7475') package() { case "$CARCH" in diff --git a/pkgbuilds/cua-driver-bin/PKGBUILD b/pkgbuilds/cua-driver-bin/PKGBUILD index 07ce954..f9679fb 100644 --- a/pkgbuilds/cua-driver-bin/PKGBUILD +++ b/pkgbuilds/cua-driver-bin/PKGBUILD @@ -18,7 +18,7 @@ # binary to point at pm.sh, a stand-in that declines and names pacman instead. pkgname=cua-driver-bin -pkgver=0.28.1 +pkgver=0.28.2 pkgrel=1 pkgdesc="Computer-use driver for native GUI apps: accessibility-tree snapshots and input injection" arch=('x86_64' 'aarch64') @@ -46,8 +46,8 @@ source_x86_64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v$ source_aarch64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v${pkgver}/cua-driver-rs-${pkgver}-linux-arm64.tar.gz") sha256sums=('c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9' 'c76e251c3ed424200eac52bec35ba534336307fabd83a175ab0b47e2084ab0d8') -sha256sums_x86_64=('a068b6e477893b77ced74bceccf7db7483cf140e8d54150ce5849b6252b90bcf') -sha256sums_aarch64=('a863951ef0699fd25091adb87bd114d69709b887fdfc059e795aca49ef8ac19c') +sha256sums_x86_64=('8f3e5b669e2bcd98d0eecc64f40640aac77f358b6332a06abc6ee79991620f7d') +sha256sums_aarch64=('cadd7e6b757c3ce50f2b5f6e273c154ea48450fb5fcaff744209b382915eddf5') case "${CARCH}" in x86_64) _platform="linux-x86_64" ;; diff --git a/pkgbuilds/cua-hyprland-plugin/DOWNSTREAM-PROVENANCE.json b/pkgbuilds/cua-hyprland-plugin/DOWNSTREAM-PROVENANCE.json new file mode 100644 index 0000000..7cf8f23 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/DOWNSTREAM-PROVENANCE.json @@ -0,0 +1,57 @@ +{ + "files": { + "CMakeLists.txt": "7e874a595e1abd708cb0626bd9f0f58d79b4b4bbc6d99b45a0cbe1c5c53b43bc", + "LICENSE.md": "c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9", + "SOURCE-PROVENANCE.json": "54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75", + "cmake/DetectHyprlandAPI.cmake": "216133ec0eb141c3696bf3770a23e63e46521c9e91a0245a7cb75c20a75ba2c5", + "cmake/VerifyRuntime.cmake": "5869f79a418e7aa6178d2b9166c36cd01c3093c2579b647624968244db57b761", + "include/cua_hyprland/protocol.hpp": "7051463b3c61a2dc93c388e66b6136b7bb524f8694350f249f9d2b5c55826493", + "include/cua_hyprland/session.hpp": "e6a968e4f2ac28122cb7413a0e318f6222d2a0a1b7b0c45f4ab419a78639ebea", + "include/cua_hyprland/status.hpp": "56a9656647c0f4eeb0c588cd4b98a77df198d1f90421f973e9a80e6802f61495", + "src/drag_geometry.hpp": "c5b783d15ff197f22938f08f8d176bab5d45544fe989d150f15cb99295acedb8", + "src/foreground_route.hpp": "4aa016c237b33c15e352a9f5f64bbbca7e1a9c1671ffd593a95a0d87994fb519", + "src/inject_server.cpp": "0935283580c50fcf0e4ad956f858885700536002b1d86d2f078da9c4404ee9e8", + "src/inject_server.hpp": "67de008b4d6983371207bb22a57bab154b1dedda9b38d1207d3ac8cb379382eb", + "src/input_client_deadline.hpp": "00a91a789ff698820607449ff7152e2fb50d0f315e0fa9c5c3855752bfffe2ef", + "src/input_experiment.cpp": "7017748c782b64b0bc1d257f4ade1a8d46fd19ce940ecc21f22d628614fcef37", + "src/input_experiment.hpp": "9da2ddab7f0de6e9cf02aea53e9119acef17ef8513af849bb5aa3d137ebd12c3", + "src/input_grant.hpp": "90b544b2f559bacd920b85ab5915f09ff201de3c05a052ce1b71ff71f767e6a6", + "src/keyboard_layout.hpp": "bc2ec039ac1974caebbb66fe4acb7a2a81832c9054b3aa644a9adcc8954a2467", + "src/owned_socket_path.hpp": "8e784656d944c700f3ded383c93a8cadf846dabdaa4cc12673bcb637c26d9fd1", + "src/passive_pointer_target.hpp": "ede36fd9fd6e95ae5923c751f12591084392be9d2270eb06ad64eb4f245169fa", + "src/plugin.cpp": "712fd73ef8e9046e0fd91531b7bf5da50ce37ccca9df174160137e1924a74b09", + "src/primary_trace.cpp": "e9468d1a3f3be2a90d47bf8c4a638ad8a60fe10b6297582ab7825751cb707aa9", + "src/primary_trace.hpp": "8d62535fb0b24a02bb80d9a8dcd540b39204afb2f3b4bcb5cabd5275c3b5eaa7", + "src/protocol.cpp": "bd083d65efb05e80946566dc535b1a6fadaa581c66ec327eff41796feba795b1", + "src/seat_lifetime.hpp": "386cf5c72c178f8eec0824f2a7d46fa755b0bb000861f9a6e00802f6b81fb779", + "src/session.cpp": "0105c7ba5f9e2dbdd9a21f48be0bc1f2bde930f6aa19f77e2216403d7790e4df", + "src/status.cpp": "e46e81e5e8ae3b1f50af5dcaa6c1776e236321c788fe61b402c9923c797b1270", + "tests/agent_keymap_test.py": "9b112f520a97a77a0d55f1009cd2594988d3c1e8e7bcaa39f213ffad2644dc56", + "tests/cmake-api/CMakeLists.txt": "6a66c8f98023f029998af917fcd3ec6b1388607bdf7acaf5bdfaef9141962685", + "tests/cmake-api/include/src/plugins/PluginAPI.hpp": "86c8ad51e668908d18928cef1b04e8e6d32a33894525640b52f0b31769c870a0", + "tests/desktop_fault_policy_fixture.cpp": "ac24d675ebc64cc98148e852eb5aba5858bffc06332678d14276d04b317aaf15", + "tests/desktop_fault_policy_test.py": "c3f624c0239036babd23eaaf1bb6b722f3a0c3321a5d3d6668ae7ab4910ca95c", + "tests/drag_geometry_test.cpp": "d32ea649d008fc051fe18555d6fbc54ba5d057b61880b648c5df8aa076a53fbc", + "tests/foreground_modifiers_test.py": "abf0ddde2d51c6639c8bdca8fdda51cdc8f922b57575a00fc8925c683ccc3bb2", + "tests/foreground_route_test.cpp": "1024168828b13ee6abd8242941e73c042e9381b39108e3ada74823039c7e7932", + "tests/input_client_deadline_test.cpp": "d62373a7815d531f1269c9a838773595f43e8bcef6482fa140edb162e59a6cac", + "tests/input_grant_test.cpp": "1f327b7ee678189ebad6a50bb1b9bd06767521cebc9cfb478d92de4a8bf7e7fe", + "tests/keyboard_layout_test.cpp": "3bb0fade4675d7ad92a81eb4a1c5201dd1d01bcc418b7ea59a4284dc235e5fb2", + "tests/mock-hyprland/mock.hpp": "3aeb1a4b9d6b83506b66c129d3fa812330fad4509fe218a0dbb99dd2bb5b6319", + "tests/mock-hyprland/src/config/values/types/BoolValue.hpp": "47cf2cca89f71a273573968cb9b8ba46a1496841d6c892756ebf123500a7ecb3", + "tests/mock-hyprland/src/plugins/PluginAPI.hpp": "5654d90ec9090a88bea3d31f8a79617d4c79742b09068648e64448319d395110", + "tests/owned_socket_path_test.cpp": "eaff6b5c6f148eca6c8650ee3dc212a5e892f002ee4f68b2a1290c7205ee7e42", + "tests/passive_pointer_target_test.cpp": "2aeef1de1dc8932b26ab8c41b83fb16a4289ff96c177a5816088f07b9a168948", + "tests/plugin_api_test.cpp": "1e7e200c309996ee945c88e172273ae942be2837e24564d422dee79d8b77d8a2", + "tests/plugin_input_lifetime_test.cpp": "82e57b335ea1216ea24cca07fe4feebafebdebb779785fde20b8dab6ee222e1b", + "tests/protocol_test.cpp": "119cfe0df81c0c00036a2d181764eda7601d6ee72459c2275a96226d4f670447", + "tests/seat_lifetime_test.cpp": "b07570edbe0a142f97c54560eeb93e8327c435ab3b8cbc7496d55175e387b78a", + "tests/status_test.cpp": "b8990efc53ec3820cfe498c920b9220c4b70615ad585468558e032e7619e32f3", + "tests/transport_test.cpp": "deef114a950a27eaff0a530165ddf7db0bfc0fe7e8fb55bdbb66135c8e0c04c9", + "verify.py": "fb35d62313ff4661f892f88666919b33b160f8b6d4fb2d5d52610708bf7f4a54" + }, + "patch_sha256": "e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7", + "schema": 1, + "upstream_manifest_sha256": "54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75", + "upstream_revision": "cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7" +} diff --git a/pkgbuilds/cua-hyprland-plugin/PKGBUILD b/pkgbuilds/cua-hyprland-plugin/PKGBUILD index cfcf472..9e0f9b0 100644 --- a/pkgbuilds/cua-hyprland-plugin/PKGBUILD +++ b/pkgbuilds/cua-hyprland-plugin/PKGBUILD @@ -1,21 +1,21 @@ -# Verified upstream kit with a local package-revision profile; source/tooling are unchanged. +# Verified upstream kit plus a separately pinned Omarchy keyboard-remap patch. # Normal reruns need a fresh build directory; makepkg -e reuses verified extracted trees. # Profile kit: original source bytes and separately committed packaging tooling. # shellcheck shell=bash disable=SC2034,SC2154 pkgname=cua-hyprland-plugin pkgver=0.26.1 -pkgrel=3 -pkgdesc='Cua input candidate for reviewed profile omarchy-hyprland-0562r3' +pkgrel=6 +pkgdesc='Cua input candidate for reviewed profile omarchy-hypr0562r3-aq0151-remaps' arch=('x86_64') url='https://github.com/trycua/cua' license=('MIT') -depends=('hyprland=0.56.2-3' 'aquamarine=0.15.0-2' 'glibc=2.44+r24+g16be1518495f-1' 'hyprcursor=0.1.13-7' 'hyprgraphics=0.5.1-4' 'hyprlang=0.6.8-5' 'hyprutils=0.14.2-1' 'libgcc=16.2.1+r23+gd564253eb6c8-1' 'libstdc++=16.2.1+r23+gd564253eb6c8-1' 'libxkbcommon=1.13.2-1' 'wayland=1.26.0-1' 'python>=3.11' 'binutils') -makedepends=('cmake>=3.30' 'ninja' 'pkgconf' 'gcc') +depends=('hyprland=0.56.2-3' 'aquamarine=0.15.1-1' 'glibc=2.44+r24+g16be1518495f-1' 'hyprcursor=0.1.13-7' 'hyprgraphics=0.5.1-4' 'hyprlang=0.6.8-5' 'hyprutils=0.14.2-1' 'libgcc=16.2.1+r23+gd564253eb6c8-1' 'libstdc++=16.2.1+r23+gd564253eb6c8-1' 'libxkbcommon=1.13.2-1' 'wayland=1.26.0-1' 'python>=3.11' 'binutils') +makedepends=('cmake>=3.30' 'ninja' 'pkgconf' 'gcc' 'patch') options=('!strip' '!debug' '!lto') _stem='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7' _archive_sha256='47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab' -_kit_sha256='1391084a903254fb16d251522dba41ae34a06cfa4b2f6b7ba91a3cceeb895921' -_profile_sha256='eebba812a7513b471cf8969364334a4bde1eaa9c4519157701c771c1b6cd1b07' +_kit_sha256='819779b93655d603d9ebb0d33ea052326c3374674a1d473886106af25e0fffdd' +_profile_sha256='a2eade8b8195036be2c666788a1d28ac722403285a35987d92b3344e095f9a5e' _verifier_sha256='480f649cbd35a0ddc7f49dc0a3a44785402c9e0ec9653b0f8ea4965d52d7f900' _cxx="${CUA_RELEASE_CXX:-/usr/bin/g++}" _download_name='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7-profile-omarchy-stable-20260910-kit-1.1.0-5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a-67f89ceb47edd973aa748820eadd02ddbfb3d3e4.tar.gz' @@ -24,7 +24,17 @@ source=('https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0 'PROFILE.json') noextract=("$_download_name") sha256sums=('a89bfa7f6490f598719dfabdd2a5badc137e27b0764fd25e6a05e1f969433520' - 'eebba812a7513b471cf8969364334a4bde1eaa9c4519157701c771c1b6cd1b07') + 'a2eade8b8195036be2c666788a1d28ac722403285a35987d92b3344e095f9a5e') + +# Downstream inputs are also checked explicitly when makepkg integrity is skipped. +declare -gA _downstream_sha256=( + ['independent-keymaps.patch']='e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7' + ['DOWNSTREAM-PROVENANCE.json']='e0c95350ec3ff2dd54a05e9377d79ddffffdb1cf494d05553a90207a3a919f7e' + ['downstream.py']='7c9725805ad038737bd4d346c85f27991ce9ddfff436a32ebf515cad1bf98ca1' + ['downstream_test.py']='7b9a992979b22a5e061fdf011ac6be59a3f1ae6e0d7c0a8ff631f74e71984c5a' +) +source+=('independent-keymaps.patch' 'DOWNSTREAM-PROVENANCE.json' 'downstream.py' 'downstream_test.py') +sha256sums+=('e74d9021111a5c217b12f3e13c1bbdd260e829c60db5fc2f7293ba4846e1a7d7' 'e0c95350ec3ff2dd54a05e9377d79ddffffdb1cf494d05553a90207a3a919f7e' '7c9725805ad038737bd4d346c85f27991ce9ddfff436a32ebf515cad1bf98ca1' '7b9a992979b22a5e061fdf011ac6be59a3f1ae6e0d7c0a8ff631f74e71984c5a') _verify_download() { python3 -I - "$SRCDEST/$_download_name" "$_download_sha256" "$srcdir" "$1" "$SRCDEST/PROFILE.json" <<'CUA_DOWNLOAD_PY' @@ -61,33 +71,34 @@ with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as contents: require(digest(content) == expected[member.name], 'outer kit member checksum mismatch') payload[member.name] = content require(payload.keys() == expected.keys(), 'outer kit inventory mismatch') -# Arch's -3 package has the same compositor and all 498 headers/pkg-config -# files as -2. Derive a version-only profile with the original source/tooling -# and byte checks intact; record its own profile and kit provenance digests. +# Derive the reviewed Hyprland -3/Aquamarine 0.15.1 profile while preserving +# upstream source/tooling and compiler, compositor, header and runtime hashes. profile_data = Path(profile_path).read_bytes() -require(digest(profile_data) == 'eebba812a7513b471cf8969364334a4bde1eaa9c4519157701c771c1b6cd1b07', +require(digest(profile_data) == 'a2eade8b8195036be2c666788a1d28ac722403285a35987d92b3344e095f9a5e', 'local profile checksum mismatch') profile = json.loads(payload['PROFILE.json']) -profile.update(profile_id='omarchy-hyprland-0562r3', package_release=3) +profile.update(profile_id='omarchy-hypr0562r3-aq0151-remaps', package_release=6) profile['hyprland']['package_version'] = '0.56.2-3' -require(json.loads(profile_data) == profile, 'local profile changes more than the reviewed package revision') +profile['runtime']['packages']['aquamarine'] = '0.15.1-1' +require(json.loads(profile_data) == profile, 'local profile changes more than the reviewed package versions') payload['PROFILE.json'] = profile_data provenance = json.loads(payload['KIT-PROVENANCE.json']) provenance['profile_sha256'] = digest(profile_data) payload['KIT-PROVENANCE.json'] = (json.dumps(provenance, sort_keys=True, indent=2) + '\n').encode() recipe = payload['PKGBUILD'].decode() -for old, new in [('pkgrel=2\n', 'pkgrel=3\n'), ('omarchy-stable-20260910', profile['profile_id']), +for old, new in [('pkgrel=2\n', 'pkgrel=6\n'), ('omarchy-stable-20260910', profile['profile_id']), ('hyprland=0.56.2-2', 'hyprland=0.56.2-3'), + ('aquamarine=0.15.0-2', 'aquamarine=0.15.1-1'), ('5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a', digest(profile_data)), ('7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997', digest(payload['KIT-PROVENANCE.json']))]: recipe = recipe.replace(old, new) payload['PKGBUILD'] = recipe.encode() payload['SHA256SUMS'] = ''.join(f'{digest(body)} {name}\n' for name, body in sorted(payload.items()) if name != 'SHA256SUMS').encode() -expected.update({'PROFILE.json': 'eebba812a7513b471cf8969364334a4bde1eaa9c4519157701c771c1b6cd1b07', - 'KIT-PROVENANCE.json': '1391084a903254fb16d251522dba41ae34a06cfa4b2f6b7ba91a3cceeb895921', - 'PKGBUILD': '4c5ef50c5a8d556d461afe283b13b228fb09d2558b37fb76257e13e25846325b', - 'SHA256SUMS': '2ea22af60f8c86df588fd7db103d9f444d9ac4018b704a48ddff2545ff7ce999'}) +expected.update({'PROFILE.json': 'a2eade8b8195036be2c666788a1d28ac722403285a35987d92b3344e095f9a5e', + 'KIT-PROVENANCE.json': '819779b93655d603d9ebb0d33ea052326c3374674a1d473886106af25e0fffdd', + 'PKGBUILD': 'c3e4149eba3f7def10ef700b30b9d0abcea994e3dd32fb169014874a0b75687c', + 'SHA256SUMS': 'd9057a9534f7a820ee04cbf5d60db567c5072fa96d1f71030a6a85b4bb7ce881'}) for name, content in payload.items(): require(digest(content) == expected[name], 'derived kit checksum mismatch: ' + name) require(srcdir.is_dir() and not srcdir.is_symlink(), 'srcdir must be a real directory') @@ -132,17 +143,31 @@ _verify() { printf '%s %s\n' "$_profile_sha256" "$srcdir/cua-profile-kit/PROFILE.json" | sha256sum -c - || return 1 printf '%s %s\n' "$_verifier_sha256" "$srcdir/cua-profile-kit/profile_verify.py" | sha256sum -c - || return 1 python3 "$srcdir/cua-profile-kit/profile_verify.py" --kit "$srcdir/cua-profile-kit" --kit-sha256 "$_kit_sha256" \ - --archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --source "$srcdir/$_stem" --cxx "$_cxx" "$@" + --archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --source "$srcdir/$_stem" --cxx "$_cxx" +} + +_downstream() { + local name + for name in independent-keymaps.patch DOWNSTREAM-PROVENANCE.json downstream.py downstream_test.py; do + printf '%s %s\n' "${_downstream_sha256[$name]}" "$SRCDEST/$name" | sha256sum -c - || return 1 + done + python3 -B "$SRCDEST/downstream.py" "$1" \ + --pristine "$srcdir/$_stem" --source "$srcdir/omarchy-source" \ + --patch "$SRCDEST/independent-keymaps.patch" --manifest "$SRCDEST/DOWNSTREAM-PROVENANCE.json" \ + --kit "$srcdir/cua-profile-kit" --kit-sha256 "$_kit_sha256" \ + --archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --cxx "$_cxx" "${@:2}" } prepare() { _verify_download extract || return 1 - _verify + _verify || return 1 + _downstream prepare } build() { _verify || return 1 - cmake -S "$srcdir/$_stem" -B "$srcdir/build" -G Ninja \ + _downstream check || return 1 + cmake -S "$srcdir/omarchy-source" -B "$srcdir/build" -G Ninja \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER="$_cxx" \ -DPKG_CONFIG_EXECUTABLE=/usr/bin/pkgconf -DPKG_CONFIG_ARGN= \ -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=OFF -DCMAKE_PREFIX_PATH= \ @@ -155,6 +180,8 @@ build() { check() { _verify || return 1 + _downstream check || return 1 + python3 -B "$SRCDEST/downstream_test.py" || return 1 ( unset LD_PRELOAD FAKEROOTKEY FAKED_MODE ctest --test-dir "$srcdir/build" --output-on-failure --no-tests=error @@ -163,7 +190,7 @@ check() { package() { check || return 1 - _verify --build "$srcdir/build" --output "$srcdir/BUILD-PROVENANCE.json" || return 1 + _downstream build --build "$srcdir/build" --output "$srcdir/BUILD-PROVENANCE.json" || return 1 install -Dm755 "$srcdir/build/cua-hyprland-plugin.so" \ "$pkgdir/usr/lib/cua/hyprland/cua-hyprland-plugin.so" || return 1 install -Dm644 "$srcdir/$_stem/LICENSE.md" \ @@ -175,4 +202,7 @@ package() { for name in KIT-PROVENANCE.json PROFILE.json profile_verify.py; do install -Dm644 "$srcdir/cua-profile-kit/$name" "$pkgdir/usr/share/$pkgname/$name" || return 1 done + for name in DOWNSTREAM-PROVENANCE.json independent-keymaps.patch; do + install -Dm644 "$SRCDEST/$name" "$pkgdir/usr/share/$pkgname/$name" || return 1 + done } diff --git a/pkgbuilds/cua-hyprland-plugin/PROFILE.json b/pkgbuilds/cua-hyprland-plugin/PROFILE.json index ec78d11..c24dc2d 100644 --- a/pkgbuilds/cua-hyprland-plugin/PROFILE.json +++ b/pkgbuilds/cua-hyprland-plugin/PROFILE.json @@ -12,12 +12,12 @@ "sha256": "da8fcacf347bcbed83edc40108c6e2298da095e22246bd764e9bb382786cebb2" }, "kit_version": "1.1.0", - "package_release": 3, - "profile_id": "omarchy-hyprland-0562r3", + "package_release": 6, + "profile_id": "omarchy-hypr0562r3-aq0151-remaps", "runtime": { "basename": "libstdc++.so.6.0.36", "packages": { - "aquamarine": "0.15.0-2", + "aquamarine": "0.15.1-1", "glibc": "2.44+r24+g16be1518495f-1", "hyprcursor": "0.1.13-7", "hyprgraphics": "0.5.1-4", diff --git a/pkgbuilds/cua-hyprland-plugin/README.md b/pkgbuilds/cua-hyprland-plugin/README.md index 15820ef..c6c85fc 100644 --- a/pkgbuilds/cua-hyprland-plugin/README.md +++ b/pkgbuilds/cua-hyprland-plugin/README.md @@ -1,8 +1,8 @@ # Optional Cua Hyprland plugin -This package targets **Omarchy stable x86_64**, with Inkscape `1.4.4-6` and two independent background-input lanes. Cua's native qualification is recorded in [the kit's qualification record](https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/QUALIFICATION.md) and [Cua #3698](https://github.com/trycua/cua/pull/3698). Omabot replay and Omarchy's merge decision are recorded in [omarchy-pkgs #346](https://github.com/omacom/omarchy-pkgs/pull/346). Scheduling the recipe does not expand the qualified stable target. +This package targets **Omarchy x86_64**, with Inkscape `1.4.4-6` and two independent background-input lanes. Release `6` is an edge candidate for Aquamarine `0.15.1-1`; it retains the keyboard-remap patch introduced in release `5`, which includes the Omarchy patch for independent agent keymaps, operation-specific foreground checks, and compatible Num Lock state; the upstream native qualification below covers the unpatched source, not this change. Cua's native qualification is recorded in [the kit's qualification record](https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/QUALIFICATION.md) and [Cua #3698](https://github.com/trycua/cua/pull/3698). Omabot replay and Omarchy's merge decision are recorded in [omarchy-pkgs #346](https://github.com/omacom/omarchy-pkgs/pull/346). Scheduling the recipe does not expand the qualified stable target. -The plugin is optional. Cua Driver works independently, and installation does not load the plugin or enable input. The package follows the normal edge-to-RC-to-stable promotion path instead of the fast release ring. Its PKGBUILD limits builds to x86_64; only stable x86_64 is a qualified target. +The plugin is optional. Cua Driver works independently, and installation does not load the plugin or enable input. The package follows the normal edge-to-RC-to-stable promotion path instead of the fast release ring. Its PKGBUILD limits builds to x86_64. The upstream qualification covers the original stable profile; the updated Aquamarine profile needs its own Omabot validation before promotion. ## Source and build profile @@ -10,29 +10,25 @@ The package uses the [Driver 0.26.1 plugin source](https://github.com/trycua/cua including the [desktop-fault cleanup repair](https://github.com/trycua/cua/pull/3702). It is not a repackaging of the unmodified 0.24.0 plugin. -The qualified Driver pairing is `cua-driver-bin 0.27.0-1`, with input protocol v3. Driver 0.27.0 contains the bounded stale-geometry retry validated with this pinned module; its production plugin source is unchanged from the source used here. Discovery protocol v2 is separate. A newer Driver release is a changed pairing and requires affected replay before promotion. +The qualified upstream Driver pairing is `cua-driver-bin 0.27.0-1`, with input protocol v3. Driver 0.27.0 contains the bounded stale-geometry retry validated with the upstream module; its production plugin source is the base for the downstream patch used here. Discovery protocol v2 is separate. A newer Driver release is a changed pairing and requires affected replay before promotion. -Profile `omarchy-hyprland-0562r3`, kit tooling `1.1.0`, and package release `3` pin: +Profile `omarchy-hypr0562r3-aq0151-remaps`, kit tooling `1.1.0`, and package release `6` pin: - Hyprland `0.56.2-3`, headers `0.56.2`, and measured executable/header hashes. - GCC `16.2.1 20260810`, including compiler bytes and emitted ELF identity. -- Shared runtime `libstdc++.so.6.0.36`, its bytes, and exact ABI package versions. +- Shared runtime `libstdc++.so.6.0.36`, its bytes, and exact ABI package versions, including Aquamarine `0.15.1-1`. This profile derives from Cua's `omarchy-stable-20260910` profile. Arch's Hyprland `-3` package splits out `hyprpm` and changes package dependencies; its compositor executable and all 498 header/pkg-config files are byte-identical to `-2`. Both executables have SHA-256 `da8fcacf347bcbed83edc40108c6e2298da095e22246bd764e9bb382786cebb2`. -The checked-in `PROFILE.json` changes only the profile name, package release, -and exact Hyprland package version. Compiler, runtime, source, executable, -and header identities remain unchanged. The download wrapper verifies the +The checked-in `PROFILE.json` changes only the profile name, package release, and exact Hyprland and Aquamarine package versions. Compiler, libstdc++ runtime, upstream source, compositor executable, and header identities remain unchanged; the separately recorded patch changes the build source. The download wrapper verifies the original kit before deriving the updated profile, recipe, and provenance, then verifies every derived member against its recorded digest. The native qualification below was recorded with package release `2` and -Hyprland `-2`. This packaging update does not claim a new application or -Driver replay. The `-3` dependency must reach a destination channel before -this artifact can be installed there; publication still follows edge → RC → stable. +Hyprland `-2`. The downstream keymap change and Aquamarine update need their own application and Driver replay before promotion. Hyprland `0.56.2-3` and Aquamarine `0.15.1-1` must both reach a destination channel before this artifact can be installed there; publication still follows edge → RC → stable. The generated `PKGBUILD` identifies the immutable kit download, outer checksum, and member checksums. The kit records the full source and tooling revisions, @@ -47,7 +43,25 @@ and production flags remain mandatory. Packaging runs all bundled CTests even with `--nocheck` or `--repackage`; `--skipinteg` does not bypass recipe checks. Production input is built in; experimental signed input and tracing are off. -## What is qualified +The pristine upstream archive, manifest, and verifier remain unchanged. `independent-keymaps.patch` is applied to a separate source tree, and `DOWNSTREAM-PROVENANCE.json` pins the patch and every resulting source file. Build, check, and package revalidate both trees, including when makepkg integrity checks are skipped. `BUILD-PROVENANCE.json` records the upstream base under `source`, the applied change under `downstream`, and the final module digest; the downstream manifest and patch are installed beside it. This preserves the existing compiler, headers, runtime, and consumer checks without representing the modified module as an unmodified upstream build. + +## Aquamarine dependency refresh + +Release `5` required Aquamarine `0.15.0-2`. When the edge mirror moved to `0.15.1-1`, pacman could no longer resolve that dependency, even after a full database refresh. Release `6` derives a new profile from the same verified upstream kit and pins `0.15.1-1` in both the package dependencies and the installed compatibility verifier. Source, patch, compiler, compositor, headers, and libstdc++ hashes remain pinned; the original upstream qualification does not establish compatibility with the changed Aquamarine package. + +A package release bump alone cannot repair future dependency drift: the checked-in profile and derived kit checksums must agree with the new environment, and affected native checks must pass before publication. Do not remove exact dependencies or selectively downgrade a library to bypass a mismatch. + +## Keyboard behavior + +Each background lane owns a canonical US keymap and independent modifier state. The physical keyboard keeps its layout, Compose key, and remaps. No installation or activation step edits `input:kb_*`. Existing Driver keycodes are interpreted by the agent keyboard, so this does not add Unicode, IME, or new Driver text routes. + +Plain click, scroll, drag, and foreground activation do not require a canonical keyboard layout. Foreground keys still use the primary seat: the plugin checks the requested key and modifier sequence against its actual XKB map before activation or input. Unrelated remaps are accepted; a sequence whose symbols or modifier/lock transitions differ from the canonical meaning is refused with `unsupported_layout`. Arbitrary foreground layout translation remains outside protocol v3. + +Foreground typing preserves Num Lock and admits a requested key sequence only when its symbols and shortcut semantics still match the canonical meaning. Num Lock does not block unaffected letters, top-row digits, Enter, or compatible shortcuts; a keypad sequence whose meaning changes is refused. Caps Lock, other unsupported lock states, held or latched modifiers, and nonzero layout groups remain guarded. + +Both routes retain target/conflict checks and cancellation on desktop/keymap changes. `hyprctl -j cua:status` exposes `keyboard_layout_independent: true` and `foreground_numlock_compatible: true` for installers to distinguish this implementation from an older mapped module. The marker does not identify every future package revision; plugin updates still require a fresh desktop session. + +## Historical upstream qualification The initial app scope is native Wayland Inkscape `1.4.4-6` with the canonical US keymap. Two lanes require independent Driver processes and distinct native @@ -93,7 +107,7 @@ background refusal never authorizes a hidden foreground fallback or unlock. Build the unsigned candidate in edge: ```sh -bin/repo build --package cua-hyprland-plugin --arch x86_64 --mirror edge +./bin/build --package cua-hyprland-plugin --arch x86_64 --mirror edge ``` In a fresh worker matching the reviewed profile: @@ -131,7 +145,7 @@ kit-provenance digest: ```sh python3 /usr/share/cua-hyprland-plugin/profile_verify.py \ --kit /usr/share/cua-hyprland-plugin \ - --kit-sha256 1391084a903254fb16d251522dba41ae34a06cfa4b2f6b7ba91a3cceeb895921 \ + --kit-sha256 819779b93655d603d9ebb0d33ea052326c3374674a1d473886106af25e0fffdd \ --consumer /usr/lib/cua/hyprland/cua-hyprland-plugin.so ``` @@ -146,98 +160,28 @@ hyprctl plugin load /usr/lib/cua/hyprland/cua-hyprland-plugin.so hyprctl -j cua:status ``` -Loading alone does not enable input. Before opting in, save open work and save -the exact current personal input configuration. The backup command refuses a -symlinked input file and refuses to replace an earlier backup: +Loading alone does not enable input. Use Omarchy's explicit Cua Input toggle when available; it verifies the installed profile and loaded capability and removes the legacy copied toggle's keyboard override. If it reports an older mapped plugin, disable Cua Input and log out and back in before enabling it again. Never hot-unload and reload the module. -```sh -test -f "$HOME/.config/hypr/input.lua" && \ - test ! -L "$HOME/.config/hypr/input.lua" && \ - test ! -e "$HOME/.config/hypr/input.lua.cua-before" && \ - cp --archive -- "$HOME/.config/hypr/input.lua" \ - "$HOME/.config/hypr/input.lua.cua-before" -``` - -If `input.lua` is a symlink, stop here: back up and later restore its resolved -target explicitly instead of using the commands below. - -The background-input admission guard requires the exact XKB keymap -`rules=evdev`, `model=pc105`, `layout=us`, empty variant and options, and no -custom keymap file. Stock Omarchy 4.0.3 English (US) is not that literal -configuration: it leaves rules and model empty and sets -`compose:caps,shift:both_capslock_cancel`. Those options make Caps Lock the -Compose key and both Shift keys the Caps Lock/cancel chord. The required empty -options restore ordinary Caps Lock behavior and remove both stock shortcuts -while Cua input is enabled. Any other effective value is intentionally refused -as `unsupported_layout`; do not weaken or bypass that admission guard. - -Append this override to `~/.config/hypr/input.lua` so it follows any existing -input settings. It both selects the exact admitted keymap and enables the -trusted local transport: +For manual activation, add only this plugin setting to a sourced Hyprland Lua configuration file, preserving all existing input settings: ```lua hl.config({ - input = { - kb_rules = "evdev", - kb_model = "pc105", - kb_layout = "us", - kb_variant = "", - kb_options = "", - kb_file = "", - }, plugin = { cua = { enabled = true } }, }) ``` -Reload, then read back every keymap value rather than relying on the source -file alone: +Then reload and inspect status: ```sh hyprctl reload -for name in kb_rules kb_model kb_layout kb_variant kb_options kb_file; do - value=$(hyprctl -j getoption "input:$name" | jq -r '.str') - printf '%s=%s\n' "$name" "$value" -done -hyprctl -j cua:status -``` - -The keymap readback must be exactly: - -```text -kb_rules=evdev -kb_model=pc105 -kb_layout=us -kb_variant= -kb_options= -kb_file= -``` - -A runtime keyword or Lua evaluation without `hyprctl reload` does not reconcile -the input sockets. Continue only when status also reports input protocol v3, -input capability, socket paths, and the expected compositor identity. Do not -disable NumLock; that was required only by a strict qualification observer, not -by the demonstrated background-input contract. - -Start Driver with `CUA_DRIVER_RS_ENABLE_WAYLAND=1`. For the activation check, -use background input in a new disposable Inkscape document to create a text -object containing `CUA activation check`. Save it under a new temporary -filename, then verify the text in both a fresh Driver snapshot and the reopened -saved SVG. Never test against an existing document, and do not automatically -replay an action with a partial or unknown outcome. - -After the check, restore the exact saved configuration and reload it: - -```sh -command mv --force -- "$HOME/.config/hypr/input.lua.cua-before" \ - "$HOME/.config/hypr/input.lua" -hyprctl reload hyprctl -j cua:status ``` -Confirm that the prior keymap values are back and status reports input disabled. -Retained inert agent pointers can remain until the compositor exits; disabling -input does not unload the mapped module. If you intentionally keep activation, -retain the backup until you are ready to perform this exact restoration. +Continue only when status reports `keyboard_layout_independent: true`, `foreground_numlock_compatible: true`, input protocol v3, input capability, socket paths, and the expected compositor identity. Do not change `kb_layout`, `kb_options`, or NumLock for background input. If you previously followed the stock-US override instructions, remove only that Cua-specific override and reload to restore your underlying personal settings. + +Start Driver with `CUA_DRIVER_RS_ENABLE_WAYLAND=1`. In a new disposable Inkscape document, test an admitted background key operation and pointer operation, then verify the result in both a fresh snapshot and a saved/reopened SVG. Driver text-route restrictions still apply. Never test against an existing document or automatically replay an action with a partial or unknown outcome. + +To disable input, turn the Cua Input toggle off, or remove the manual `plugin.cua.enabled` setting and reload. Confirm that status reports input disabled. Retained inert agent pointers can remain until the compositor exits; disabling input does not unload the mapped module. Before an incompatible desktop update, remove operator-added plugin activation settings, save work, and exit the graphical session. From a text console, run diff --git a/pkgbuilds/cua-hyprland-plugin/downstream.py b/pkgbuilds/cua-hyprland-plugin/downstream.py new file mode 100644 index 0000000..429b7f0 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/downstream.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Verify the Omarchy patch separately from the unchanged upstream source kit.""" + +import argparse +import hashlib +import importlib.util +import json +from pathlib import Path, PurePosixPath +import shutil +import subprocess + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def inventory(root): + require(root.is_dir() and not root.is_symlink(), "source must be a real directory") + result = {} + for path in root.rglob("*"): + require(not path.is_symlink() and (path.is_dir() or path.is_file()), "nonregular source entry") + if path.is_file(): + result[path.relative_to(root).as_posix()] = digest(path) + return result + + +def verify_inputs(pristine, patch, manifest): + require(manifest["schema"] == 1, "unsupported downstream schema") + require(patch.is_file() and not patch.is_symlink() and digest(patch) == manifest["patch_sha256"], + "downstream patch checksum mismatch") + require(digest(pristine / "SOURCE-PROVENANCE.json") == manifest["upstream_manifest_sha256"], + "downstream base manifest mismatch") + require(manifest["files"], "empty downstream inventory") + for name in manifest["files"]: + path = PurePosixPath(name) + require(name and not path.is_absolute() and path.as_posix() == name and + ".." not in path.parts and "\\" not in name, "invalid downstream path") + + +def verify_tree(source, manifest): + require(inventory(source) == manifest["files"], "patched source inventory/checksum mismatch") + + +def prepare(pristine, source, patch, manifest): + require(not source.exists() and not source.is_symlink(), "patched source requires a fresh destination") + shutil.copytree(pristine, source) + subprocess.run(["patch", "--batch", "--fuzz=0", "-p1", "-i", str(patch.resolve())], cwd=source, check=True) + verify_tree(source, manifest) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("mode", choices=("prepare", "check", "build")) + parser.add_argument("--pristine", required=True, type=Path) + parser.add_argument("--source", required=True, type=Path) + parser.add_argument("--patch", required=True, type=Path) + parser.add_argument("--manifest", required=True, type=Path) + parser.add_argument("--kit", required=True, type=Path) + parser.add_argument("--kit-sha256", required=True) + parser.add_argument("--archive", required=True, type=Path) + parser.add_argument("--cxx", required=True, type=Path) + parser.add_argument("--build", type=Path) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + try: + # PKGBUILD authenticates the verifier and this helper before execution. + spec = importlib.util.spec_from_file_location("upstream_profile", args.kit / "profile_verify.py") + upstream = importlib.util.module_from_spec(spec) + spec.loader.exec_module(upstream) + profile, kit = upstream.verify_kit(args.kit, args.kit_sha256) + base = upstream.verify_archive(args.archive, profile) + require(upstream.verify_source(args.pristine, profile) == base, "upstream source identity mismatch") + manifest = upstream.read_json(args.manifest.read_bytes()) + verify_inputs(args.pristine, args.patch, manifest) + if args.mode == "prepare": + prepare(args.pristine, args.source, args.patch, manifest) + else: + verify_tree(args.source, manifest) + if args.mode == "build": + require(args.build is not None and args.output is not None, "build evidence requires output") + native = upstream.verify_native(args.cxx, profile) + native["module_sha256"] = upstream.verify_build(args.build, args.source, args.cxx, profile) + native["module_runtime_sha256"] = profile["runtime"]["sha256"] + args.output.write_bytes(upstream.json_bytes(dict(native, source=base, profile=profile, + kit=kit, downstream=manifest))) + except (ValueError, KeyError, TypeError, OSError, subprocess.CalledProcessError) as error: + parser.exit(1, f"error: {error}\n") + + +if __name__ == "__main__": + main() diff --git a/pkgbuilds/cua-hyprland-plugin/downstream_test.py b/pkgbuilds/cua-hyprland-plugin/downstream_test.py new file mode 100644 index 0000000..60586d2 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/downstream_test.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Exercise downstream integrity with real patch application and tampering.""" + +import hashlib +from pathlib import Path +import tempfile +import unittest + +import downstream + + +class DownstreamTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.pristine = self.root / "pristine" + self.pristine.mkdir() + (self.pristine / "SOURCE-PROVENANCE.json").write_text("upstream\n") + (self.pristine / "input.cpp").write_text("old\n") + self.patch = self.root / "change.patch" + self.patch.write_text("--- a/input.cpp\n+++ b/input.cpp\n@@ -1 +1 @@\n-old\n+new\n") + self.source = self.root / "patched" + self.manifest = { + "schema": 1, + "patch_sha256": downstream.digest(self.patch), + "upstream_manifest_sha256": downstream.digest(self.pristine / "SOURCE-PROVENANCE.json"), + "files": {"SOURCE-PROVENANCE.json": downstream.digest(self.pristine / "SOURCE-PROVENANCE.json"), + "input.cpp": hashlib.sha256(b"new\n").hexdigest()}, + } + + def test_applies_patch_without_changing_upstream(self): + downstream.verify_inputs(self.pristine, self.patch, self.manifest) + downstream.prepare(self.pristine, self.source, self.patch, self.manifest) + self.assertEqual((self.pristine / "input.cpp").read_text(), "old\n") + self.assertEqual((self.source / "input.cpp").read_text(), "new\n") + + def test_changed_patch_refuses(self): + self.patch.write_text(self.patch.read_text().replace("+new", "+bad")) + with self.assertRaisesRegex(ValueError, "patch checksum"): + downstream.verify_inputs(self.pristine, self.patch, self.manifest) + + def test_changed_base_manifest_refuses(self): + (self.pristine / "SOURCE-PROVENANCE.json").write_text("different\n") + with self.assertRaisesRegex(ValueError, "base manifest"): + downstream.verify_inputs(self.pristine, self.patch, self.manifest) + + def test_tampered_missing_and_extra_files_refuse(self): + downstream.prepare(self.pristine, self.source, self.patch, self.manifest) + file = self.source / "input.cpp" + for content in ("tampered\n", None): + if content is None: + file.unlink() + else: + file.write_text(content) + with self.assertRaisesRegex(ValueError, "inventory/checksum"): + downstream.verify_tree(self.source, self.manifest) + file.write_text("new\n") + (self.source / "unexpected.cpp").write_text("extra\n") + with self.assertRaisesRegex(ValueError, "inventory/checksum"): + downstream.verify_tree(self.source, self.manifest) + + def test_symlink_and_reused_destination_refuse(self): + downstream.prepare(self.pristine, self.source, self.patch, self.manifest) + with self.assertRaisesRegex(ValueError, "fresh destination"): + downstream.prepare(self.pristine, self.source, self.patch, self.manifest) + file = self.source / "input.cpp" + file.unlink() + file.symlink_to(self.pristine / "input.cpp") + with self.assertRaisesRegex(ValueError, "nonregular"): + downstream.verify_tree(self.source, self.manifest) + + +if __name__ == "__main__": + unittest.main() diff --git a/pkgbuilds/cua-hyprland-plugin/independent-keymaps.patch b/pkgbuilds/cua-hyprland-plugin/independent-keymaps.patch new file mode 100644 index 0000000..245ba60 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/independent-keymaps.patch @@ -0,0 +1,1127 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 8a80de2..2d48237 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -39,8 +39,28 @@ target_compile_options(cua_hyprland_protocol PRIVATE -Wall -Wextra -Wpedantic -W + cua_hyprland_harden(cua_hyprland_protocol) + set_target_properties(cua_hyprland_protocol PROPERTIES POSITION_INDEPENDENT_CODE ON) + ++if(BUILD_TESTING OR CUA_HYPRLAND_INPUT OR CUA_HYPRLAND_TEST_INPUT) ++ find_package(PkgConfig REQUIRED) ++ pkg_check_modules(XKBCOMMON REQUIRED IMPORTED_TARGET xkbcommon) ++endif() ++ + if(BUILD_TESTING) ++ add_executable(cua_hyprland_keyboard_layout_test tests/keyboard_layout_test.cpp) ++ target_compile_features(cua_hyprland_keyboard_layout_test PRIVATE cxx_std_26) ++ target_include_directories(cua_hyprland_keyboard_layout_test PRIVATE src) ++ target_link_libraries(cua_hyprland_keyboard_layout_test PRIVATE PkgConfig::XKBCOMMON) ++ target_compile_options(cua_hyprland_keyboard_layout_test PRIVATE -Wall -Wextra -Wpedantic -Werror) ++ add_test(NAME cua_hyprland_keyboard_layout_test COMMAND cua_hyprland_keyboard_layout_test) ++ + find_package(Python3 REQUIRED COMPONENTS Interpreter) ++ add_test(NAME cua_hyprland_agent_keymap_test ++ COMMAND ${Python3_EXECUTABLE} -B ${CMAKE_CURRENT_SOURCE_DIR}/tests/agent_keymap_test.py) ++ set_tests_properties(cua_hyprland_agent_keymap_test PROPERTIES ++ ENVIRONMENT "CXX=${CMAKE_CXX_COMPILER}") ++ add_test(NAME cua_hyprland_foreground_modifiers_test ++ COMMAND ${Python3_EXECUTABLE} -B ${CMAKE_CURRENT_SOURCE_DIR}/tests/foreground_modifiers_test.py) ++ set_tests_properties(cua_hyprland_foreground_modifiers_test PROPERTIES ++ ENVIRONMENT "CXX=${CMAKE_CXX_COMPILER}") + add_test(NAME cua_hyprland_desktop_fault_policy_test + COMMAND ${Python3_EXECUTABLE} -B + ${CMAKE_CURRENT_SOURCE_DIR}/tests/desktop_fault_policy_test.py) +@@ -218,6 +238,7 @@ if(CUA_HYPRLAND_BUILD_PLUGIN) + message(FATAL_ERROR "Input is pinned to Hyprland 0.56.2") + endif() + target_sources(cua_hyprland_plugin PRIVATE src/input_experiment.cpp) ++ target_link_libraries(cua_hyprland_plugin PRIVATE PkgConfig::XKBCOMMON) + endif() + if(CUA_HYPRLAND_INPUT) + target_compile_definitions(cua_hyprland_plugin PRIVATE CUA_HYPRLAND_INPUT=1) +diff --git a/src/foreground_route.hpp b/src/foreground_route.hpp +index 0b675d3..c3af461 100644 +--- a/src/foreground_route.hpp ++++ b/src/foreground_route.hpp +@@ -68,11 +68,13 @@ struct ForegroundSeatBindings { + bool unique() const { return primary_candidates == 1; } + }; + +-inline ForegroundFailureReason foreground_key_modifier_failure(const std::array& modifiers) { +- // The KEY mapping assumes a neutral US state, including layout group zero. ++inline ForegroundFailureReason foreground_key_modifier_failure(const std::array& modifiers, ++ std::uint32_t allowed_locked = 0) { ++ // The caller may admit a keymap-resolved ambient Num Lock mask. All other ++ // human modifiers and nonzero layout groups remain unsupported. + if (modifiers[0]) return ForegroundFailureReason::keyboard_depressed; + if (modifiers[1]) return ForegroundFailureReason::keyboard_latched; +- if (modifiers[2]) return ForegroundFailureReason::keyboard_locked; ++ if (modifiers[2] & ~allowed_locked) return ForegroundFailureReason::keyboard_locked; + if (modifiers[3]) return ForegroundFailureReason::keyboard_group; + return ForegroundFailureReason::none; + } +diff --git a/src/input_experiment.cpp b/src/input_experiment.cpp +index fc7e740..5d80d3e 100644 +--- a/src/input_experiment.cpp ++++ b/src/input_experiment.cpp +@@ -9,6 +9,8 @@ + #include "seat_lifetime.hpp" + #include "owned_socket_path.hpp" + #include "foreground_route.hpp" ++#include "keyboard_layout.hpp" ++#include + + #include + #include +@@ -214,7 +216,11 @@ struct InputExperiment::Impl { + xkb_keymap* keymap = nullptr; + xkb_state* keyboard_state = nullptr; + int keymap_fd = -1; +- bool retired = false, suspended = true, us_keymap = false, physical_keymap_present = false; ++ // Foreground state uses the actual primary map; never share it with agent seats. ++ xkb_keymap* physical_keymap = nullptr; ++ xkb_state* physical_state = nullptr; ++ std::string physical_keymap_text; ++ bool retired = false, suspended = true, physical_keymap_present = false; + WP physical_keyboard; + CHyprSignalListener keymap_listener; + unsigned lane; +@@ -243,23 +249,37 @@ struct InputExperiment::Impl { + // No private key or input-enabled default exists in this component. + } + +- static bool canonical_us_keymap(xkb_context* context, xkb_keymap* map) { +- // Compare canonical compiled content, not a layout display name. This +- // deliberately excludes variants, options, remaps, and multiple groups. +- const xkb_rule_names names{"evdev", "pc105", "us", "", ""}; +- auto* reference = xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); +- if (!reference) return false; +- char* actual = xkb_keymap_get_as_string(map, XKB_KEYMAP_FORMAT_TEXT_V1); +- char* expected = xkb_keymap_get_as_string(reference, XKB_KEYMAP_FORMAT_TEXT_V1); +- const bool matches = actual && expected && std::strcmp(actual, expected) == 0; +- std::free(actual); std::free(expected); xkb_keymap_unref(reference); +- return matches; +- } +- bool layout_qualified() const { +- if (!kProduction) return true; ++ bool physical_layout_ready() const { + const auto keyboard = g_pSeatManager->m_keyboard.lock(); +- return physical_keymap_present && us_keymap && keyboard_state && keyboard && +- keyboard->m_xkbKeymapV1FD.get() >= 0 && keyboard->m_xkbKeymapV1String == keymap_text; ++ return physical_keymap_present && physical_state && keyboard && ++ keyboard->m_xkbKeymapV1FD.get() >= 0 && keyboard->m_xkbKeymapV1String == physical_keymap_text; ++ } ++ bool layout_qualified(InputRoute route, std::uint64_t capability) const { ++ return keyboard_layout_ready(route, capability, keyboard_state && keymap_fd >= 0, physical_layout_ready()); ++ } ++ void initialize_agent_keymap() { ++ if (keymap) return; ++ xkb_context_ = xkb_context_new(XKB_CONTEXT_NO_FLAGS); ++ keymap = xkb_context_ ? agent_keymap(xkb_context_) : nullptr; ++ keyboard_state = keymap ? xkb_state_new(keymap) : nullptr; ++ char* text = keymap ? xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1) : nullptr; ++ if (!keyboard_state || !text) { ++ std::free(text); ++ throw std::runtime_error("agent XKB keymap unavailable"); ++ } ++ keymap_text = text; ++ std::free(text); ++ keymap_fd = memfd_create("cua-agent-keymap", MFD_CLOEXEC | MFD_ALLOW_SEALING); ++ if (keymap_fd < 0) throw std::runtime_error("agent keymap fd unavailable"); ++ std::size_t offset = 0; ++ while (offset < keymap_text.size() + 1) { ++ const auto count = write(keymap_fd, keymap_text.c_str() + offset, keymap_text.size() + 1 - offset); ++ if (count < 0 && errno == EINTR) continue; ++ if (count <= 0) throw std::runtime_error("agent keymap write failed"); ++ offset += static_cast(count); ++ } ++ if (fcntl(keymap_fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) < 0) ++ throw std::runtime_error("agent keymap sealing failed"); + } + void sync_keymap() { + const auto keyboard = g_pSeatManager->m_keyboard.lock(); +@@ -278,38 +298,20 @@ struct InputExperiment::Impl { + return; + } + physical_keymap_present = true; +- if (keyboard_state && keymap_text == keyboard->m_xkbKeymapV1String) return; +- // Prepare a complete replacement before retiring the old independent +- // state. Keep our own fd: primary keyboard replacement must not leave +- // later seat bindings referring to a closed compositor fd. +- auto* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); +- auto* map = context ? xkb_keymap_new_from_string(context, keyboard->m_xkbKeymapV1String.c_str(), +- XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS) : nullptr; +- auto* state = map ? xkb_state_new(map) : nullptr; +- const auto fd = fcntl(keyboard->m_xkbKeymapV1FD.get(), F_DUPFD_CLOEXEC, 0); +- if (!state || fd < 0) { +- if (fd >= 0) close(fd); +- if (state) xkb_state_unref(state); +- if (map) xkb_keymap_unref(map); +- if (context) xkb_context_unref(context); +- throw std::runtime_error("independent XKB state unavailable"); +- } ++ if (physical_keymap_text == keyboard->m_xkbKeymapV1String) return; ++ // Physical changes still revoke authority, but never replace the map ++ // advertised by either background keyboard. + desktop_transition(); +- if (keyboard_state) xkb_state_unref(keyboard_state); +- if (keymap) xkb_keymap_unref(keymap); +- if (xkb_context_) xkb_context_unref(xkb_context_); +- if (keymap_fd >= 0) close(keymap_fd); +- keyboard_state = state; keymap = map; xkb_context_ = context; keymap_fd = fd; +- us_keymap = !kProduction || canonical_us_keymap(context, map); +- keymap_text = keyboard->m_xkbKeymapV1String; +- for (auto& k : keyboards) +- if (!k->dead && k->wl->resource()) +- k->wl->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_text.size() + 1); +- for (auto& seat : seats) +- if (!seat->dead && seat->wl->resource()) +- seat->wl->sendCapabilities(static_cast(WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD)); ++ auto* map = xkb_keymap_new_from_string(xkb_context_, keyboard->m_xkbKeymapV1String.c_str(), ++ XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); ++ auto* state = map ? xkb_state_new(map) : nullptr; ++ if (physical_state) xkb_state_unref(physical_state); ++ if (physical_keymap) xkb_keymap_unref(physical_keymap); ++ physical_state = state; physical_keymap = map; ++ physical_keymap_text = keyboard->m_xkbKeymapV1String; + } + void start() { ++ initialize_agent_keymap(); + sync_keymap(); + timer = wl_event_loop_add_timer(g_pCompositor->m_wlEventLoop, tick, this); + if (!timer) throw std::runtime_error("input timer registration failed"); +@@ -389,6 +391,8 @@ struct InputExperiment::Impl { + cleanup_socket(); + if (keyboard_state) xkb_state_unref(keyboard_state); + if (keymap) xkb_keymap_unref(keymap); ++ if (physical_state) xkb_state_unref(physical_state); ++ if (physical_keymap) xkb_keymap_unref(physical_keymap); + if (xkb_context_) xkb_context_unref(xkb_context_); + if (keymap_fd >= 0) close(keymap_fd); + } +@@ -782,12 +786,40 @@ struct InputExperiment::Impl { + .exact_pointer_focus = root && g_pSeatManager->m_state.pointerFocus == root, + }; + } ++ std::array capture_foreground_modifiers(bool needs_keyboard) const { ++ const auto physical = g_pSeatManager->m_keyboard.lock(); ++ if (!physical) throw ForegroundFailure{ForegroundFailureReason::physical_keyboard}; ++ std::array result{}; ++ const auto observe = [&](const auto& kb, bool primary) { ++ const std::array state{kb->m_modifiersState.depressed, kb->m_modifiersState.latched, ++ kb->m_modifiersState.locked, kb->m_modifiersState.group}; ++ if (needs_keyboard) { ++ const auto failure = foreground_key_modifier_failure(state, foreground_numlock_mask(kb->m_xkbKeymap)); ++ if (failure != ForegroundFailureReason::none) throw ForegroundFailure{failure}; ++ // Hyprland combines shared raw masks. A lock from a different ++ // encoding must not be reinterpreted as the primary Num Lock. ++ if (state[2] && state[2] != foreground_numlock_mask(physical_keymap)) ++ throw ForegroundFailure{ForegroundFailureReason::keyboard_locked}; ++ } ++ for (unsigned i = 0; i < 3; ++i) result[i] |= state[i]; ++ if (primary) result[3] = state[3]; ++ }; ++ observe(physical, true); ++ for (const auto& kb : g_pInputManager->m_keyboards) { ++ if (kb == physical || !kb->m_enabled || !kb->shareStates() || ++ (kb->isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(kb))) continue; ++ observe(kb, false); ++ } ++ return result; ++ } + void require_foreground(Client& c) { + if (lease != &c) throw ForegroundFailure{ForegroundFailureReason::lease}; + if (c.dead) throw ForegroundFailure{ForegroundFailureReason::client_dead}; + if (!available()) throw ForegroundFailure{ForegroundFailureReason::session_unavailable}; +- if (!layout_qualified()) throw ForegroundFailure{ForegroundFailureReason::unsupported_layout}; ++ if (foreground_keyboard_used && !physical_layout_ready()) throw ForegroundFailure{ForegroundFailureReason::unsupported_layout}; + if (Clock::now() >= expires) throw ForegroundFailure{ForegroundFailureReason::lease_expired}; ++ if (foreground_keyboard_used && capture_foreground_modifiers(true) != foreground_modifiers) ++ throw ForegroundFailure{ForegroundFailureReason::keyboard_state}; + const auto failure = foreground_guard(c).dispatch_failure(foreground_needs_pointer); + if (failure != ForegroundFailureReason::none) throw ForegroundFailure{failure}; + } +@@ -803,24 +835,31 @@ struct InputExperiment::Impl { + p->sendButton(event_ms(), held_button, WL_POINTER_BUTTON_STATE_RELEASED); + p->sendFrame(); + } ++ // Synthetic events only change our private state. If human input ++ // cancelled the action, restore the current real state, not stale locks. ++ const auto restore_modifiers = g_pSeatManager->m_keyboard.lock() ? ++ capture_foreground_modifiers(false) : foreground_modifiers; + if (foreground_keyboard_used && root && root->good() && g_pSeatManager->m_state.keyboardFocus == root) + for (const auto& weak : foreground_keyboards) + if (const auto k = weak.lock(); k && k->good()) { + for (auto code : held_keys) k->sendKey(event_ms(), code, WL_KEYBOARD_KEY_STATE_RELEASED); +- k->sendMods(foreground_modifiers[0], foreground_modifiers[1], foreground_modifiers[2], foreground_modifiers[3]); ++ k->sendMods(restore_modifiers[0], restore_modifiers[1], restore_modifiers[2], restore_modifiers[3]); + } + held_button = 0; held_keys.clear(); + foreground_pointers.clear(); foreground_keyboards.clear(); foreground_surface.reset(); foreground_seat.reset(); + foreground_started = false; + foreground_keyboard_used = false; ++ if (physical_state) xkb_state_unref(physical_state); ++ physical_state = physical_keymap ? xkb_state_new(physical_keymap) : nullptr; + } +- void start_foreground(Client& c, double x, double y, bool needs_pointer, bool needs_keyboard) { ++ void start_foreground(Client& c, double x, double y, bool needs_pointer, bool needs_keyboard, ++ const std::array& modifiers) { + const auto root = c.surface.lock(); + const auto physical = g_pSeatManager->m_keyboard.lock(); + const auto failure = foreground_guard(c).activation_failure(); + if (failure != ForegroundFailureReason::none) throw ForegroundFailure{failure}; + if (!physical) throw ForegroundFailure{ForegroundFailureReason::physical_keyboard}; +- if (!keyboard_state) throw ForegroundFailure{ForegroundFailureReason::keyboard_state}; ++ if (needs_keyboard && !physical_layout_ready()) throw ForegroundFailure{ForegroundFailureReason::keyboard_state}; + if (needs_pointer && !g_pSeatManager->m_mouse) throw ForegroundFailure{ForegroundFailureReason::physical_pointer}; + const Vector2D local{x + c.geometry[0] - c.geometry[4], y + c.geometry[1] - c.geometry[5]}; + if (needs_pointer && (!point(c, x, y) || root->at(local, true).first != root)) throw ForegroundFailure{ForegroundFailureReason::pointer_target}; +@@ -831,24 +870,16 @@ struct InputExperiment::Impl { + for (const auto& k : seat->m_keyboards) if (k && k->good()) foreground_keyboards.push_back(k); + if (needs_pointer && foreground_pointers.empty()) throw ForegroundFailure{ForegroundFailureReason::pointer_resources}; + if (foreground_keyboards.empty()) throw ForegroundFailure{ForegroundFailureReason::keyboard_resources}; +- foreground_modifiers = {physical->m_modifiersState.depressed, physical->m_modifiersState.latched, +- physical->m_modifiersState.locked, physical->m_modifiersState.group}; +- for (const auto& kb : g_pInputManager->m_keyboards) { +- if (!kb->m_enabled || !kb->shareStates() || (kb->isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(kb))) continue; +- foreground_modifiers[0] |= kb->m_modifiersState.depressed; +- foreground_modifiers[1] |= kb->m_modifiersState.latched; +- foreground_modifiers[2] |= kb->m_modifiersState.locked; +- } +- if (needs_keyboard) { +- const auto modifier_failure = foreground_key_modifier_failure(foreground_modifiers); +- if (modifier_failure != ForegroundFailureReason::none) throw ForegroundFailure{modifier_failure}; +- } +- xkb_state_update_mask(keyboard_state, foreground_modifiers[0], foreground_modifiers[1], foreground_modifiers[2], 0, 0, foreground_modifiers[3]); ++ foreground_modifiers = modifiers; ++ if (needs_keyboard && capture_foreground_modifiers(true) != foreground_modifiers) ++ throw ForegroundFailure{ForegroundFailureReason::keyboard_state}; ++ if (needs_keyboard) xkb_state_update_mask(physical_state, foreground_modifiers[0], foreground_modifiers[1], foreground_modifiers[2], 0, 0, foreground_modifiers[3]); + foreground_surface = root; + foreground_seat = seat; + foreground_needs_pointer = needs_pointer; + c.foreground_attempted = true; + foreground_started = true; ++ foreground_keyboard_used = needs_keyboard; + foreground_activating = true; + // Activation intentionally persists. Never save, borrow, or restore focus. + if (g_pSeatManager->m_state.keyboardFocus != root || Desktop::focusState()->window() != c.window.lock() || +@@ -858,7 +889,16 @@ struct InputExperiment::Impl { + if (lease != &c) throw ForegroundFailure{ForegroundFailureReason::lease}; + const auto focus_failure = foreground_guard(c).dispatch_failure(false); + if (focus_failure != ForegroundFailureReason::none) throw ForegroundFailure{focus_failure}; +- if (!needs_pointer) { require_foreground(c); return; } ++ if (!needs_pointer) { ++ require_foreground(c); ++ if (needs_keyboard) ++ for (const auto& weak : foreground_keyboards) { ++ const auto k = weak.lock(); ++ if (!k || !k->good()) throw ForegroundFailure{ForegroundFailureReason::keyboard_resources}; ++ k->sendMods(foreground_modifiers[0], foreground_modifiers[1], foreground_modifiers[2], foreground_modifiers[3]); ++ } ++ return; ++ } + foreground_activating = true; + ::Pointer::mgr()->warpTo({x + c.geometry[0], y + c.geometry[1]}); + if (lease != &c) throw ForegroundFailure{ForegroundFailureReason::lease}; +@@ -893,16 +933,17 @@ struct InputExperiment::Impl { + } + void foreground_key(Client& c, std::uint32_t code, bool pressed) { + require_foreground(c); ++ if (!physical_layout_ready()) throw ForegroundFailure{ForegroundFailureReason::unsupported_layout}; + foreground_keyboard_used = true; +- xkb_state_update_key(keyboard_state, code + 8, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); ++ xkb_state_update_key(physical_state, code + 8, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); + if (pressed) held_keys.push_back(code); else std::erase(held_keys, code); + for (const auto& weak : foreground_keyboards) { + const auto k = weak.lock(); if (!k || !k->good()) throw ForegroundFailure{ForegroundFailureReason::keyboard_resources}; + k->sendKey(event_ms(), code, pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED); +- k->sendMods(xkb_state_serialize_mods(keyboard_state, XKB_STATE_MODS_DEPRESSED), +- xkb_state_serialize_mods(keyboard_state, XKB_STATE_MODS_LATCHED), +- xkb_state_serialize_mods(keyboard_state, XKB_STATE_MODS_LOCKED), +- xkb_state_serialize_layout(keyboard_state, XKB_STATE_LAYOUT_EFFECTIVE)); ++ k->sendMods(xkb_state_serialize_mods(physical_state, XKB_STATE_MODS_DEPRESSED), ++ xkb_state_serialize_mods(physical_state, XKB_STATE_MODS_LATCHED), ++ xkb_state_serialize_mods(physical_state, XKB_STATE_MODS_LOCKED), ++ xkb_state_serialize_layout(physical_state, XKB_STATE_LAYOUT_EFFECTIVE)); + } + } + bool pointer_enter(Client& c, double x, double y) { +@@ -942,8 +983,8 @@ struct InputExperiment::Impl { + held_button = pressed ? value : 0; + } + bool keyboard_enter(Client& c) { +- const auto root = c.surface.lock(); const auto physical = g_pSeatManager->m_keyboard.lock(); +- if (!root || !physical || physical->m_xkbKeymapV1String != keymap_text || !keyboard_state) return false; ++ const auto root = c.surface.lock(); ++ if (!root || !keyboard_state || keymap_fd < 0) return false; + unsigned count = 0; + for (auto& k : keyboards) { + if (k->dead || !k->wl->resource() || k->wl->client() != root->client()) continue; +@@ -1030,7 +1071,7 @@ struct InputExperiment::Impl { + if (kProduction && (!InputGrant::single_operation(requested_cap) || + (requested_cap == 16 && route != InputRoute::primary_foreground))) { invalidate(c); send(c, refusal("unsupported")); return; } + if (kProduction && !available()) { invalidate(c, false); send(c, refusal("session_unavailable")); return; } +- if (!layout_qualified()) { invalidate(c, false); send(c, refusal("unsupported_layout")); return; } ++ if (!layout_qualified(route, requested_cap)) { invalidate(c, false); send(c, refusal("unsupported_layout")); return; } + const auto pid = number(f[1]); const auto address = number(f[2], 16); + PHLWINDOW window; + for (const auto& w : Desktop::windowState()->windows()) +@@ -1090,7 +1131,7 @@ struct InputExperiment::Impl { + if (c.token.empty() || f[2] != c.token || !refresh(c)) { send(c, refusal("stale_target")); return; } + if (number(f[3]) != c.revision) { if (kProduction) revoke("stale_geometry"); send(c, refusal("stale_geometry")); return; } + if (!available()) { revoke("session_unavailable", true); send(c, refusal("session_unavailable")); return; } +- if (!layout_qualified()) { revoke("unsupported_layout", true); send(c, refusal("unsupported_layout")); return; } ++ if (!layout_qualified(c.route, cap)) { revoke("unsupported_layout", true); send(c, refusal("unsupported_layout")); return; } + if (lease && Clock::now() >= expires) revoke("lease_expired"); + if (drag) { send(c, refusal("lease_busy")); return; } + if (lease != &c || !(capabilities & cap) || (kProduction && !grant.permits(cap, Clock::now()))) { +@@ -1184,8 +1225,13 @@ struct InputExperiment::Impl { + if (Clock::now() + std::chrono::milliseconds(duration + 50) >= expires) { send(c, refusal("lease_expired")); return; } + } + } ++ const auto modifiers = capture_foreground_modifiers(command == "KEY"); ++ if (command == "KEY" && !foreground_chord_compatible(physical_keymap, keymap, code, mods, modifiers)) { ++ revoke("unsupported_layout", true); ++ send(c, refusal("unsupported_layout")); return; ++ } + if (!consume_grant(c, cap)) return; +- start_foreground(c, x, y, command != "KEY" && command != "ACTIVATE", command == "KEY"); ++ start_foreground(c, x, y, command != "KEY" && command != "ACTIVATE", command == "KEY", modifiers); + if (command == "KEY") { + const std::array keys{42, 29, 56, 125}; + for (unsigned i = 0; i < 4; ++i) if ((mods & (1u << i)) && keys[i] != code) foreground_key(c, keys[i], true); +@@ -1227,7 +1273,7 @@ struct InputExperiment::Impl { + if (lease) { + if (lease->dead) revoke("disconnected", true); + else if (Clock::now() >= expires) revoke("lease_expired"); +- else if (!available() || !layout_qualified()) revoke("cancelled", true); ++ else if (!available() || !layout_qualified(lease->route, capabilities)) revoke("cancelled", true); + else if (!refresh(*lease) || primary_conflict(*lease) || agent_conflict(*lease) || + (drag && !drag->geometry.matches(lease->revision))) revoke("cancelled"); + } +diff --git a/src/keyboard_layout.hpp b/src/keyboard_layout.hpp +new file mode 100644 +index 0000000..f5f51c8 +--- /dev/null ++++ b/src/keyboard_layout.hpp +@@ -0,0 +1,127 @@ ++#pragma once ++ ++#include "foreground_route.hpp" ++#include ++#include ++#include ++#include ++#include ++ ++namespace cua::hyprland { ++// Wire v3 carries evdev key positions plus fixed Shift/Ctrl/Alt/Super bits. ++// Background seats advertise this map, independently of the human keyboard. ++inline xkb_keymap* agent_keymap(xkb_context* context) { ++ const xkb_rule_names names{"evdev", "pc105", "us", "", ""}; ++ return xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); ++} ++ ++inline bool keyboard_layout_ready(InputRoute route, std::uint64_t capability, ++ bool agent_ready, bool physical_ready) { ++ if (!(capability & 2)) return true; // Pointer/activation needs no key mapping. ++ return route == InputRoute::primary_foreground ? physical_ready : agent_ready; ++} ++ ++inline bool same_key_symbols(xkb_state* actual, xkb_state* expected, xkb_keycode_t key) { ++ const xkb_keysym_t *a = nullptr, *b = nullptr; ++ const int na = xkb_state_key_get_syms(actual, key, &a); ++ const int nb = xkb_state_key_get_syms(expected, key, &b); ++ if (na != nb) return false; ++ for (int i = 0; i < na; ++i) if (a[i] != b[i]) return false; ++ return true; ++} ++ ++inline bool same_consumed_modifiers(xkb_state* actual, xkb_state* expected, xkb_keycode_t key) { ++ // Toolkits subtract consumed modifiers when matching shortcuts. Matching ++ // symbols and effective state alone does not preserve shortcut meaning. ++ for (auto* state : {actual, expected}) { ++ auto* map = xkb_state_get_keymap(state); ++ for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(map); ++i) { ++ const char* name = xkb_keymap_mod_get_name(map, i); ++ if (xkb_state_mod_name_is_active(state, name, XKB_STATE_MODS_EFFECTIVE) <= 0) continue; ++ const auto ai = xkb_keymap_mod_get_index(xkb_state_get_keymap(actual), name); ++ const auto bi = xkb_keymap_mod_get_index(xkb_state_get_keymap(expected), name); ++ for (auto mode : {XKB_CONSUMED_MODE_XKB, XKB_CONSUMED_MODE_GTK}) ++ if ((xkb_state_mod_index_is_consumed2(actual, key, ai, mode) > 0) != ++ (xkb_state_mod_index_is_consumed2(expected, key, bi, mode) > 0)) return false; ++ } ++ } ++ return true; ++} ++ ++inline bool same_modifier_state(xkb_state* actual, xkb_state* expected) { ++ // Modifier indices can differ between maps. Compare names, in both directions, ++ // including nonstandard modifiers and lock/latch changes on key release. ++ for (auto* state : {actual, expected}) { ++ auto* map = xkb_state_get_keymap(state); ++ for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(map); ++i) { ++ const char* name = xkb_keymap_mod_get_name(map, i); ++ for (auto component : {XKB_STATE_MODS_DEPRESSED, XKB_STATE_MODS_LATCHED, ++ XKB_STATE_MODS_LOCKED, XKB_STATE_MODS_EFFECTIVE}) { ++ const bool a = xkb_state_mod_name_is_active(actual, name, component) > 0; ++ const bool b = xkb_state_mod_name_is_active(expected, name, component) > 0; ++ if (a != b) return false; ++ } ++ } ++ } ++ for (auto component : {XKB_STATE_LAYOUT_DEPRESSED, XKB_STATE_LAYOUT_LATCHED, ++ XKB_STATE_LAYOUT_LOCKED, XKB_STATE_LAYOUT_EFFECTIVE}) ++ if (xkb_state_serialize_layout(actual, component) != xkb_state_serialize_layout(expected, component)) ++ return false; ++ return true; ++} ++ ++// Resolve the virtual modifier through this map; Num Lock is not necessarily ++// encoded as Mod2. Ambiguous encodings are deliberately not admitted. ++inline xkb_mod_mask_t foreground_numlock_mask(xkb_keymap* map) { ++ const auto mask = map ? xkb_keymap_mod_get_mask(map, XKB_VMOD_NAME_NUM) : 0; ++ return mask && !(mask & (mask - 1)) && ++ !(mask & xkb_keymap_mod_get_mask(map, XKB_MOD_NAME_CAPS)) ? mask : 0; ++} ++ ++// Simulate the complete chord before delivering any events or changing focus. ++// This is compatibility checking, not layout translation: physical key positions ++// remain unchanged. Unrelated remaps are harmless, requested remaps fail closed. ++inline bool foreground_chord_compatible(xkb_keymap* physical, xkb_keymap* canonical, ++ std::uint32_t code, std::uint32_t mods, ++ const std::array& modifiers) { ++ if (!physical || !canonical) return false; ++ if (foreground_key_modifier_failure(modifiers, foreground_numlock_mask(physical)) != ++ ForegroundFailureReason::none) return false; ++ using State = std::unique_ptr; ++ State actual{xkb_state_new(physical), xkb_state_unref}; ++ State expected{xkb_state_new(canonical), xkb_state_unref}; ++ // The client retains Num Lock, but wire keys still mean the neutral US ++ // chord. A third state rejects keypad/navigation changes caused by the lock. ++ State intended{xkb_state_new(canonical), xkb_state_unref}; ++ if (!actual || !expected || !intended) return false; ++ if (modifiers[2]) { ++ const auto canonical_lock = foreground_numlock_mask(canonical); ++ if (!canonical_lock) return false; ++ xkb_state_update_mask(actual.get(), 0, 0, modifiers[2], 0, 0, 0); ++ xkb_state_update_mask(expected.get(), 0, 0, canonical_lock, 0, 0, 0); ++ if (!same_modifier_state(actual.get(), expected.get())) return false; ++ } ++ const auto event = [&](std::uint32_t key, xkb_key_direction direction) { ++ // A client interprets the press using the preceding modifiers event. ++ // Stock both_capslock_cancel gives Shift a Caps_Lock symbol at its ++ // shifted level; that level is not another press. Releases pair by ++ // keycode, but their lock/latch/group effects still must agree. ++ if (direction == XKB_KEY_DOWN && ++ (!same_key_symbols(actual.get(), expected.get(), key + 8) || ++ !same_consumed_modifiers(actual.get(), expected.get(), key + 8) || ++ !same_key_symbols(expected.get(), intended.get(), key + 8) || ++ !same_consumed_modifiers(expected.get(), intended.get(), key + 8))) return false; ++ xkb_state_update_key(actual.get(), key + 8, direction); ++ xkb_state_update_key(expected.get(), key + 8, direction); ++ xkb_state_update_key(intended.get(), key + 8, direction); ++ return same_modifier_state(actual.get(), expected.get()); ++ }; ++ constexpr std::array keys{42, 29, 56, 125}; ++ for (unsigned i = 0; i < keys.size(); ++i) ++ if ((mods & (1u << i)) && keys[i] != code && !event(keys[i], XKB_KEY_DOWN)) return false; ++ if (!event(code, XKB_KEY_DOWN) || !event(code, XKB_KEY_UP)) return false; ++ for (int i = 3; i >= 0; --i) ++ if ((mods & (1u << i)) && keys[i] != code && !event(keys[i], XKB_KEY_UP)) return false; ++ return true; ++} ++} // namespace cua::hyprland +diff --git a/src/plugin.cpp b/src/plugin.cpp +index 88b9900..ae7fc7c 100644 +--- a/src/plugin.cpp ++++ b/src/plugin.cpp +@@ -218,6 +218,11 @@ std::string status_output(bool json) { + + if (json) { + auto result = cua::hyprland::render_status_json(report); ++#ifdef CUA_HYPRLAND_INPUT ++ // Installation checks compiled support before enabling input seats. ++ result.pop_back(); ++ result += ",\"keyboard_layout_independent\":true,\"foreground_numlock_compatible\":true}"; ++#endif + #if defined(CUA_HYPRLAND_TEST_INPUT) || defined(CUA_HYPRLAND_INPUT) + if (g_experiment) { + #ifdef CUA_HYPRLAND_INPUT +diff --git a/tests/agent_keymap_test.py b/tests/agent_keymap_test.py +new file mode 100644 +index 0000000..78a3603 +--- /dev/null ++++ b/tests/agent_keymap_test.py +@@ -0,0 +1,86 @@ ++"""Exercise the production map initializer without a compositor or desktop changes.""" ++import os ++from pathlib import Path ++import re ++import shlex ++import subprocess ++import tempfile ++import unittest ++ ++ROOT = Path(__file__).resolve().parents[1] ++ ++ ++class AgentKeymapTest(unittest.TestCase): ++ def test_production_keymap_and_fd(self): ++ source = (ROOT / 'src/input_experiment.cpp').read_text() ++ body = re.search(r'^ void initialize_agent_keymap\(\).*?^ }', source, re.M | re.S) ++ self.assertIsNotNone(body) ++ fixture = r''' ++#include "keyboard_layout.hpp" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++using namespace cua::hyprland; ++void check(bool v, const char* m) { if (!v) { std::cerr << m; std::exit(1); } } ++struct Lane { ++ xkb_context* xkb_context_ = nullptr; ++ xkb_keymap* keymap = nullptr; ++ xkb_state* keyboard_state = nullptr; ++ int keymap_fd = -1; ++ std::string keymap_text; ++ // INITIALIZER ++ ~Lane() { ++ if (keyboard_state) xkb_state_unref(keyboard_state); ++ if (keymap) xkb_keymap_unref(keymap); ++ if (xkb_context_) xkb_context_unref(xkb_context_); ++ if (keymap_fd >= 0) close(keymap_fd); ++ } ++}; ++int main() { ++ Lane a, b; ++ a.initialize_agent_keymap(); b.initialize_agent_keymap(); ++ check(a.keymap_fd != b.keymap_fd && a.keymap != b.keymap && a.keyboard_state != b.keyboard_state, ++ "lanes share owned map resources"); ++ check(a.keymap_text == b.keymap_text, "lanes advertise different layouts"); ++ for (Lane* lane : {&a, &b}) { ++ struct stat status{}; ++ check(fstat(lane->keymap_fd, &status) == 0 && status.st_size == (off_t)lane->keymap_text.size() + 1, ++ "keymap fd is not terminated serialized map"); ++ std::string text(status.st_size, '\0'); ++ check(pread(lane->keymap_fd, text.data(), text.size(), 0) == status.st_size, "keymap fd cannot be read"); ++ check(text == lane->keymap_text + '\0', "keymap fd differs from advertised map"); ++ check(fcntl(lane->keymap_fd, F_GETFD) & FD_CLOEXEC, "keymap fd inherited by exec"); ++ const int seals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL; ++ check((fcntl(lane->keymap_fd, F_GET_SEALS) & seals) == seals, "keymap fd not immutable"); ++ const int fd = lane->keymap_fd; ++ lane->initialize_agent_keymap(); ++ check(lane->keymap_fd == fd, "initialization replaced advertised map"); ++ check(xkb_state_key_get_one_sym(lane->keyboard_state, 21 + 8) == XKB_KEY_y, ++ "background lane did not initialize US map"); ++ } ++ xkb_state_update_key(a.keyboard_state, 42 + 8, XKB_KEY_DOWN); ++ check(xkb_state_key_get_one_sym(a.keyboard_state, 30 + 8) == XKB_KEY_A, "lane modifier not applied"); ++ check(xkb_state_key_get_one_sym(b.keyboard_state, 30 + 8) == XKB_KEY_a, "lane modifier leaked"); ++} ++'''.replace('// INITIALIZER', body.group()) ++ compiler = shlex.split(os.environ.get('CXX', 'c++')) ++ flags = shlex.split(subprocess.check_output(['pkg-config', '--cflags', '--libs', 'xkbcommon'], text=True)) ++ with tempfile.TemporaryDirectory(prefix='cua-agent-keymap-') as directory: ++ cpp, binary = Path(directory) / 'fixture.cpp', Path(directory) / 'fixture' ++ cpp.write_text(fixture) ++ build = subprocess.run([*compiler, '-std=c++20', '-Wall', '-Wextra', '-Wpedantic', '-Werror', ++ '-I', str(ROOT / 'src'), str(cpp), '-o', str(binary), *flags], ++ capture_output=True, text=True, timeout=60) ++ self.assertEqual(build.returncode, 0, build.stdout + build.stderr) ++ result = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10) ++ self.assertEqual(result.returncode, 0, result.stdout + result.stderr) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/tests/desktop_fault_policy_fixture.cpp b/tests/desktop_fault_policy_fixture.cpp +index 45c48ef..36be139 100644 +--- a/tests/desktop_fault_policy_fixture.cpp ++++ b/tests/desktop_fault_policy_fixture.cpp +@@ -1,6 +1,7 @@ + // Used by desktop_fault_policy_test.py, which inserts actual production bodies. + // Transport effects are counted here; native protocol/app behavior is separate. + #include "drag_geometry.hpp" ++#include "keyboard_layout.hpp" + #include "input_grant.hpp" + #include "passive_pointer_target.hpp" + +@@ -18,6 +19,7 @@ struct Window {}; + struct Surface { int client() const { return 1; } }; + using Target = PassivePointerTarget, std::weak_ptr>; + struct Client { ++ InputRoute route = InputRoute::independent; + bool dead = false; + void* source = nullptr; + int fd = -1; +@@ -34,8 +36,6 @@ struct Pointer { + }; + struct Drag { Client* client; DragGeometry geometry{1}; }; + struct Trace { void mark(const char*, unsigned) {} }; +-enum class ForegroundFailureReason { none }; +-struct ForegroundFailure { ForegroundFailureReason reason; }; + void wl_event_source_remove(void*) {} + int close(int) { return 0; } + std::string refusal(std::string_view reason) { return std::string(reason); } +@@ -79,7 +79,7 @@ struct Lane { + drag.emplace(lease); + } + bool available() const { return !suspended && session; } +- bool layout_qualified() const { return layout; } ++ bool layout_qualified(InputRoute route, uint64_t cap) const { return keyboard_layout_ready(route, cap, layout, layout); } + bool refresh(Client&) const { return refresh_ok; } + template bool primary_conflict(const T&) const { return primary_busy; } + template bool agent_conflict(const T&) const { return peer_busy; } +@@ -122,7 +122,7 @@ int main() { + for (bool session_fault : {true, false}) { + for (int path = 0; path < 3; ++path) { + Lane l; +- if (session_fault) l.session = false; else l.layout = false; ++ if (session_fault) l.session = false; else { l.layout = false; l.capabilities = 2; } + if (path == 0) l.guard_targets(); + if (path == 1) l.target_refusal(*l.lease); + if (path == 2) l.action_refusal(*l.lease); +@@ -133,6 +133,19 @@ int main() { + l.session = true; l.layout = true; l.guard_targets(); l.check_inert(); + } + } ++ // Pointer admission, dispatch and ongoing drag ignore keyboard layout readiness. ++ for (auto route : {InputRoute::independent, InputRoute::primary_foreground}) { ++ for (uint64_t cap : {1, 4, 8, 16}) { ++ Lane l; l.layout = false; l.lease->route = route; l.capabilities = cap; ++ l.target_refusal(*l.lease, route, cap); ++ check(l.lease && l.responses.empty(), "pointer admission gated on layout"); ++ l.action_refusal(*l.lease, cap); ++ check(l.lease && l.responses.empty(), "pointer dispatch gated on layout"); ++ l.guard_targets(); ++ check(l.lease && l.drag && l.held_button && l.responses.empty(), ++ "pointer operation was gated by keyboard layout"); ++ } ++ } + // Every passive safety guard still applies during either desktop fault. + for (bool session_fault : {true, false}) { + for (int bad = 0; bad < 8; ++bad) { +diff --git a/tests/desktop_fault_policy_test.py b/tests/desktop_fault_policy_test.py +index 68ca6c8..7a4d1b4 100644 +--- a/tests/desktop_fault_policy_test.py ++++ b/tests/desktop_fault_policy_test.py +@@ -35,10 +35,11 @@ def fixture(source): + ): + block = source.split(start, 1)[1].split(end, 1)[0] + refusals = [line.strip() for line in block.splitlines() +- if 'if (' in line and ('!available()' in line or '!layout_qualified()' in line)] ++ if 'if (' in line and ('!available()' in line or '!layout_qualified(' in line)] + if len(refusals) != 2: + raise AssertionError(f'production refusal branches not found: {label}') +- methods += '\nvoid ' + label + '(Client& c) {\n' + '\n'.join(refusals) + '\n}' ++ params = ', InputRoute route = InputRoute::independent, uint64_t requested_cap = 2' if label == 'target_refusal' else ', uint64_t cap = 2' ++ methods += '\nvoid ' + label + '(Client& c' + params + ') {\n' + '\n'.join(refusals) + '\n}' + return (ROOT / 'tests/desktop_fault_policy_fixture.cpp').read_text().replace( + '// PRODUCTION_METHODS', methods) + +diff --git a/tests/foreground_modifiers_test.py b/tests/foreground_modifiers_test.py +new file mode 100644 +index 0000000..013c9bf +--- /dev/null ++++ b/tests/foreground_modifiers_test.py +@@ -0,0 +1,225 @@ ++"""Exercise production foreground state admission, dispatch and unwind with XKB.""" ++import os ++from pathlib import Path ++import re ++import shlex ++import subprocess ++import tempfile ++import unittest ++ ++ROOT = Path(__file__).resolve().parents[1] ++ ++ ++class ForegroundModifiersTest(unittest.TestCase): ++ def test_production_modifiers(self): ++ source = (ROOT / 'src/input_experiment.cpp').read_text() ++ methods = [] ++ for name in ('capture_foreground_modifiers', 'require_foreground', 'finish_foreground', ++ 'start_foreground', 'foreground_key'): ++ body = re.search(r'^ \S[^\n]*\b' + name + r'\(.*?^ }', source, re.M | re.S) ++ self.assertIsNotNone(body, name) ++ methods.append(body.group()) ++ preflight = re.search(r' const auto modifiers = capture_foreground_modifiers\(command == "KEY"\);.*?' ++ r' start_foreground\([^\n]*;', source, re.S) ++ self.assertIsNotNone(preflight) ++ fixture = r''' ++#include "keyboard_layout.hpp" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++using namespace cua::hyprland; ++using Clock = std::chrono::steady_clock; ++void check(bool ok, const char* why) { if (!ok) { std::cerr << why; std::exit(1); } } ++struct Vector2D { double x, y; }; ++constexpr int WL_KEYBOARD_KEY_STATE_PRESSED=1, WL_KEYBOARD_KEY_STATE_RELEASED=0; ++constexpr int WL_POINTER_BUTTON_STATE_RELEASED=0; ++struct Root : std::enable_shared_from_this { ++ bool good() const { return true; } ++ int client() const { return 0; } ++ auto at(Vector2D, bool) { return std::pair{shared_from_this(), 0}; } ++}; ++struct Resource { ++ xkb_state* client = nullptr; ++ std::vector symbols; ++ std::array last{}; ++ std::vector> history; ++ bool good() const { return true; } ++ void sendKey(unsigned, unsigned code, unsigned down) { ++ if (down) symbols.push_back(xkb_state_key_get_one_sym(client, code + 8)); ++ } ++ void sendMods(unsigned d, unsigned l, unsigned k, unsigned g) { ++ last={d,l,k,g}; history.push_back(last); xkb_state_update_mask(client,d,l,k,0,0,g); ++ } ++ void sendButton(unsigned, unsigned, unsigned) {} ++ void sendFrame() {} ++}; ++struct Keyboard { ++ struct { unsigned depressed=0,latched=0,locked=0,group=0; } m_modifiersState; ++ xkb_keymap* m_xkbKeymap = nullptr; ++ bool m_enabled=true, shared=true, virt=false; ++ bool shareStates() const { return shared; } ++ bool isVirtual() const { return virt; } ++}; ++struct Seat { ++ std::vector> m_keyboards, m_pointers; ++ bool good() const { return true; } ++}; ++struct SeatManager { ++ std::weak_ptr m_keyboard; ++ bool m_mouse=true; ++ struct { std::shared_ptr keyboardFocus, pointerFocus; } m_state; ++ std::shared_ptr seat; ++ auto seatResourceForClient(int) { return seat; } ++ void setPointerFocus(std::shared_ptr root, Vector2D) { m_state.pointerFocus=root; } ++} manager, *g_pSeatManager=&manager; ++struct InputManager { ++ std::vector> m_keyboards; ++ bool shouldIgnoreVirtualKeyboard(const std::shared_ptr&) { return false; } ++} input, *g_pInputManager=&input; ++namespace Desktop { ++constexpr int FOCUS_REASON_OTHER=0; ++struct Focus { ++ std::shared_ptr root; ++ std::shared_ptr change_on_focus; ++ auto window() { return root; } ++ auto surface() { return root; } ++ void fullWindowFocus(std::shared_ptr, int, std::shared_ptr r) { ++ root=r; manager.m_state.keyboardFocus=r; ++ if (change_on_focus) change_on_focus->m_modifiersState.locked=0; ++ } ++} focus; ++auto focusState() { return &focus; } ++} ++namespace Pointer { struct Manager { void warpTo(Vector2D) {} } pointer; auto mgr(){ return &pointer; } } ++struct Client { ++ bool dead=false, foreground_attempted=false; ++ std::weak_ptr surface, window; ++ std::array geometry{}; ++}; ++struct Lane { ++ xkb_keymap* physical_keymap=nullptr; ++ xkb_keymap* keymap=nullptr; ++ xkb_state* physical_state=nullptr; ++ std::array foreground_modifiers{}; ++ std::vector> foreground_keyboards, foreground_pointers; ++ std::weak_ptr foreground_surface; ++ std::weak_ptr foreground_seat; ++ std::vector held_keys; ++ unsigned held_button=0; ++ bool foreground_started=false,foreground_activating=false,foreground_keyboard_used=false; ++ bool foreground_needs_pointer=false, layout_ready=true, physical_held=false; ++ Client* lease=nullptr; ++ Clock::time_point expires=Clock::now()+std::chrono::hours(1); ++ unsigned consumed=0, refused=0; ++ bool available() { return true; } ++ bool physical_layout_ready() { return layout_ready; } ++ bool point(Client&, double,double) { return true; } ++ unsigned event_ms() { return 0; } ++ void foreground_motion(Client&,double,double) {} ++ ForegroundGuard foreground_guard(Client&) { ++ return {.exact_root=true,.physical_keys=physical_held,.exact_keyboard_focus=true,.exact_pointer_focus=true}; ++ } ++ void revoke(const char*, bool) {} ++ auto refusal(const char*) { return std::string{}; } ++ void send(Client&, const std::string&) { ++refused; } ++ bool consume_grant(Client&, unsigned) { ++consumed; return true; } ++ // METHODS ++ void preflight(Client& c, unsigned code, unsigned mods) { ++ const std::string command="KEY"; ++ const unsigned cap=2; ++ const double x=0,y=0; ++ // PREFLIGHT ++ } ++ ~Lane() { if (physical_state) xkb_state_unref(physical_state); } ++}; ++int main() { ++ auto context=xkb_context_new(XKB_CONTEXT_NO_FLAGS); ++ const xkb_rule_names names{"evdev","pc105","us","","ctrl:nocaps"}; ++ auto physical=xkb_keymap_new_from_names(context,&names,XKB_KEYMAP_COMPILE_NO_FLAGS); ++ auto canonical=agent_keymap(context); ++ auto keyboard=std::make_shared(); keyboard->m_xkbKeymap=physical; ++ manager.m_keyboard=keyboard; input.m_keyboards={keyboard}; ++ auto root=std::make_shared(); ++ manager.m_state.keyboardFocus=root; manager.m_state.pointerFocus=root; Desktop::focus.root=root; ++ manager.seat=std::make_shared(); auto resource=std::make_shared(); ++ resource->client=xkb_state_new(physical); manager.seat->m_keyboards={resource}; ++ Client client; client.surface=root; client.window=root; ++ Lane lane; lane.physical_keymap=physical; lane.keymap=canonical; ++ lane.physical_state=xkb_state_new(physical); lane.lease=&client; ++ const auto num=foreground_numlock_mask(physical); ++ keyboard->m_modifiersState.locked=num; ++ lane.preflight(client,30,1); ++ check(lane.foreground_started && lane.foreground_modifiers[2]==num && resource->last[2]==num, ++ "actual Num Lock state was not captured and published before input"); ++ lane.foreground_key(client,42,true); lane.foreground_key(client,30,true); ++ lane.foreground_key(client,30,false); lane.foreground_key(client,42,false); ++ check(resource->symbols.back()==XKB_KEY_A,"client did not receive intended shifted text"); ++ for (const auto& state : resource->history) ++ check(state[2]==num,"dispatch transiently cleared Num Lock"); ++ lane.finish_foreground(); ++ check(resource->last==std::array{0,0,num,0} && keyboard->m_modifiersState.locked==num, ++ "completion changed real or client Num Lock state"); ++ const auto consumed=lane.consumed; ++ lane.preflight(client,79,0); ++ check(lane.refused==1 && lane.consumed==consumed && !lane.foreground_started, ++ "caller checked neutral state and admitted Num Lock keypad semantics"); ++ keyboard->m_modifiersState.locked=0; ++ lane.preflight(client,79,0); lane.foreground_key(client,79,true); lane.foreground_key(client,79,false); ++ check(resource->symbols.back()==XKB_KEY_KP_End,"neutral keypad contract changed"); lane.finish_foreground(); ++ keyboard->m_modifiersState.locked=num; lane.preflight(client,30,1); lane.foreground_key(client,42,true); ++ keyboard->m_modifiersState.locked=0; ++ try { lane.foreground_key(client,30,true); check(false,"ambient state change accepted"); } ++ catch (ForegroundFailure f) { check(f.reason==ForegroundFailureReason::keyboard_state,"wrong state change refusal"); } ++ lane.finish_foreground(); check(resource->last==std::array{},"cancellation restored stale Num Lock"); ++ keyboard->m_modifiersState.locked=num; lane.preflight(client,30,1); lane.foreground_key(client,42,true); ++ keyboard->m_modifiersState.locked=xkb_keymap_mod_get_mask(physical,XKB_MOD_NAME_CAPS); ++ keyboard->m_modifiersState.depressed=xkb_keymap_mod_get_mask(physical,XKB_MOD_NAME_CTRL); ++ lane.finish_foreground(); ++ check(resource->last[0]==keyboard->m_modifiersState.depressed && resource->last[2]==keyboard->m_modifiersState.locked, ++ "cancellation failed to restore current human Caps and held Control"); ++ keyboard->m_modifiersState.depressed=0; keyboard->m_modifiersState.locked=num; ++ lane.physical_held=true; ++ try { lane.preflight(client,30,0); check(false,"held physical key admitted"); } ++ catch (ForegroundFailure f) { check(f.reason==ForegroundFailureReason::physical_keys,"wrong held-key refusal"); } ++ lane.physical_held=false; ++ auto shared=std::make_shared(); shared->m_xkbKeymap=physical; ++ shared->m_modifiersState.locked=num; ++ keyboard->m_modifiersState.locked=0; ++ input.m_keyboards.push_back(shared); ++ lane.preflight(client,30,0); ++ check(lane.foreground_modifiers[2]==num && resource->last[2]==num,"shared Num Lock was ignored"); ++ lane.foreground_key(client,30,true); lane.foreground_key(client,30,false); lane.finish_foreground(); ++ check(resource->last[2]==num,"shared Num Lock not restored"); ++ shared->m_modifiersState.locked=xkb_keymap_mod_get_mask(physical,XKB_MOD_NAME_CAPS); ++ try { lane.preflight(client,30,0); check(false,"shared Caps state admitted"); } ++ catch (ForegroundFailure f) { check(f.reason==ForegroundFailureReason::keyboard_locked,"wrong shared lock refusal"); } ++ shared->shared=false; lane.preflight(client,30,0); lane.finish_foreground(); ++ // Focus callbacks can change real state between preflight and first event. ++ keyboard->m_modifiersState.locked=num; ++ Desktop::focus.root.reset(); Desktop::focus.change_on_focus=keyboard; ++ try { lane.preflight(client,30,0); check(false,"focus-time state change accepted"); } ++ catch (ForegroundFailure f) { check(f.reason==ForegroundFailureReason::keyboard_state,"wrong focus change refusal"); } ++ lane.finish_foreground(); ++ check(resource->last[2]==0,"focus-time cancellation restored stale lock"); ++ xkb_state_unref(resource->client); xkb_keymap_unref(physical); xkb_keymap_unref(canonical); xkb_context_unref(context); ++} ++'''.replace('// METHODS', '\n'.join(methods)).replace('// PREFLIGHT', preflight.group()) ++ compiler = shlex.split(os.environ.get('CXX', 'c++')) ++ flags = shlex.split(subprocess.check_output(['pkg-config', '--cflags', '--libs', 'xkbcommon'], text=True)) ++ with tempfile.TemporaryDirectory(prefix='cua-foreground-modifiers-') as directory: ++ cpp, binary = Path(directory) / 'fixture.cpp', Path(directory) / 'fixture' ++ cpp.write_text(fixture) ++ build = subprocess.run([*compiler, '-std=c++20', '-Wall', '-Wextra', '-Wpedantic', '-Werror', ++ '-I', str(ROOT / 'src'), str(cpp), '-o', str(binary), *flags], ++ capture_output=True, text=True, timeout=60) ++ self.assertEqual(build.returncode, 0, build.stdout + build.stderr) ++ result = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10) ++ self.assertEqual(result.returncode, 0, result.stdout + result.stderr) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/tests/keyboard_layout_test.cpp b/tests/keyboard_layout_test.cpp +new file mode 100644 +index 0000000..6d57616 +--- /dev/null ++++ b/tests/keyboard_layout_test.cpp +@@ -0,0 +1,130 @@ ++#include "keyboard_layout.hpp" ++#include ++#include ++#include ++ ++using namespace cua::hyprland; ++void check(bool condition, const char* message) { ++ if (!condition) { std::cerr << message << '\n'; std::exit(1); } ++} ++int main() { ++ using Context = std::unique_ptr; ++ using Map = std::unique_ptr; ++ using State = std::unique_ptr; ++ Context context{xkb_context_new(XKB_CONTEXT_NO_FLAGS), xkb_context_unref}; ++ Map agent{agent_keymap(context.get()), xkb_keymap_unref}; ++ Map second{agent_keymap(context.get()), xkb_keymap_unref}; ++ check(bool(agent) && bool(second), "canonical maps unavailable"); ++ const auto map = [&](const char* layout, const char* options) { ++ const xkb_rule_names names{"evdev", "pc105", layout, "", options}; ++ return Map{xkb_keymap_new_from_names(context.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS), xkb_keymap_unref}; ++ }; ++ auto stock = map("us", "compose:caps,shift:both_capslock_cancel"); ++ auto nocaps = map("us", "ctrl:nocaps"); ++ auto swapctrl = map("us", "ctrl:swapcaps"); ++ auto swapalt = map("us", "altwin:swap_alt_win"); ++ auto german = map("de", ""); ++ check(stock && nocaps && swapctrl && swapalt && german, "test keymaps unavailable"); ++ for (auto* physical : {stock.get(), nocaps.get(), swapctrl.get(), swapalt.get(), german.get()}) { ++ check(foreground_chord_compatible(physical, agent.get(), 30, 0, {}), "unrelated remap blocked A"); ++ check(foreground_chord_compatible(physical, agent.get(), 30, 1, {}), "unrelated remap blocked Shift+A"); ++ check(foreground_chord_compatible(physical, agent.get(), 28, 0, {}), "unrelated remap blocked Return"); ++ } ++ check(foreground_chord_compatible(stock.get(), agent.get(), 30, 2, {}), "stock Omarchy blocked Ctrl+A"); ++ check(foreground_chord_compatible(nocaps.get(), agent.get(), 30, 2, {}), "Caps to Ctrl blocked Ctrl+A"); ++ check(!foreground_chord_compatible(swapctrl.get(), agent.get(), 30, 2, {}), "Ctrl/Caps swap sent wrong Ctrl+A"); ++ check(!foreground_chord_compatible(swapctrl.get(), agent.get(), 29, 0, {}), "remapped modifier key accepted"); ++ check(!foreground_chord_compatible(swapalt.get(), agent.get(), 30, 4, {}), "Alt/Super swap sent wrong Alt+A"); ++ check(!foreground_chord_compatible(swapalt.get(), agent.get(), 30, 8, {}), "Alt/Super swap sent wrong Super+A"); ++ check(!foreground_chord_compatible(german.get(), agent.get(), 21, 0, {}), "German Z accepted as US Y"); ++ check(!foreground_chord_compatible(german.get(), agent.get(), 3, 1, {}), "German shifted punctuation accepted"); ++ check(!foreground_chord_compatible(nullptr, agent.get(), 30, 0, {}), "missing physical map accepted"); ++ for (auto* physical : {agent.get(), stock.get(), nocaps.get()}) { ++ const auto numlock = foreground_numlock_mask(physical); ++ check(numlock != 0, "Num Lock encoding missing"); ++ for (const auto locked : {0u, numlock}) { ++ const std::array ambient{0, 0, locked, 0}; ++ for (const auto key : {30u, 48u, 44u, 2u, 11u, 28u, 57u}) ++ for (const auto mods : {0u, 1u, 2u, 3u}) ++ check(foreground_chord_compatible(physical, agent.get(), key, mods, ambient), ++ "Num Lock blocked ordinary text or shortcut"); ++ for (const auto key : {71u, 72u, 75u, 79u, 82u, 83u}) ++ check(foreground_chord_compatible(physical, agent.get(), key, 0, ambient) == !locked, ++ "Num Lock keypad semantic change was ignored"); ++ } ++ const auto caps = xkb_keymap_mod_get_mask(physical, XKB_MOD_NAME_CAPS); ++ for (const auto locked : {caps, caps | numlock, 0x80000000u}) ++ check(!foreground_chord_compatible(physical, agent.get(), 30, 0, {0, 0, locked, 0}), ++ "unsupported lock accepted"); ++ check(!foreground_chord_compatible(physical, agent.get(), 30, 0, {numlock, 0, numlock, 0}), ++ "held modifier accepted with Num Lock"); ++ check(!foreground_chord_compatible(physical, agent.get(), 30, 0, {0, numlock, numlock, 0}), ++ "latched modifier accepted with Num Lock"); ++ check(!foreground_chord_compatible(physical, agent.get(), 30, 0, {0, 0, numlock, 1}), ++ "nonzero group accepted with Num Lock"); ++ } ++ const auto numlock = foreground_numlock_mask(agent.get()); ++ check(!foreground_chord_compatible(swapctrl.get(), agent.get(), 30, 2, {0, 0, numlock, 0}), ++ "Num Lock hid Ctrl remap"); ++ check(!foreground_chord_compatible(german.get(), agent.get(), 21, 0, {0, 0, numlock, 0}), ++ "Num Lock hid layout mismatch"); ++ // All supported wire chords remain compatible on the independent map. ++ for (unsigned code = 1; code <= 247; ++code) ++ if (code != 58 && code != 69 && code != 70) ++ for (unsigned mods = 0; mods < 16; ++mods) ++ check(foreground_chord_compatible(agent.get(), agent.get(), code, mods, {}), "canonical chord rejected"); ++ // Two lanes never share modifier state and never mutate the human state. ++ State first{xkb_state_new(agent.get()), xkb_state_unref}; ++ State other{xkb_state_new(second.get()), xkb_state_unref}; ++ State human{xkb_state_new(german.get()), xkb_state_unref}; ++ xkb_state_update_key(first.get(), 42 + 8, XKB_KEY_DOWN); ++ check(xkb_state_key_get_one_sym(first.get(), 30 + 8) == XKB_KEY_A, "agent shift not active"); ++ check(xkb_state_key_get_one_sym(other.get(), 30 + 8) == XKB_KEY_a, "agent shift leaked across lanes"); ++ check(xkb_state_key_get_one_sym(human.get(), 21 + 8) == XKB_KEY_z, "human layout changed"); ++ // Same symbols can hide a changed XKB action. Simulate an A that sets Mod3. ++ char* serialized = xkb_keymap_get_as_string(agent.get(), XKB_KEYMAP_FORMAT_TEXT_V1); ++ std::string changed = serialized; ++ std::free(serialized); ++ const auto at = changed.find("key "); ++ const auto end = changed.find(';', at); ++ check(at != std::string::npos && end != std::string::npos, "test action fixture unavailable"); ++ changed.replace(at, end - at + 1, ++ "key { type=\"ALPHABETIC\", symbols[Group1]=[a,A], actions[Group1]=[LockMods(modifiers=Mod3),LockMods(modifiers=Mod3)] };"); ++ Map lock{xkb_keymap_new_from_string(context.get(), changed.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), xkb_keymap_unref}; ++ check(bool(lock), "action fixture failed to compile"); ++ check(!foreground_chord_compatible(lock.get(), agent.get(), 30, 0, {}), "hidden lock action accepted"); ++ check(!foreground_chord_compatible(lock.get(), agent.get(), 30, 0, {0, 0, numlock, 0}), ++ "Num Lock hid an unexpected lock action"); ++ // Equal symbols and modifier state can still alter toolkit shortcut matching. ++ serialized = xkb_keymap_get_as_string(agent.get(), XKB_KEYMAP_FORMAT_TEXT_V1); ++ changed = serialized; ++ std::free(serialized); ++ const auto type_at = changed.find("type \"ALPHABETIC\""); ++ const auto type_end = changed.find("};", type_at); ++ check(type_at != std::string::npos && type_end != std::string::npos, "key type fixture unavailable"); ++ changed.insert(type_end, "preserve[Shift] = Shift;\n"); ++ Map preserved{xkb_keymap_new_from_string(context.get(), changed.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), xkb_keymap_unref}; ++ check(bool(preserved), "preserved modifier fixture failed to compile"); ++ check(!foreground_chord_compatible(preserved.get(), agent.get(), 30, 1, {}), "consumed modifier mismatch accepted"); ++ check(!foreground_chord_compatible(preserved.get(), agent.get(), 30, 1, {0, 0, numlock, 0}), ++ "Num Lock hid changed shortcut consumption"); ++ // A modifier that preserves Shift state but emits another symbol must fail. ++ serialized = xkb_keymap_get_as_string(agent.get(), XKB_KEYMAP_FORMAT_TEXT_V1); ++ changed = serialized; ++ std::free(serialized); ++ const auto shift_at = changed.find("key "); ++ const auto shift_end = changed.find(';', shift_at); ++ check(shift_at != std::string::npos && shift_end != std::string::npos, "shift fixture unavailable"); ++ changed.replace(shift_at, shift_end - shift_at + 1, ++ "key { symbols[Group1]=[Delete], actions[Group1]=[SetMods(modifiers=Shift)] };"); ++ Map badshift{xkb_keymap_new_from_string(context.get(), changed.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), xkb_keymap_unref}; ++ check(bool(badshift), "shift fixture failed to compile"); ++ check(!foreground_chord_compatible(badshift.get(), agent.get(), 30, 1, {}), "modifier press symbols not checked"); ++ for (auto route : {InputRoute::independent, InputRoute::primary_foreground}) { ++ for (uint64_t cap : {1, 4, 8, 16}) check(keyboard_layout_ready(route, cap, false, false), "pointer gated on layout"); ++ check(!keyboard_layout_ready(route, 2, false, false), "key accepted without map"); ++ } ++ check(keyboard_layout_ready(InputRoute::independent, 2, true, false), "background depends on human map"); ++ check(!keyboard_layout_ready(InputRoute::primary_foreground, 2, true, false), "foreground uses agent readiness"); ++ std::cout << "keyboard layout tests passed\n"; ++} +diff --git a/tests/plugin_input_lifetime_test.cpp b/tests/plugin_input_lifetime_test.cpp +index ff09451..456da44 100644 +--- a/tests/plugin_input_lifetime_test.cpp ++++ b/tests/plugin_input_lifetime_test.cpp +@@ -51,12 +51,17 @@ int main() { + check(setenv("XDG_RUNTIME_DIR", directory, 1) == 0 && + setenv("HYPRLAND_INSTANCE_SIGNATURE", "mock", 1) == 0, + "select runtime"); +- Config::Values::configured_bool_override = true; ++ Config::Values::configured_bool_override = false; + static_cast(pluginInit(nullptr)); + const auto toggle = [](bool enabled) { + HyprlandAPI::registered_bool->set_mock_value(enabled); + Event::bus()->m_events.config.reloaded.emit(); + }; ++#ifdef CUA_HYPRLAND_INPUT ++ const auto initial_status = HyprlandAPI::registered_legacy_command->fn(FORMAT_JSON, {}); ++ check(created == 0 && initial_status.find("\"configured\":false") != std::string::npos && initial_status.find("\"keyboard_layout_independent\":true") != std::string::npos && initial_status.find("\"foreground_numlock_compatible\":true") != std::string::npos, ++ "disabled production module advertises compiled keyboard support before enable"); ++#endif + for (unsigned i = 0; i < 20; ++i) { + toggle(true); + #ifdef CUA_HYPRLAND_INPUT +@@ -64,6 +69,8 @@ int main() { + check(status.find("\"state\":\"input_v3_candidate\"") != std::string::npos && + status.find("trusted_local_per_action") != std::string::npos && + status.find("\"input\":{}") != std::string::npos && ++ status.find("\"keyboard_layout_independent\":true") != std::string::npos && ++ status.find("\"foreground_numlock_compatible\":true") != std::string::npos && + status.find("operator") == std::string::npos, + "v3 status advertises its actual admission mode"); + #endif diff --git a/pkgbuilds/cursor-bin/PKGBUILD b/pkgbuilds/cursor-bin/PKGBUILD index d0a3e32..1a69432 100644 --- a/pkgbuilds/cursor-bin/PKGBUILD +++ b/pkgbuilds/cursor-bin/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Gunther Schulz pkgname=cursor-bin -pkgver=3.20.21 +pkgver=3.21.16 pkgrel=2 pkgdesc='AI-first coding environment' arch=('x86_64') @@ -12,11 +12,11 @@ _electron=electron42 depends=(xdg-utils ripgrep $_electron nodejs 'gcc-libs' 'hicolor-icon-theme' 'libxkbfile') options=(!strip !debug) # Don't break ext of VSCode -_commit=f09fca384ceca23f7bf21f9c23655b162641d747 +_commit=8ae78e8eee1e63479c7e0504b664bc0a80c6800f source=("https://downloads.cursor.com/production/${_commit}/linux/x64/deb/amd64/deb/cursor_${pkgver}_amd64.deb" "https://gitlab.archlinux.org/archlinux/packaging/packages/code/-/raw/main/code."{sh,mjs} rg.sh) -sha512sums=('8329138d207309d16410f1cb58da18eea1a034a35f2bdd022ef9b373bb8f1b3d80e489e65256b7283c40e10da77962d158bfa3a64e742337a942633810d80dbe' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a') +sha512sums=('032c86a5d51f154ce36b1a0bf34aa06b2d117666b4372a787ea3117fc0b2b1686e952c721388f2eaf7787f2e0581378c98608048126f7470df2e85e9dbd75ca4' '937299c6cb6be2f8d25f7dbc95cf77423875c5f8353b8bd6cd7cc8e5603cbf8405b14dbf8bd615db2e3b36ed680fc8e1909410815f7f8587b7267a699e00ab37' '793f9ff6306e3992ac89802d98110cba288ea1181a901467333293b7d76182ef9792c2a39ff49d9347a18a174b1f42bc58862091dff583f4146c2704eea28033' 'e79fe7659f59d1ae02fc68816399bfd31587315df6cdb6ccf1d0ca76f7cdc692c2a42b30591c0091147bd97ef14b1c7745dc26bd7cb3ea6bba45698e5044fa2a') noextract=(cursor_${pkgver}_amd64.deb) # avoid double tarball _app=usr/share/cursor/resources/app package() { @@ -24,6 +24,9 @@ package() { bsdtar -xOf ${noextract[0]} data.tar.xz | tar -xJf - -C "$pkgdir" \ --exclude 'usr/share/cursor/[^r]*' --exclude 'usr/share/cursor/*.pak' cd "$pkgdir" + # Disable Cursor's bundled updater; Omarchy manages updates via pacman (#238). + sed -i '/^[[:space:]]*"\(backupUpdateUrl\|updateUrl\)":/d' \ + "${_app}/product.json" mv usr/share/zsh/{vendor-completions,site-functions} ln -sf /usr/bin/node ${_app}/resources/helpers/node install -Dm755 "${srcdir}/rg.sh" ${_app}/node_modules/@vscode/ripgrep/bin/rg diff --git a/pkgbuilds/cursor-cli/PKGBUILD b/pkgbuilds/cursor-cli/PKGBUILD index 14869ab..c8fc1d5 100644 --- a/pkgbuilds/cursor-cli/PKGBUILD +++ b/pkgbuilds/cursor-cli/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Ismet Togay # Contributor: Christopher Cooper pkgname=cursor-cli -pkgver=2026.09.10.1.fd3934a +pkgver=2026.09.18.1.9a7762b # Upstream is YYYY.MM.DD-. pkgver cannot contain hyphens, and hashes are # not monotonically ordered, so pkgver is YYYY.MM.DD..: n resets to 1 # on a new date and increments when the same date gets a new hash. @@ -25,8 +25,8 @@ source_x86_64=("cursor-cli-${_upstream_ver}-x86_64.tar.gz::https://downloads.cur source_aarch64=("cursor-cli-${_upstream_ver}-aarch64.tar.gz::https://downloads.cursor.com/lab/${_upstream_ver}/linux/arm64/agent-cli-package.tar.gz") b2sums=('d241ee9895bdb1c17514438fde8528222a8f2326568bd7a033d7a1b11432ce6b4575ff1a50625764bfe6bc6f8a9dc060f7439c3be7e95f8fd02912cdd37a011d' '1928e04c713e13911ea607f84c3e4a2fed1f76af9795503811078f43d2b53c753e28b2233e553fc17e766831800fb0dbc272aad2a80b387f95ba6071d7d4116a') -b2sums_x86_64=('121c0128fd630565c7000b86ae53bf76e73fd72c1ab8ba2509fae7739b1c7f4bed0587a82137340303ac4704f3344cf63a10eab0e8bea262c8e48bbb21bcb742') -b2sums_aarch64=('bfc0f540190214396df3cbb93c411ab8055677bc1dd0b0e76d1b914d6c6d90af12519434227ba8b38a38249f1bfe0a8848f47e16dc048b4fa005d366815307be') +b2sums_x86_64=('3fccee6929df1042d03461895e56c222a996d3ae9e4f9c61dcc5e6ab7b1d075d3265c21a44f48dd94de0dcde4f8012cc39bfbf323e2bb0c6106cac79885c35ae') +b2sums_aarch64=('3d3bb0a3cb7e2409acf4925f207eaa4e3f41782c3c947e2834b69664b116b972da0b67eea17147fc524ea248af9919494570adc7c48d12c44f12deca17ba2c28') prepare() { # Block cursor-agent auto-updates by making its versions directory diff --git a/pkgbuilds/dell-xps-touchpad-haptics/PKGBUILD b/pkgbuilds/dell-xps-touchpad-haptics/PKGBUILD index 68d9967..9d35b3a 100644 --- a/pkgbuilds/dell-xps-touchpad-haptics/PKGBUILD +++ b/pkgbuilds/dell-xps-touchpad-haptics/PKGBUILD @@ -2,7 +2,7 @@ pkgname=dell-xps-touchpad-haptics pkgver=1.0.0 -pkgrel=3 +pkgrel=4 pkgdesc="Synaptics haptic touchpad presets for Dell XPS on Omarchy" arch=('x86_64') url="https://github.com/omacom-io/omarchy-pkgs" diff --git a/pkgbuilds/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install b/pkgbuilds/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install index 587f96c..25ddfa4 100644 --- a/pkgbuilds/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install +++ b/pkgbuilds/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install @@ -3,6 +3,7 @@ _default_level="high" _env_path="/etc/dell-xps-touchpad-haptics.env" _legacy_env_path="/etc/omarchy-dell-haptic-touchpad.env" _legacy_override_dir="/etc/systemd/system/dell-xps-haptic-touchpad.service.d" +_runuser_path="/usr/bin/runuser" _existing_home() { local line value @@ -124,18 +125,13 @@ _ensure_user_config() { local config_dir="$home/.config/omarchy" local config_path="$config_dir/dell-haptic.conf" - if [[ ! -f $config_path ]] && ! env HOME="$home" USER="$user" LOGNAME="$user" \ + if [[ ! -f $config_path ]] && ! "$_runuser_path" --user "$user" -- \ + /usr/bin/env HOME="$home" USER="$user" LOGNAME="$user" \ /usr/bin/dell-xps-touchpad-haptics set "$_default_level"; then echo ":: Failed to create ${config_path} for user '$user'." >&2 return 1 fi - if [[ -f $config_path ]]; then - chown "$user:$user" "$home/.config" 2>/dev/null || true - chown "$user:$user" "$config_dir" 2>/dev/null || true - chown "$user:$user" "$config_path" 2>/dev/null || true - fi - return 0 } diff --git a/pkgbuilds/dotnet-core-bin/.omarchy/package.json b/pkgbuilds/dotnet-core-bin/.omarchy/package.json new file mode 100644 index 0000000..db663bc --- /dev/null +++ b/pkgbuilds/dotnet-core-bin/.omarchy/package.json @@ -0,0 +1,19 @@ +{ + "source": "local", + "origin": { + "aur": "dotnet-core-bin", + "commit": "2c499d7ce634efb8e93eee4c4239490b02e98e09" + }, + "upstream": { + "watch": { + "json": "https://builds.dotnet.microsoft.com/dotnet/release-metadata/10.0/releases.json", + "path": "latest-sdk", + "fields": { + "runtime": "latest-runtime" + }, + "variables": { + "_runtimever": "{runtime}" + } + } + } +} diff --git a/pkgbuilds/dotnet-core-bin/PKGBUILD b/pkgbuilds/dotnet-core-bin/PKGBUILD new file mode 100644 index 0000000..040344b --- /dev/null +++ b/pkgbuilds/dotnet-core-bin/PKGBUILD @@ -0,0 +1,131 @@ +# Maintainer: Attila Greguss +# Co-Maintainer: Nate Plumm + +pkgbase=dotnet-core-bin +pkgname=( + 'dotnet-host-bin' + 'aspnet-runtime-bin' + 'dotnet-runtime-bin' + 'dotnet-sdk-bin' + 'dotnet-targeting-pack-bin' + 'aspnet-targeting-pack-bin' + ) +# Version the split family by SDK release; dependencies expose runtime versions. +pkgver=10.0.401 +_runtimever=10.0.12 +_sdkver=$pkgver +_short_ver=10.0 +pkgrel=1 +arch=('x86_64' 'armv7h' 'aarch64') +url='https://www.microsoft.com/net/core' +license=('MIT') +options=('staticlibs') +source=('dotnet.sh') +source_armv7h=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-arm.tar.gz") +source_aarch64=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-arm64.tar.gz") +source_x86_64=("https://builds.dotnet.microsoft.com/dotnet/Sdk/${_sdkver}/dotnet-sdk-${_sdkver}-linux-x64.tar.gz") +sha512sums=('768151c7179fb6a126b3de9cae01e363e8894f6fab384b1e2c5066c2adca4578638983b1b62aea10dd18045e6d6e8f8ea13280481134de94f004a118919b2c06') +sha512sums_armv7h=('94a8a52862ca9f0de1075a468d6e4e307a1d4463098a9e8266e66939709a812b0126e212cce7e8c6feae6b078d86c8b77e088814a0e32531edb0da0a1ce90c11') +sha512sums_aarch64=('58ace73ced6b4360754689a686bdfb8a317f4da6cb8bb416dbc7d0ba9f47e43e3c09f5eb1f1a1cfaacbd10df9558da4882bf2a5e195d6ab56a02c1f9f76102ed') +sha512sums_x86_64=('51c8b999af9e8dd9998c9edc5944e19a90788862068acd38694e098889054ce8c23d4f0c5cccfa16bf187d044562359e5ee69a9f8ad0bbe913ba90311fbce25b') + +# Keep each split package's notices usable when installed independently. +_install_license() { + install -Dm644 LICENSE.txt "$pkgdir/usr/share/licenses/$pkgname/LICENSE.txt" + install -Dm644 ThirdPartyNotices.txt "$pkgdir/usr/share/licenses/$pkgname/ThirdPartyNotices.txt" +} + +package_dotnet-host-bin() { + pkgdesc='A generic driver for the .NET Core Command Line Interface (binary)' + provides=("dotnet-host" "dotnet-host=${_runtimever}") + conflicts=('dotnet-host') + depends=( + 'libgcc' + 'libstdc++' + 'glibc' + ) + + install -dm 755 "${pkgdir}"/usr/{bin,lib,share/{dotnet,dnx}} + cp -dr --no-preserve='ownership' dotnet host dnx "${pkgdir}"/usr/share/dotnet/ + _install_license + ln -sf /usr/share/dotnet/dotnet "${pkgdir}"/usr/bin/dotnet + ln -sf /usr/share/dotnet/dnx "${pkgdir}"/usr/bin/dnx + ln -sf /usr/share/dotnet/host/fxr/"${_runtimever}"/libhostfxr.so "${pkgdir}"/usr/lib/libhostfxr.so + install -Dm 644 "${srcdir}"/dotnet.sh -t "${pkgdir}"/etc/profile.d/ +} + +package_dotnet-runtime-bin() { + pkgdesc='The .NET Core runtime (binary)' + depends=( + "dotnet-host>=${_runtimever}" + 'libgcc' + 'libstdc++' + 'glibc' + 'icu' + 'libunwind' + 'zlib' + 'openssl' + ) + optdepends=('lttng-ust2.12: CoreCLR tracing') + provides=("dotnet-runtime=${_runtimever}" "dotnet-runtime-${_short_ver}") + conflicts=("dotnet-runtime=${_runtimever}" "dotnet-runtime-${_short_ver}") + + install -dm 755 "${pkgdir}"/usr/share/{dotnet/shared,licenses} + cp -dr --no-preserve='ownership' shared/Microsoft.NETCore.App "${pkgdir}"/usr/share/dotnet/shared/ + _install_license +} + +package_aspnet-runtime-bin() { + pkgdesc='The ASP.NET Core runtime (binary)' + depends=('dotnet-runtime-bin') + provides=("aspnet-runtime=${_runtimever}" "aspnet-runtime-${_short_ver}") + conflicts=("aspnet-runtime=${_runtimever}" "aspnet-runtime-${_short_ver}") + + install -dm 755 "${pkgdir}"/usr/share/{dotnet/shared,licenses} + cp -dr --no-preserve='ownership' shared/Microsoft.AspNetCore.App "${pkgdir}"/usr/share/dotnet/shared/ + _install_license +} + +package_dotnet-sdk-bin() { + pkgdesc='The .NET Core SDK (binary)' + depends=( + 'glibc' + 'libgcc' + 'libstdc++' + 'dotnet-runtime-bin' + 'dotnet-targeting-pack-bin' + 'aspnet-runtime-bin' + 'aspnet-targeting-pack-bin' + ) + provides=("dotnet-sdk-bin" "dotnet-sdk=${pkgver}" "dotnet-sdk-${_short_ver}=${pkgver}") + conflicts=("dotnet-sdk-bin" "dotnet-sdk=${pkgver}" "dotnet-sdk-${_short_ver}") + + install -dm 755 "${pkgdir}"/usr/share/{dotnet,licenses} + cp -dr --no-preserve='ownership' sdk sdk-manifests templates "${pkgdir}"/usr/share/dotnet/ + _install_license +} + +package_dotnet-targeting-pack-bin() { + pkgdesc='The .NET Core targeting pack (binary)' + provides=(dotnet-targeting-pack=${_runtimever} dotnet-targeting-pack-${_short_ver}) + conflicts=(dotnet-targeting-pack=${_runtimever} dotnet-targeting-pack-${_short_ver}) + + if [ $CARCH = 'x86_64' ]; then msarch=x64; + elif [ $CARCH = 'armv7h' ]; then msarch=arm; + elif [ $CARCH = 'aarch64' ]; then msarch=arm64; fi + + install -dm 755 "${pkgdir}"/usr/share/{dotnet,dotnet/packs,licenses} + cp -dr --no-preserve='ownership' packs/Microsoft.NETCore.App.{Host.linux-${msarch},Ref} "${pkgdir}"/usr/share/dotnet/packs/ + _install_license +} + +package_aspnet-targeting-pack-bin() { + pkgdesc='The ASP.NET Core targeting pack (binary)' + depends=(dotnet-targeting-pack-bin) + provides=(aspnet-targeting-pack=${_runtimever} aspnet-targeting-pack-${_short_ver}) + conflicts=(aspnet-targeting-pack=${_runtimever} aspnet-targeting-pack-${_short_ver}) + + install -dm 755 "${pkgdir}"/usr/share/{dotnet,dotnet/packs,licenses} + cp -dr --no-preserve='ownership' packs/Microsoft.AspNetCore.App.Ref "${pkgdir}"/usr/share/dotnet/packs/ + _install_license +} diff --git a/pkgbuilds/dotnet-core-bin/dotnet.sh b/pkgbuilds/dotnet-core-bin/dotnet.sh new file mode 100755 index 0000000..48b6ee6 --- /dev/null +++ b/pkgbuilds/dotnet-core-bin/dotnet.sh @@ -0,0 +1,19 @@ +# Set location for AppHost lookup +[ -z "$DOTNET_ROOT" ] && export DOTNET_ROOT=/usr/share/dotnet + +# Add dotnet directory to PATH, according to docs it must be added, plus VSCode C# Dev Kit doesn't work without this. +# See https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#set-environment-variables-system-wide +case "$PATH" in + *"$DOTNET_ROOT"* ) true ;; + * ) PATH="$PATH:$DOTNET_ROOT" ;; +esac + +# Add dotnet tools directory to PATH +[ -z "$DOTNET_TOOLS_PATH" ] && export DOTNET_TOOLS_PATH="$HOME/.dotnet/tools" +case "$PATH" in + *"$DOTNET_TOOLS_PATH"* ) true ;; + * ) PATH="$PATH:$DOTNET_TOOLS_PATH" ;; +esac + +# Extract self-contained executables under HOME to avoid multi-user issues from using the default '/var/tmp' +[ -z "$DOTNET_BUNDLE_EXTRACT_BASE_DIR" ] && export DOTNET_BUNDLE_EXTRACT_BASE_DIR="${XDG_CACHE_HOME:-"$HOME"/.cache}/dotnet_bundle_extract" diff --git a/pkgbuilds/dropbox/PKGBUILD b/pkgbuilds/dropbox/PKGBUILD index 0b081a1..5e052aa 100644 --- a/pkgbuilds/dropbox/PKGBUILD +++ b/pkgbuilds/dropbox/PKGBUILD @@ -4,7 +4,7 @@ # Contributor: David Manouchehri pkgname=dropbox -pkgver=268.4.4124 +pkgver=270.4.3312 pkgrel=1 pkgdesc="A free service that lets you bring your photos, docs, and videos anywhere and share them easily." arch=("x86_64") @@ -27,7 +27,7 @@ source=("DropboxGlyph_Blue.svg" "dropbox@.service" "https://edge.dropboxstatic.com/dbx-releng/client/dropbox-lnx.x86_64-$pkgver.tar.gz"{,.asc}) -sha256sums=('9ba76205ec5838db85d822f23cfd7e2112fd2757e8031d8374709f102143c548' '1610ff57e8b20ee7a37682c3cc505da4ddc9cec2bd7234c90c0f2073657521d2' '6c67a9c8c95c08fafafd2f1d828074b13e3347b05d2e4f4bf4e62746115d7477' '98581e65a91ae1f19ed42edcdaaa52e102298b5da0d71b50089393d364474d3d' 'fccaaa9fbe008e56729fafe13b581e2106f38a8b6f5d61de8bf546f349d5b155' 'SKIP') +sha256sums=('9ba76205ec5838db85d822f23cfd7e2112fd2757e8031d8374709f102143c548' '1610ff57e8b20ee7a37682c3cc505da4ddc9cec2bd7234c90c0f2073657521d2' '6c67a9c8c95c08fafafd2f1d828074b13e3347b05d2e4f4bf4e62746115d7477' '98581e65a91ae1f19ed42edcdaaa52e102298b5da0d71b50089393d364474d3d' '35404957d2a15dcac998d53cbec692d5236e197493f6c009accd91ea9aa8f34c' 'SKIP') # The PGP key fingerprint should match the one on https://www.dropbox.com/help/desktop-web/linux-commands validpgpkeys=( '1C61A2656FB57B7E4DE0F4C1FC918B335044912E' # Dropbox Automatic Signing Key diff --git a/pkgbuilds/elsewhen/.omarchy/README.md b/pkgbuilds/elsewhen/.omarchy/README.md new file mode 100644 index 0000000..9195490 --- /dev/null +++ b/pkgbuilds/elsewhen/.omarchy/README.md @@ -0,0 +1,17 @@ +# elsewhen + +Installs the Elsewhen world clock plugin from the `v{pkgver}` GitHub tag archive into `/usr/share/omarchy/shell/plugins/omacom.elsewhen/` (the directory name is the plugin id the shell scans for, not the package name), plus `LICENSE` under `/usr/share/licenses/elsewhen/` and the upstream `README.md` under `/usr/share/doc/elsewhen/`. The shell scans this directory alongside its bundled plugins. + +`package()` copies an explicit allow-list (`manifest.json`, every `*.qml` and `*.js`, `cities.json`, `world.json`, `worldclock-data.py`), so `tests/`, `.github/` and `.gitignore` never ship, and it fails the build if `manifest.json` is missing, does not declare `omacom.elsewhen`, or does not name a present `Panel.qml` as the entry point. An upstream release that adds a runtime file outside those patterns needs the allow-list extended here; the sync only moves versions and checksums. Every file is 0644: `Panel.qml` runs the script as `python3 /worldclock-data.py`, so it needs no execute bit. No install hook: Omarchy restarts the shell after `omarchy update`, and nothing here may write into a user home. The script's only writes go to `$XDG_CACHE_HOME/omacom-elsewhen/`, which it creates itself at runtime. + +Dependencies, cited as `file: tool` in the upstream tree: + +- `omarchy`: `ArcText.qml`, `Chip.qml`, `EarthRow.qml`, `Globe.qml`, `MiniGlobe.qml`, `MoonDot.qml`, `Panel.qml: import qs.Commons`; `EarthRow.qml`, `Globe.qml`, `Panel.qml: import qs.Ui`. Owns `/usr/share/omarchy` and the shell plugin directory. +- `quickshell`: `Globe.qml`, `Panel.qml: import Quickshell`; `Globe.qml`, `MiniGlobe.qml`, `Panel.qml: import Quickshell.Io` (`Process`, `FileView`, `StdioCollector`). +- `python`: `Panel.qml: python3 /worldclock-data.py` (the facts process). `worldclock-data.py` imports only `json`, `os`, `sys`, `time`, `urllib`. +- Left implicit as members of `base`, per Arch convention: `bash` (`Panel.qml`, `Globe.qml: bash -c` wraps every probe), `coreutils` (`Panel.qml`, `Globe.qml: date`, one probe per refresh), `systemd` (`Panel.qml: timedatectl show`, `timedatectl list-timezones`, each with a fallback: `/etc/localtime` for the home zone and `find /usr/share/zoneinfo` for the catalog), `sed` and `grep` (`Panel.qml`: the symlink target of `/etc/localtime` and the zoneinfo catalog filter), `findutils` and `tzdata` (that fallback; `worldclock-data.py: /usr/share/zoneinfo/zone1970.tab`). `omarchy` cannot run without any of them either. +- Not a dependency: `iso-codes`. Only `tests/currency_check.py` reads `/usr/share/iso-codes/json`, to validate the currency table before a release; the runtime never touches it. + +Release tracking: `bin/sync-upstream` follows `omacom/elsewhen` through the `upstream.watch.github` provider, which reads the GitHub Releases feed (drafts and prereleases excluded; a tag with no published Release is not seen) and matches exactly `vX.Y.Z`, the grammar upstream's `scripts/set-version.sh` enforces. A newer release rewrites `pkgver`, resets `pkgrel` to 1, fetches `archive/refs/tags/v{pkgver}.tar.gz` again and rewrites `sha256sums` from the download. The Release's `published_at` is what lets `min_release_age: 24h` hold a fresh release for a day; `release_ring: fast` builds it straight to rc and stable as well as edge. + +The watch only moves on a version increase, so the first release's digest is filled in by hand (`curl -fsSL | sha256sum`), which is why the recipe carries a placeholder until the `v0.1.0` tag exists. Until upstream has published at least one Release, the watch finds nothing and fails the scheduled `sync-upstream` run for every package in the batch, so this recipe stays a draft until then. diff --git a/pkgbuilds/elsewhen/.omarchy/package.json b/pkgbuilds/elsewhen/.omarchy/package.json new file mode 100644 index 0000000..b1e7e71 --- /dev/null +++ b/pkgbuilds/elsewhen/.omarchy/package.json @@ -0,0 +1,11 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "watch": { + "github": "omacom/elsewhen", + "pattern": "v(?P[0-9]+\\.[0-9]+\\.[0-9]+)" + } + } +} diff --git a/pkgbuilds/elsewhen/PKGBUILD b/pkgbuilds/elsewhen/PKGBUILD new file mode 100644 index 0000000..93639b1 --- /dev/null +++ b/pkgbuilds/elsewhen/PKGBUILD @@ -0,0 +1,63 @@ +# Maintainer: Spencer Bull + +pkgname=elsewhen +pkgver=1.0.0 +pkgrel=2 +pkgdesc='World clock plugin for the Omarchy shell' +arch=('any') +url='https://github.com/omacom/elsewhen' +license=('MIT') + +# What the plugin needs to load and run. It also shells out to bash, date +# (coreutils) and timedatectl (systemd) and reads /usr/share/zoneinfo +# (tzdata); those are members of the base group and stay implicit, per Arch +# convention. The citations for each entry are in .omarchy/README.md. +depends=( + 'omarchy' + 'python' + 'quickshell' +) + +options=('!debug') + +source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('3124f0c0a19ebc1b158bcf04151cddd6c733ceeead88052186b6a54c46bee263') + +package() { + # Install alongside the bundled plugins in the shell's plugin directory. + local plugin="$pkgdir/usr/share/omarchy/shell/plugins/omacom.elsewhen" + cd "$srcdir/$pkgname-$pkgver" || return 1 + + # The shell loads the entry point each manifest declares. A tree without + # either would install cleanly and never load, so fail the build instead of + # shipping it. + [[ -f manifest.json ]] || { + echo "release tree is missing manifest.json" >&2 + return 1 + } + grep -Eq '"id"[[:space:]]*:[[:space:]]*"omacom\.elsewhen"' manifest.json || { + echo "manifest.json does not declare the plugin id omacom.elsewhen" >&2 + return 1 + } + grep -Eq '"barWidget"[[:space:]]*:[[:space:]]*"Panel\.qml"' manifest.json || { + echo "manifest.json does not name Panel.qml as the bar widget entry point" >&2 + return 1 + } + [[ -f Panel.qml ]] || { + echo "release tree is missing the entry point Panel.qml" >&2 + return 1 + } + + # An explicit allow-list of runtime files, so tests/, .github/ and the rest + # of the repository never reach the package. Directories end up 0755 and + # every file 0644: worldclock-data.py runs as `python3 ` and needs no + # execute bit. An unmatched glob is left literal and fails install, which + # is the right outcome for a release tree missing its QML or JS. + local file + for file in manifest.json cities.json world.json worldclock-data.py *.qml *.js; do + install -Dm644 "$file" "$plugin/$file" + done + + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/pkgbuilds/flea/.omarchy/upstream.sh b/pkgbuilds/flea/.omarchy/upstream.sh index 3acff3e..15c8853 100755 --- a/pkgbuilds/flea/.omarchy/upstream.sh +++ b/pkgbuilds/flea/.omarchy/upstream.sh @@ -85,6 +85,14 @@ sharelink_qml=$(tar -xOzf "$tarball" "$expected_root/ui/ShareLink.qml") copyfile_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/copyfile.rs") regfile_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/regfile.rs") +# Every check below pins a literal line except the O_NOFOLLOW one. That check +# guards a property -- the copy opens its source with O_NOFOLLOW, so a symlink +# swapped in cannot redirect the read -- and pinning the exact call expression +# made it assert the spelling instead. v0.3.0 moved the first argument from +# `src` to `src.at` when directory-relative opens landed, kept O_NOFOLLOW, and +# hardened symlink handling further; the literal still refused it. Match the +# call and the flag together so a rename cannot read as a removed fix, while +# dropping O_NOFOLLOW still fails. if ! grep -Fq 'a.push("--".to_string());' <<<"$archive_rs" || ! grep -Fq 'let input = std::fs::canonicalize(input)' <<<"$archiveops_rs" || ! grep -Fq 'if op != "compress" && op != "extract"' <<<"$run_rs$archivereq_rs" || @@ -92,7 +100,7 @@ if ! grep -Fq 'a.push("--".to_string());' <<<"$archive_rs" || ! grep -Fq 'if !sandbox::available()' <<<"$mediaprobe_rs" || ! grep -Fq 'if !sandbox::available()' <<<"$metareq_rs" || ! grep -Fq 'copyToClipboard.command = ["wl-copy", url]' <<<"$sharelink_qml" || - ! grep -Fq 'regfile::open_if_regular(src, O_NOFOLLOW)' <<<"$copyfile_rs" || + ! grep -Eq 'open_if_regular\(.*O_NOFOLLOW' <<<"$copyfile_rs" || ! grep -Fq '.custom_flags(O_NONBLOCK | extra_flags)' <<<"$regfile_rs"; then printf 'Release %s does not contain every required upstream security fix\n' "$best_tag" >&2 exit 1 diff --git a/pkgbuilds/flea/PKGBUILD b/pkgbuilds/flea/PKGBUILD index a525033..a8cd3fd 100644 --- a/pkgbuilds/flea/PKGBUILD +++ b/pkgbuilds/flea/PKGBUILD @@ -1,8 +1,8 @@ # Maintainer: GM pkgname=flea -pkgver=0.2.1 -pkgrel=3 +pkgver=0.3.1 +pkgrel=2 pkgdesc='Fast, keyboard-first file manager for Omarchy' arch=('x86_64' 'aarch64') url='https://github.com/thisisgm/flea' @@ -15,7 +15,10 @@ depends=( 'glib2' 'glibc' 'gvfs' + 'gvfs-afc' 'gvfs-dnssd' + 'gvfs-gphoto2' + 'gvfs-mtp' 'gvfs-nfs' 'gvfs-smb' 'hicolor-icon-theme' @@ -28,6 +31,7 @@ depends=( 'qt6-multimedia' 'qt6-webengine' 'shared-mime-info' + 'usbmuxd' 'util-linux' 'wl-clipboard' 'xdg-terminal-exec' @@ -48,7 +52,7 @@ options=('!debug') source=( "$url/releases/download/v$pkgver/$pkgname-v$pkgver.tar.gz" ) -sha256sums=('75f9ac0274a09a0d55cf7d9187943983c1b78a9e443465738b3ac8af8f2a77e9') +sha256sums=('b146ac3f5025da987eae623c4392c44ce69a6a7275a8df3ec1a84563920a4dd2') build() { cd "$pkgname-$pkgver" @@ -60,7 +64,13 @@ build() { check() { cd "$pkgname-$pkgver" - export CARGO_TARGET_DIR=target + # Upstream's release profile uses fat LTO, whose final link runs on one + # thread. build() keeps it for the shipped binary; the test harness is a + # throwaway second compile, so build it in parallel in its own target + # directory. + export CARGO_TARGET_DIR=target-check + export CARGO_PROFILE_RELEASE_LTO=thin + export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 local -a test_args=() if ! /usr/bin/prlimit --cpu=30 --as=1073741824 \ @@ -99,6 +109,12 @@ check() { # not provide. Keep executable fixtures in the remaining suites on the normal # temp root (/dev/shm is noexec). Note that /dev/shm does NOT buy finer # timestamps -- see the skip below. + # shelfundo::tests::undo_refuses_to_walk_a_stranger_back deletes a file, + # creates another under the same name, and expects undo to tell them + # apart by (dev, ino, kind). ext4 hands the freed inode number straight + # back to the next create, so on the builder's /tmp the stranger is + # identical and undo walks it back. tmpfs allocates inode numbers from a + # counter and never reuses one, which is what the test assumes. local -a filesystem_tests=( backend::menu_actions::tests:: backend::menudelete::tests:: @@ -106,6 +122,7 @@ check() { backend::trashbrowse::tests:: backend::trashdelete:: backend::trashmanifest::tests:: + shelfundo::tests:: ) # redo_refuses_changed_sources_and_destination_collisions writes a file and # then immediately asks redo to notice the edit. flea decides "changed" from @@ -123,6 +140,19 @@ check() { --skip backend::redo::tests::redo_refuses_changed_sources_and_destination_collisions ) + # These three expect spawning a missing or failing program to be reported + # as such. aarch64 builds run under QEMU user-mode emulation, where + # glibc's posix_spawn cannot see the child's failed execve: the spawn + # "succeeds" with exit 127, and the timing test's child never sleeps. + # Passes natively. + if [[ $CARCH == aarch64 ]]; then + test_args+=( + --skip backend::child::tests::a_child_is_noticed_when_it_exits_rather_than_at_the_next_poll_boundary + --skip backend::child::tests::a_child_that_never_started_is_told_apart_from_one_that_ran_and_failed + --skip backend::menu_registry::tests::unavailable_failed_and_oversized_queries_are_named_errors + ) + fi + local test_tmp test_status=0 suite test_tmp=$(mktemp -d /dev/shm/flea-tests.XXXXXXXX) || return 1 TMPDIR="$test_tmp" cargo test --frozen --release -- \ @@ -158,6 +188,9 @@ package() { install -Dm644 ui/qmldir ui/*.qml -t "$pkgdir/usr/share/flea/ui" install -Dm644 ui/js/*.js -t "$pkgdir/usr/share/flea/ui/js" + # Flea's Shelf setting installs this plugin into the user's plugin directory. + install -Dm644 shelf/manifest.json shelf/README.md shelf/*.qml shelf/*.js \ + -t "$pkgdir/usr/share/flea/shelf" ln -s /usr/share/omarchy/shell/Commons "$pkgdir/usr/share/flea/ui/Commons" ln -s /usr/share/omarchy/shell/Ui "$pkgdir/usr/share/flea/ui/Ui" } diff --git a/pkgbuilds/ghostty/.omarchy/package.json b/pkgbuilds/ghostty/.omarchy/package.json new file mode 100644 index 0000000..f9c4cf1 --- /dev/null +++ b/pkgbuilds/ghostty/.omarchy/package.json @@ -0,0 +1,10 @@ +{ + "source": "local", + "release_ring": "fast", + "upstream": { + "watch": { + "pattern": "v(?P[0-9]+(?:\\.[0-9]+)*)", + "git_tags": "https://github.com/ghostty-org/ghostty.git" + } + } +} diff --git a/pkgbuilds/ghostty/PKGBUILD b/pkgbuilds/ghostty/PKGBUILD new file mode 100644 index 0000000..d0f0ed0 --- /dev/null +++ b/pkgbuilds/ghostty/PKGBUILD @@ -0,0 +1,123 @@ +# Ghostty for x86_64 and aarch64, built from the upstream release source tarball. +# +# Ghostty 1.3.x requires Zig 0.15.2 exactly. Distribution toolchains can move +# ahead, so use the verified upstream toolchain for each architecture only at +# package-build time rather than publishing a second Zig package. + +pkgbase=ghostty +pkgname=(ghostty ghostty-shell-integration ghostty-terminfo ghostty-nautilus) +pkgver=1.3.1 +pkgrel=3 +pkgdesc='Fast, native, feature-rich terminal emulator pushing modern features' +arch=(x86_64 aarch64) +url='https://github.com/ghostty-org/ghostty' +license=(MIT) +depends=( + bzip2 + fontconfig + freetype2 + glib2 + glibc + gtk4 + gtk4-layer-shell + harfbuzz + libadwaita + libpng + oniguruma + pixman + wayland + zlib +) +makedepends=( + blueprint-compiler + curl + gettext + pkgconf +) +_zigver=0.15.2 +_archive="$pkgbase-$pkgver" +source=( + "https://release.files.ghostty.org/$pkgver/$_archive.tar.gz" + 'build-data-llvm.patch' +) +sha256sums=( + '3349d25600ffbda281197a18314f7d18791969cffe9474f0ff16a45a9ebfccdb' + 'd9f5781b748651fa1ff7b919f4a79cd8570118faefe2848f64f02ea4220257ba' +) +source_x86_64=("https://ziglang.org/download/$_zigver/zig-x86_64-linux-$_zigver.tar.xz") +sha256sums_x86_64=('02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239') +source_aarch64=("https://ziglang.org/download/$_zigver/zig-aarch64-linux-$_zigver.tar.xz") +sha256sums_aarch64=('958ed7d1e00d0ea76590d27666efbf7a932281b3d7ba0c6b01b0ff26498f667f') + +prepare() { + cd "$_archive" + # Zig's native x86 linker cannot read .sframe relocations in Arch's crt1.o. + # Use bundled LLVM for the build-data helper, as the main executable does. + if [[ "$CARCH" == x86_64 ]]; then + patch -Np1 -i "$srcdir/build-data-llvm.patch" + fi + PATH="$srcdir/zig-$CARCH-linux-$_zigver:$PATH" \ + ZIG_GLOBAL_CACHE_DIR="$srcdir/zig-global-cache" \ + ./nix/build-support/fetch-zig-cache.sh +} + +build() { + cd "$_archive" + # A '-' suffix is a SemVer prerelease and selects Ghostty's tip channel. + # Keep the package revision as build metadata on the stable release. + PATH="$srcdir/zig-$CARCH-linux-$_zigver:$PATH" \ + DESTDIR=build \ + zig build \ + --prefix /usr \ + --system "$srcdir/zig-global-cache/p" \ + -Doptimize=ReleaseFast \ + -Dgtk-x11=true \ + -Dcpu=baseline \ + -Dpie=true \ + -Demit-docs=false \ + -Dversion-string="$pkgver+omarchy.$pkgrel" \ + --build-id=sha1 +} + +package_ghostty() { + depends+=(ghostty-shell-integration ghostty-terminfo) + optdepends=('ghostty-nautilus: Open in Ghostty context menu in GNOME Files') + + cd "$_archive" + cp -a build/* "$pkgdir/" + install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/ghostty/LICENSE" + rm -r "$pkgdir/usr/share/terminfo" \ + "$pkgdir/usr/share/ghostty/shell-integration" \ + "$pkgdir/usr/share/nautilus-python" +} + +package_ghostty-shell-integration() { + pkgdesc='Shell integration scripts for Ghostty' + depends=() + + cd "$_archive" + install -d "$pkgdir/usr/share/ghostty/shell-integration" + cp -a build/usr/share/ghostty/shell-integration/. \ + "$pkgdir/usr/share/ghostty/shell-integration/" + install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} + +package_ghostty-terminfo() { + pkgdesc='Terminfo for Ghostty' + depends=() + + cd "$_archive" + install -d "$pkgdir/usr/share/terminfo" + cp -a build/usr/share/terminfo/x "$pkgdir/usr/share/terminfo/" + install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} + +package_ghostty-nautilus() { + pkgdesc='Open in Ghostty for GNOME Files' + depends=(ghostty nautilus-python) + license=(GPL-2.0-or-later) + + cd "$_archive" + install -d "$pkgdir/usr/share/nautilus-python" + cp -a build/usr/share/nautilus-python/. "$pkgdir/usr/share/nautilus-python/" +} diff --git a/pkgbuilds/ghostty/build-data-llvm.patch b/pkgbuilds/ghostty/build-data-llvm.patch new file mode 100644 index 0000000..629f3d6 --- /dev/null +++ b/pkgbuilds/ghostty/build-data-llvm.patch @@ -0,0 +1,10 @@ +--- a/src/build/GhosttyResources.zig ++++ b/src/build/GhosttyResources.zig +@@ -15,6 +15,7 @@ + // This is the exe used to generate some build data. + const build_data_exe = b.addExecutable(.{ + .name = "ghostty-build-data", ++ .use_llvm = true, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_build_data.zig"), + .target = b.graph.host, diff --git a/pkgbuilds/github-copilot-cli/PKGBUILD b/pkgbuilds/github-copilot-cli/PKGBUILD index 917dca5..71f4f52 100644 --- a/pkgbuilds/github-copilot-cli/PKGBUILD +++ b/pkgbuilds/github-copilot-cli/PKGBUILD @@ -6,7 +6,7 @@ _npmmodule=@github/copilot pkgname=github-copilot-cli _pkgexec=copilot -pkgver=1.0.83 +pkgver=1.0.86 pkgrel=1 pkgdesc="GitHub Copilot CLI brings the power of Copilot coding agent directly to your terminal." @@ -31,8 +31,8 @@ source=("https://registry.npmjs.org/${_npmmodule}/-/copilot-${pkgver}.tgz" noextract=("copilot-${pkgver}.tgz") sha256sums=( - '135506fc2b13163ab55dbf76a06e2fbcbad04ecac76b4e9c6659f0ba309e6a86' - '0c0064a10effac8adf9ad97338bafaa0d7d7d5bf191cc1c0384e05ff4366d36c' + '4c6433345f08199e96dcf8db1c3e46a337dfab96cea32132d6127ffcd63a6200' + 'b94d2aab574cf3e0c8d950e72430186cdd918261a1376146de971fa90ba0a672' ) # Document: https://wiki.archlinux.org/title/Node.js_package_guidelines diff --git a/pkgbuilds/gliff/.omarchy/package.json b/pkgbuilds/gliff/.omarchy/package.json new file mode 100644 index 0000000..2a9719d --- /dev/null +++ b/pkgbuilds/gliff/.omarchy/package.json @@ -0,0 +1,3 @@ +{ + "source": "local" +} diff --git a/pkgbuilds/gliff/PKGBUILD b/pkgbuilds/gliff/PKGBUILD new file mode 100644 index 0000000..c9d957b --- /dev/null +++ b/pkgbuilds/gliff/PKGBUILD @@ -0,0 +1,53 @@ +# Maintainer: David Heinemeier Hansson + +pkgname=gliff +pkgver=0.1.0 +pkgrel=1 +pkgdesc="Hyprland remote desktop over SSH (Vulkan Video, 4:4:4)" +arch=('x86_64') +url="https://github.com/kevinmcconnell/gliff" +license=('MIT') +depends=('gcc-libs' 'glibc' 'wayland' 'libxkbcommon' 'libdrm' 'mesa' + 'gtk4>=4.14' 'libadwaita>=1.5' 'vulkan-icd-loader' 'openssh') +makedepends=('cargo' 'pkgconf') +optdepends=('vulkan-radeon: Vulkan Video on AMD' + 'vulkan-intel: Vulkan Video on Intel' + 'vulkan-tools: vulkaninfo for debugging' + 'vulkan-validation-layers: driver call validation for development') +options=('!debug') + +# Upstream only publishes a rolling prerelease; pin its source for reproducible builds. +_commit=2edbfba52780c7ace15dbb05ca92177f493d4bc9 +source=("$pkgname-$_commit.tar.gz::$url/archive/$_commit.tar.gz") +sha256sums=('2d75961085a72fb8e34149de63e312a7336daa70fd6e157e30c2cd08c14eb1c7') + +prepare() { + cd "$pkgname-$_commit" + + cargo fetch --locked --target "$CARCH-unknown-linux-gnu" +} + +build() { + cd "$pkgname-$_commit" + + export CARGO_TARGET_DIR=target + cargo build --frozen --release +} + +check() { + cd "$pkgname-$_commit" + + export CARGO_TARGET_DIR=target + cargo test --frozen --release --workspace +} + +package() { + cd "$pkgname-$_commit" + + install -Dm755 target/release/gliff "$pkgdir/usr/bin/gliff" + install -Dm755 target/release/gliff-server "$pkgdir/usr/bin/gliff-server" + install -Dm755 target/release/gliff-probe "$pkgdir/usr/bin/gliff-probe" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" + install -Dm644 docs/hardware-quirks.md "$pkgdir/usr/share/doc/$pkgname/hardware-quirks.md" +} diff --git a/pkgbuilds/grok-bot/PKGBUILD b/pkgbuilds/grok-bot/PKGBUILD index 1109333..bcc5797 100644 --- a/pkgbuilds/grok-bot/PKGBUILD +++ b/pkgbuilds/grok-bot/PKGBUILD @@ -2,14 +2,15 @@ # Contributor: Omarchy pkgname=grok-bot -pkgver=0.29.0 +pkgver=0.47.0 pkgrel=1 -_commit=f0e5bfcee649ea84c0c61369cf896cd146d72136 +_commit=c1e7d7a46549956d25f53e9c0b9f59666e03aa3a pkgdesc='Grok Bot desktop agent' -arch=('x86_64') +arch=('x86_64' 'aarch64') url='https://x.ai/bot' license=('custom') depends=( + 'alsa-lib' 'at-spi2-core' 'gtk3' 'hicolor-icon-theme' @@ -25,32 +26,36 @@ optdepends=('libappindicator-gtk3: tray support') provides=('sand') conflicts=('sand') options=('!strip' '!debug') +install=grok-bot.install + +_deb_x86_64="grok-bot_${pkgver}_amd64.deb" +_deb_aarch64="grok-bot_${pkgver}_arm64.deb" source=( - "${pkgname}_${pkgver}.deb::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/x64/Grok_Bot_${pkgver}.deb" 'grok-bot.sh' 'grok-bot.desktop' ) -sha256sums=('d223b5830282aef11d5c46d8f4d1edd239bf992e336405cbd288d4476b9233d4' - '6dfa6c305941afa6cbaefbeaae06d05ab5a88f31630005d25a819a160c20c7a3' - '856056c9ca63dda5d01158ce8fb6a9a7cbb3f67c13a92b573cd196d3e50f26e7') -noextract=("${pkgname}_${pkgver}.deb") +source_x86_64=( + "${_deb_x86_64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/x64/${_deb_x86_64}" +) +source_aarch64=( + "${_deb_aarch64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/arm64/${_deb_aarch64}" +) +sha256sums=('6dfa6c305941afa6cbaefbeaae06d05ab5a88f31630005d25a819a160c20c7a3' + '3e2a2461ea58d17ac1777616be9ba660f7cb9ceefa9292016e36c55758bf78dd') +sha256sums_x86_64=('11ca0f51a535b97af51a352adf9c0f9ecd2e1b0430a69ae9451b688a7a065808') +sha256sums_aarch64=('836f8d19d3826c6573c31ac45c7a9b797abc73381ae0d2b1e7a8dae5410e7e46') +noextract=("${_deb_x86_64}" "${_deb_aarch64}") package() { - bsdtar -xOf "${srcdir}/${pkgname}_${pkgver}.deb" data.tar.xz | + local deb_var="_deb_${CARCH}" + local deb="${!deb_var}" + + bsdtar -xOf "${srcdir}/${deb}" data.tar.xz | bsdtar -x -C "${pkgdir}" -f - rm -rf "${pkgdir}/usr/share/doc" \ - "${pkgdir}/usr/share/applications/sand.desktop" - - local icon1024="${pkgdir}/usr/share/icons/hicolor/1024x1024/apps" - if [[ -f "${icon1024}/sand.png" && ! -f "${icon1024}/grok-bot.png" ]]; then - install -Dm644 "${icon1024}/sand.png" "${icon1024}/grok-bot.png" - fi - rm -f "${icon1024}/sand.png" - if [[ -f "${icon1024}/grok-bot.png" ]]; then - install -Dm644 "${icon1024}/grok-bot.png" \ - "${pkgdir}/usr/share/icons/hicolor/512x512/apps/grok-bot.png" - fi + "${pkgdir}/usr/share/applications/sand.desktop" \ + "${pkgdir}/usr/share/applications/grok-bot.desktop" # Always install our Wayland wrapper; do not keep any /usr/bin from the .deb. rm -f "${pkgdir}/usr/bin/grok-bot" "${pkgdir}/usr/bin/sand" @@ -64,9 +69,10 @@ package() { install -Dm644 "${pkgdir}/opt/Grok Bot/LICENSES.chromium.html" \ "${pkgdir}/usr/share/licenses/${pkgname}/LICENSES.chromium.html" - if ! { [[ -L /proc/self/ns/user ]] && unshare --user true; }; then - chmod 4755 "${pkgdir}/opt/Grok Bot/chrome-sandbox" - else - chmod 0755 "${pkgdir}/opt/Grok Bot/chrome-sandbox" - fi + # Ship chrome-sandbox without setuid. Upstream's build-time userns probe + # would measure the CI container, not the user's machine, and a setuid + # helper could not exec from "/opt/Grok Bot/" anyway (electron#44414). + # grok-bot.install tells users on kernels without unprivileged user + # namespaces how to run without the sandbox. + chmod 0755 "${pkgdir}/opt/Grok Bot/chrome-sandbox" } diff --git a/pkgbuilds/grok-bot/grok-bot.desktop b/pkgbuilds/grok-bot/grok-bot.desktop index 992c36c..ddda47a 100644 --- a/pkgbuilds/grok-bot/grok-bot.desktop +++ b/pkgbuilds/grok-bot/grok-bot.desktop @@ -8,6 +8,6 @@ Terminal=false Type=Application Categories=Development; MimeType=x-scheme-handler/grokbot;x-scheme-handler/sand; -StartupWMClass=Grok Bot +StartupWMClass=grok-bot StartupNotify=true Keywords=Grok;AI;Agent; diff --git a/pkgbuilds/grok-bot/grok-bot.install b/pkgbuilds/grok-bot/grok-bot.install new file mode 100644 index 0000000..11a7683 --- /dev/null +++ b/pkgbuilds/grok-bot/grok-bot.install @@ -0,0 +1,41 @@ +# Electron's renderer sandbox needs unprivileged user namespaces, or else a +# setuid-root chrome-sandbox. Upstream omarchy-pkgs probes for user namespaces +# inside package() and sets 4755 when they are missing. That is wrong twice +# for this repo: the build runs in a CI container where the probe fails, so +# every user would get the setuid helper; and the helper lives under +# "/opt/Grok Bot/", and Electron cannot exec a setuid chrome-sandbox from a +# path with a space (electron/electron#44414), so 4755 would not even work. +# +# The package therefore always ships chrome-sandbox as 0755. This hook only +# tells the user what to do on a host that lacks unprivileged user namespaces. +# The probe drops to nobody first: pacman runs hooks as root, and root can +# unshare a user namespace even where unprivileged users cannot. +_userns_available() { + [[ -L /proc/self/ns/user ]] || return 1 + if (( EUID == 0 )) && command -v setpriv >/dev/null; then + setpriv --reuid=65534 --regid=65534 --clear-groups -- unshare --user true 2>/dev/null + else + # Already unprivileged (or no setpriv): the direct probe is the real answer. + unshare --user true 2>/dev/null + fi +} + +_advise() { + _userns_available && return 0 + cat <<'MSG' +==> Unprivileged user namespaces are unavailable on this kernel, so Grok Bot's + renderer sandbox cannot start. A setuid chrome-sandbox is not an option + here: Electron cannot exec it from "/opt/Grok Bot/" (electron#44414). + To run without the sandbox, add this line to ~/.config/grok-bot-flags.conf: + + --no-sandbox +MSG +} + +post_install() { + _advise +} + +post_upgrade() { + _advise +} diff --git a/pkgbuilds/herdr/PKGBUILD b/pkgbuilds/herdr/PKGBUILD index 02cc4d3..94e5e31 100644 --- a/pkgbuilds/herdr/PKGBUILD +++ b/pkgbuilds/herdr/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: David Heinemeier Hansson pkgname=herdr -pkgver=0.9.0 +pkgver=0.9.1 pkgrel=1 pkgdesc="Herdr terminal workspace manager for AI coding agents" arch=('x86_64' 'aarch64') @@ -13,13 +13,13 @@ replaces=('omarchy-herdr') conflicts=('omarchy-herdr') options=('!debug' '!lto') -_zigver=0.15.2 +_zigver=0.16.0 source=("herdr-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") source_x86_64=("zig-x86_64-linux-$_zigver.tar.xz::https://ziglang.org/download/$_zigver/zig-x86_64-linux-$_zigver.tar.xz") source_aarch64=("zig-aarch64-linux-$_zigver.tar.xz::https://ziglang.org/download/$_zigver/zig-aarch64-linux-$_zigver.tar.xz") -sha256sums=('1e83bff4b05834ed8281e16f1680e8f3e58375a94b2e3f2b3d021e28e293ef9a') -sha256sums_x86_64=('02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239') -sha256sums_aarch64=('958ed7d1e00d0ea76590d27666efbf7a932281b3d7ba0c6b01b0ff26498f667f') +sha256sums=('03403d3ef80dcf2b954dd5d27eb636e6c4f5279d240b48de272b7f53e4b73093') +sha256sums_x86_64=('70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00') +sha256sums_aarch64=('ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17') prepare() { cd "herdr-$pkgver" diff --git a/pkgbuilds/heroic-games-launcher-bin/PKGBUILD b/pkgbuilds/heroic-games-launcher-bin/PKGBUILD index 2bc7604..6c18559 100755 --- a/pkgbuilds/heroic-games-launcher-bin/PKGBUILD +++ b/pkgbuilds/heroic-games-launcher-bin/PKGBUILD @@ -2,8 +2,8 @@ # Maintainer: CommandMC pkgname=heroic-games-launcher-bin -pkgver=2.22.1 -pkgrel=2 +pkgver=2.22.3 +pkgrel=1 pkgdesc="An Open source Launcher for Epic, Amazon and GOG Games" arch=('x86_64') url="https://heroicgameslauncher.com/" @@ -11,7 +11,7 @@ license=('GPL-3.0-only') _filename=Heroic-${pkgver}-linux-x64.pacman source=("https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/download/v${pkgver}/${_filename}") noextract=("${_filename}") -sha256sums=(66ed041a93ac2817b744d3d0985194adfa408c8d35f64d9a8967fa5e2a58f2c1) +sha256sums=('ed17ce083a71dd7e49a89218052ab475edd5a654cedf443853f6baacbb0a00e9') options=(!strip) depends=( which diff --git a/pkgbuilds/hype/.omarchy/package.json b/pkgbuilds/hype/.omarchy/package.json new file mode 100644 index 0000000..2a9719d --- /dev/null +++ b/pkgbuilds/hype/.omarchy/package.json @@ -0,0 +1,3 @@ +{ + "source": "local" +} diff --git a/pkgbuilds/hype/PKGBUILD b/pkgbuilds/hype/PKGBUILD new file mode 100644 index 0000000..af2508f --- /dev/null +++ b/pkgbuilds/hype/PKGBUILD @@ -0,0 +1,42 @@ +# Maintainer: David Heinemeier Hansson + +pkgname=hype +pkgver=0.4.1 +pkgrel=1 +pkgdesc='Simple Markdown presentations with a visual slide editor' +arch=('x86_64' 'aarch64') +url='https://github.com/omacom/hype' +license=('MIT') +install='hype.install' +options=('!debug') +depends=( + 'ffmpeg' + 'hicolor-icon-theme' + 'qt6-base>=6.9' + 'qt6-declarative>=6.9' + 'qt6-multimedia>=6.9' + 'qt6-imageformats' + 'qt6-svg' + 'source-highlight' + 'zlib' + 'libwebp' + 'xdg-desktop-portal' +) +makedepends=('gcc' 'make') +source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('0724a9d18df7657b50dff21ffe3dec1c107e9191a9e9fd5ad40b56ed9d5d6f51') + +build() { + cd "$srcdir/$pkgname-$pkgver" + ./bin/build +} + +package() { + cd "$srcdir/$pkgname-$pkgver" + + install -Dm755 build/hype "$pkgdir/usr/bin/hype" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 pkgbuild/hype.svg "$pkgdir/usr/share/icons/hicolor/scalable/apps/hype.svg" + install -Dm644 pkgbuild/hype.desktop "$pkgdir/usr/share/applications/hype.desktop" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/pkgbuilds/hype/hype.install b/pkgbuilds/hype/hype.install new file mode 100644 index 0000000..5357602 --- /dev/null +++ b/pkgbuilds/hype/hype.install @@ -0,0 +1,12 @@ +post_install() { + command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q + command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -t -f usr/share/icons/hicolor +} + +post_upgrade() { + post_install +} + +post_remove() { + post_install +} diff --git a/pkgbuilds/hyprland-preview-share-picker/PKGBUILD b/pkgbuilds/hyprland-preview-share-picker/PKGBUILD index b3d80be..0862780 100644 --- a/pkgbuilds/hyprland-preview-share-picker/PKGBUILD +++ b/pkgbuilds/hyprland-preview-share-picker/PKGBUILD @@ -2,7 +2,7 @@ pkgname="hyprland-preview-share-picker" pkgver=0.2.1 -pkgrel=1 +pkgrel=2 pkgdesc="An alternative share picker for hyprland with window and monitor previews" arch=(x86_64 aarch64) url="https://github.com/WhySoBad/hyprland-preview-share-picker" @@ -32,14 +32,14 @@ fn main() { } EOF - export RUSTUP_TOOLCHAIN=nightly + export RUSTUP_TOOLCHAIN=stable cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')" } build() { cd "$pkgname-$pkgver" - export RUSTUP_TOOLCHAIN=nightly + export RUSTUP_TOOLCHAIN=stable export CARGO_TARGET_DIR=target cargo build --frozen --release diff --git a/pkgbuilds/intel-ipu7-camera/PKGBUILD b/pkgbuilds/intel-ipu7-camera/PKGBUILD index c2770fd..6683be9 100644 --- a/pkgbuilds/intel-ipu7-camera/PKGBUILD +++ b/pkgbuilds/intel-ipu7-camera/PKGBUILD @@ -2,14 +2,13 @@ pkgname=intel-ipu7-camera pkgver=1.0.6 -pkgrel=1 +pkgrel=2 pkgdesc="Intel IPU7 MIPI camera stack for Hurrican/Performance (OV08X40 + hardware ISP)" arch=('x86_64') url="https://github.com/TsaiGaggery/hurrican_omarchy_enabling" license=('GPL-2.0-or-later') depends=( 'dkms' - 'linux-headers' 'v4l2loopback-dkms' 'v4l2-relayd' 'gstreamer' diff --git a/pkgbuilds/learn-omarchy/.omarchy/package.json b/pkgbuilds/learn-omarchy/.omarchy/package.json new file mode 100644 index 0000000..7fed645 --- /dev/null +++ b/pkgbuilds/learn-omarchy/.omarchy/package.json @@ -0,0 +1,12 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "github": "DanWahlin/learn-omarchy", + "checksums": "SHA256SUMS", + "assets": { + "any": "learn-omarchy-{pkgver}.tar.gz" + } + } +} diff --git a/pkgbuilds/learn-omarchy/PKGBUILD b/pkgbuilds/learn-omarchy/PKGBUILD new file mode 100644 index 0000000..90d4071 --- /dev/null +++ b/pkgbuilds/learn-omarchy/PKGBUILD @@ -0,0 +1,34 @@ +# Maintainer: Dan Wahlin + +pkgname=learn-omarchy +pkgver=0.2.2 +pkgrel=1 +pkgdesc="Interactive, theme-aware courses for learning Omarchy" +arch=('any') +url="https://github.com/DanWahlin/learn-omarchy" +license=('MIT' 'CC-BY-4.0' 'CC0-1.0') +depends=( + 'bash' 'coreutils' 'sudo' 'hyprland' 'mpv' 'nodejs>=22.6' 'omarchy' + 'quickshell>=0.3' 'qt6-declarative' 'qt6-multimedia' 'qt6-multimedia-ffmpeg' 'xdg-utils' + 'xdg-terminal-exec' 'nautilus' + 'ttf-liberation' 'noto-fonts-emoji' + 'grim' 'slurp' 'gpu-screen-recorder' 'util-linux' 'ffmpeg' +) +makedepends=('make') +optdepends=( + 'btop: activity-monitor practice' + 'tesseract: OCR practice' + 'tesseract-data-eng: English OCR sample recognition' + 'zbar: QR recognition (zbarimg)' + 'qrencode: QR sample creation' + 'voxtype: optional dictation practice' +) +options=('!strip') +source=("$pkgname-$pkgver.tar.gz::$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") +sha256sums=('67f14778c2b66d56c1504695b0e11cab603f1a002570abc94b4bae9b0bec6066') + +package() { + cd "$srcdir/$pkgname-$pkgver" + node tools/prepare-release.mjs --check . + make DESTDIR="$pkgdir" PREFIX=/usr install +} diff --git a/pkgbuilds/limine-snapper-sync/PKGBUILD b/pkgbuilds/limine-snapper-sync/PKGBUILD index 97d9add..51fd558 100644 --- a/pkgbuilds/limine-snapper-sync/PKGBUILD +++ b/pkgbuilds/limine-snapper-sync/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Zesko pkgname="limine-snapper-sync" -pkgver=1.31.0 -pkgrel=1.1 +pkgver=1.32.0 +pkgrel=1 _gradle_version=9.7.1 pkgdesc="Integrates Limine boot entries with Snapper snapshots." arch=('x86_64' 'aarch64') @@ -31,7 +31,7 @@ makedepends=('git') makedepends_x86_64=('gradle') backup=(etc/limine-snapper-sync.conf) conflicts=('limine-snapper-cli' 'limine-snapper-sync-git') -sha256sums=('ed236f1bbab966950bf11ba5a7958e97a76e66db7fd647b7737bab4b48c9fc40') +sha256sums=('6bcc1d3ace58030204260a9a4d40d500c4822416be1b1c6edd153d1df0796b3d') sha256sums_x86_64=('e0be791c8fda4d03b6b0a0cb824fef3149736170057b3a515252b44419606af0') sha256sums_aarch64=('b4580d9f223d0a4b3a1757e58b18ff4c1db950e67e105fc5cb741457d2384a71' 'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a') diff --git a/pkgbuilds/linux-firmware-cirrus/.omarchy/package.json b/pkgbuilds/linux-firmware-cirrus/.omarchy/package.json new file mode 100644 index 0000000..ba6af6c --- /dev/null +++ b/pkgbuilds/linux-firmware-cirrus/.omarchy/package.json @@ -0,0 +1,5 @@ +{ + "source": "local", + "release_ring": "fast", + "sync": false +} diff --git a/pkgbuilds/linux-firmware-cirrus/PKGBUILD b/pkgbuilds/linux-firmware-cirrus/PKGBUILD new file mode 100644 index 0000000..0c46f9b --- /dev/null +++ b/pkgbuilds/linux-firmware-cirrus/PKGBUILD @@ -0,0 +1,63 @@ +# Maintainer: Spencer Bull +# +# Self-retiring shim: ships Arch's linux-firmware-cirrus 20260910-2 payload to +# the stable channel while stable's pinned Arch snapshot is still on +# linux-firmware 20260810-2 (which lacks the Dell XPS 13 DX13260 / 1028:0e54 +# CS35L56 amplifier firmware aliases, leaving that machine's speakers silent). +# +# Nothing is rebuilt. The signed Arch package is verified against the Arch +# packager key and its payload reinstalled as-is, minus the files that Arch +# moved out of linux-firmware-other in 20260910 (the cs42l45 SDCA tree): on +# the stable snapshot those are still owned by linux-firmware-other 20260810-2 +# and would conflict, so they are left to that package. +# +# Versioning is deliberate: 20260810-3 orders above the snapshot's 20260810-2 +# and BELOW Arch's real 20260910-2, so as soon as the stable snapshot advances +# pacman replaces this shim with the genuine package in the same transaction +# that upgrades linux-firmware-other, and nothing is lost. Delete this recipe +# once stable's snapshot is at linux-firmware >= 20260910. + +pkgname=linux-firmware-cirrus +pkgver=20260810 +pkgrel=3 +_fwver=20260910 +_fwrel=2 +_basever=20260810 +_baserel=2 +pkgdesc="Firmware files for Linux - Firmware for Cirrus Logic audio devices (Arch - payload, stable-snapshot shim)" +arch=('any') +url="https://gitlab.com/kernel-firmware/linux-firmware" +license=('LicenseRef-WHENCE' 'LicenseRef-cirrus') +depends=('linux-firmware-whence') +options=('!strip' '!debug') +_cirrus="linux-firmware-cirrus-${_fwver}-${_fwrel}-any.pkg.tar.zst" +_other="linux-firmware-other-${_basever}-${_baserel}-any.pkg.tar.zst" +source=( + "https://archive.archlinux.org/packages/l/linux-firmware-cirrus/${_cirrus}" + "https://archive.archlinux.org/packages/l/linux-firmware-cirrus/${_cirrus}.sig" + "https://archive.archlinux.org/packages/l/linux-firmware-other/${_other}" + "https://archive.archlinux.org/packages/l/linux-firmware-other/${_other}.sig" +) +noextract=("${_cirrus}" "${_other}") +sha256sums=('70100c551b079bd8abec3d9c96a16defd04766b2a4afc6d7be9128d37e9840f7' + 'SKIP' + 'b0f016ee0d0532b977211b0cbb630bca75184f68a9ace82675eb3cf9acce4f6c' + 'SKIP') +# Jan Alexander Steffens (heftig) , Arch Linux packager +validpgpkeys=('83BC8889351B5DEBBB68416EB8AC08600F108CDF') + +package() { + # Payload only; makepkg regenerates .PKGINFO/.MTREE/.BUILDINFO. + bsdtar -xf "${srcdir}/${_cirrus}" -C "${pkgdir}" \ + --exclude='.PKGINFO' --exclude='.MTREE' --exclude='.BUILDINFO' \ + --exclude='.INSTALL' --exclude='.CHANGELOG' + + # Drop every file the snapshot's linux-firmware-other still owns. + local f + while IFS= read -r f; do + [[ -e "${pkgdir}/${f}" || -L "${pkgdir}/${f}" ]] && rm -f "${pkgdir}/${f}" + done < <(bsdtar -tf "${srcdir}/${_other}" | grep -v '^\.' | grep -v '/$') + + # Remove directories emptied by the step above. + find "${pkgdir}/usr/lib/firmware" -depth -type d -empty -delete +} diff --git a/pkgbuilds/linux-firmware-cirrus/keys/pgp/83BC8889351B5DEBBB68416EB8AC08600F108CDF.asc b/pkgbuilds/linux-firmware-cirrus/keys/pgp/83BC8889351B5DEBBB68416EB8AC08600F108CDF.asc new file mode 100644 index 0000000..e6ae5d8 --- /dev/null +++ b/pkgbuilds/linux-firmware-cirrus/keys/pgp/83BC8889351B5DEBBB68416EB8AC08600F108CDF.asc @@ -0,0 +1,60 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZXeS9hYJKwYBBAHaRw8BAQdAT04Na8ee0UltkhyNi2RGHYdjZDgg+X/K8Jix +dSSQ+Ni0NkphbiBBbGV4YW5kZXIgU3RlZmZlbnMgKGhlZnRpZykgPGhlZnRpZ0Bh +cmNobGludXgub3JnPoiQBBMWCgA4AhsDAheAFiEEg7yIiTUbXeu7aEFuuKwIYA8Q +jN8FAmWq/kcFCwkIBwMFFQoJCAsFFgIDAQACHgUACgkQuKwIYA8QjN+2qAEAh/RL +Zq7Hmqv/z09yq0m6IEb0kbXaW50POi/V+2VcJ9wBAN5Ik/fFgnGMlvZF7Rugu37o +2fk7jnUVsWJca9QmytgGiHUEEBYKAB0WIQSZtmGEcqOzuBQYW67X09gjuIvbmwUC +aYWy0QAKCRDX09gjuIvbm7vJAP9xqmmI8jNGEUQNidh9yZCmVOr3vT5/sUcHGo0J +08o/TgD/Wix2/4DHfnk6fu+onERrK5uF9BAacERnCsG5lMWQ2wSJAjMEEAEKAB0W +IQSR/+BwDoBhnOtzI1yojiPjd1FOAAUCZa0cywAKCRCojiPjd1FOAIKbEACipPSS +sA2G7ntHRGEHSWKgqEKseGGr7kflVdsmJn1tjKI8/VGKa77NkCRgqIJRwmE9m9+F +Cx+a3ILmMOxsjj5RCVFce9NJuscVGHaphZcp7Z0MVoR9hhtnsyjWk/CYOAv+JLKq +NTYhdmat+ixY4fwPzjdV6sxCDL/s6Fci0zPLUam7QVr9LIS4U3UH7smkKj6jM+G/ +sEn0QM02EKy2iJLlbPNN3pppYWqZ5p9xXeB6EJ89JvZMI3k92xy456JjkCgLN860 +NPmdmnLLtlrGWJRgNK6+iaFjLt/BL9gU8aGe+K3ONXES2k7iZnh+oLAEV8Z07dgv +Gd0o+OFEPrC8kETbQK45r1yAjZVO+tp4dSxvV/BD+7KhUXAZnOKWLaOMomfsG17E +KKa894cBLJG7LWN/d/Uk9fjfjd4ZYbSpIALJiEoKrm1ytj2KgnxKveTtY69i63/N +0BpNVrkYIJffWR6zSosC8geAWfqm7pR6JARUAZrw0YtJbluDrRgOO0XFn8hDqRUm +FPWgXuvqNdR2JyD4aB87LKGhzVFzQvbHa+S4sG7+w5nWQB0Eca+hiVpJDpMYjoU+ +f+L3yUb6POwvUJe7VT/2PYOwidnV3guTevnn9a4erKoW/6wKAr13cW+KTKTR0bdC +S0UTOWbJnbi2Hfhyr7NZgs4T4OcpH3kj+R4WPIkCMwQQAQoAHRYhBNiv3aB6W27f +p9jM2tbQVfknhD8cBQJlg4C5AAoJENbQVfknhD8cpPQP/19uAVMaG1lQNaAK9XWT +Z1/lAr6sxYT6B2+MqnRtbkr4Gh3ts7Ey5BI39HxNWRIUGUt7pq4FjCQxWeJC9mfp +/hqj9rl1s32lbBzevbkjESro+cHPfrv0vsVlwJA2W3rURQlQzbEq3fvDzc/wz4sN +vgQDiUlxH5JYXZ5OexsDATLyfHNpJh+4NxqB24axjDe/1ZK8hoYl+eX46dP9JKWB +F8GA3Ebwst3de/81oNngBjDgNPju6cvgykoT2jCBn4asp1xKekZh5ZbRG40jAeoQ +QAvEgTxIbwNf4HBVhvGjQ6G5lCNO6gQonq1X4UbHgH8tRGSSgnz2yDAdcxtaj1eC +e5BMhjuoHuxS4DCtR97Dtn7YjrYoVus5eMkhEZGuWKK4hkggsLbyUx5llxZxLXPD +mBAYLkqgZsHgiqLeHAdrEmAFaRfskVg0MzfCcEE9StWnWf0ixk3thuwQAaPI4GCB +nNLnmqCcOyCN1Qa3iXjdOzp5bU0qwFi/qFQagq0nu8sJim7q0g0Bk7J+iGRht3no +L5iT9d+8CQaN1it+L+elSYfrr/LamghRvh9epOtGUVH3GQYS4Bgo2rgktGXKtcX+ +P/jR/aM034g8VPlbV+Kas/c6mIk9JzKOvJJ4+Lpvr0ZzRG32ez9dAlDogE3xASi6 +sMWbY+t4unGs+nY3SqIOyHLTiHUEEBYKAB0WIQRp5kceOuBlKXUpgy5roPWiA39P +QQUCZX7ZYAAKCRBroPWiA39PQadvAQD7F/N3xuyWogrJV7TMZk2PFteviEW2Dv9d +dSUGasK3dQEAxf3HxRDvrv3yJLhgNKa+ksr4bBBmruvilpWS+X4InwKIdQQQFgoA +HRYhBDVy+iobBn8ixYrxVfi4IbQqb9zXBQJlfKbrAAoJEPi4IbQqb9zXe7MBAIzF +QqdV8CJXYIcZJtUUIQ7a/AN2enHBpoa/qXEre5bAAP4uNEUMKiDZRpHAh/KmqarM +vF+c1BOpEJbQsPqv0L5hDIh1BBAWCgAdFiEEKsCkLvsLXLx6BALtTclbbXvpiS4F +AmV8nsgACgkQTclbbXvpiS6bpwD/W0sMOH4lmR4t9Sc8hJB+uBLGYxzoNIgaNa5x +vbdm5hwBAIPYr1SVl0+yghsxg5k75jStRL2S5MZW2iSV3ynNLTkFiHkEEBYKACEW +IQSi/zo2qqVmVBCQZKsZgC+LDXD8MAUCZXe6yAMFAngACgkQGYAviw1w/DCmCAD8 +Cfvn8O+N/AJTOKY8lZzk+OSX3tSTQTOUiLHKRl+RX6IA/1Bku44c1YJVZ+RhWkGQ +n0C8ZN/DYzHh9JInl3blLcIMtDhKYW4gQWxleGFuZGVyIFN0ZWZmZW5zIChoZWZ0 +aWcpIDxqYW4uc3RlZmZlbnNAZ21haWwuY29tPoiTBBMWCgA7AhsDAheAAhkBFiEE +g7yIiTUbXeu7aEFuuKwIYA8QjN8FAmWq/kIFCwkIBwMFFQoJCAsFFgIDAQACHgUA +CgkQuKwIYA8QjN+ySAD+PI99JJsFWz2CaS3enxjUMCWJZJvSV9G1FqmeTKtH95oA +/ismVRjBbwbCrDDEsZVIK3NeRyRyhiWIVFXWix/KnH4CiHkEEBYKACEWIQSi/zo2 +qqVmVBCQZKsZgC+LDXD8MAUCZXe6xQMFAngACgkQGYAviw1w/DDnzAD6AwROKYI8 +7DZ6a1onZeR5wOV50bt2LCB4XxNiupHcpLMA/i4dmwa4Bkzyh/h+v0kN2PSssueX +7kFPNcnyhe3KbUUDuDMEZXeUSxYJKwYBBAHaRw8BAQdAkvIbYwde3OFqoAy6QOO9 +BPwFNCll8tgQ6iAmQMkOjtWIeAQYFgoAIBYhBIO8iIk1G13ru2hBbrisCGAPEIzf +BQJld5RLAhsgAAoJELisCGAPEIzfhU8A/3NZzIEk3dmCAL0XLtylcFp/HExnN+5Q +RGmT0+SzzuGaAP9ozoMlSjtcGLAZMglLk8/mYzKveR89RJlB0cZtUU6UArg4BGV3 +kvYSCisGAQQBl1UBBQEBB0Dh/7CubQh/MabODq3IcoqeGUzEGUPU8GXCVrDmPHih +WQMBCAeIeAQYFgoAIBYhBIO8iIk1G13ru2hBbrisCGAPEIzfBQJld5L2AhsMAAoJ +ELisCGAPEIzfao0A/AiJPB4igiyHjPgR8OpKh4Nz+pwmFrD/j5l2YC0Xi2dOAP4j +LDEa1VCNNhq7vWA8SqUjareBzHpwlG2ObUwYxORjBQ== +=OXp6 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig new file mode 100644 index 0000000..a7eaa24 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch b/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch new file mode 100644 index 0000000..9506ee1 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch @@ -0,0 +1,31 @@ +diff --git a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -935,6 +935,9 @@ KBUILD_RUSTFLAGS += -Copt-level=2 + else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE + KBUILD_CFLAGS += -Os + KBUILD_RUSTFLAGS += -Copt-level=s ++else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3 ++KBUILD_CFLAGS += -O3 ++KBUILD_RUSTFLAGS += -Copt-level=3 + endif + + # Always set `debug-assertions` and `overflow-checks` because their default +diff --git a/init/Kconfig b/init/Kconfig +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1622,6 +1622,14 @@ config CC_OPTIMIZE_FOR_SIZE + Choosing this option will pass "-Os" to your compiler resulting + in a smaller kernel. + ++config CC_OPTIMIZE_FOR_PERFORMANCE_O3 ++ bool "Optimize harder for performance (-O3)" ++ help ++ Build with the "-O3" compiler flag: more inlining, loop ++ unrolling and vectorization than -O2, at the cost of a larger ++ kernel image and larger modules. Rust code is built at ++ opt-level 3. ++ + endchoice + + config HAVE_LD_DEAD_CODE_DATA_ELIMINATION diff --git a/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch.sig b/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch.sig new file mode 100644 index 0000000..48ab131 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0011-kbuild-optimize-for-performance-o3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig new file mode 100644 index 0000000..6256900 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig new file mode 100644 index 0000000..54e6137 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0121-smp-preempt.patch.sig b/pkgbuilds/linux-omarchy-bore/0121-smp-preempt.patch.sig new file mode 100644 index 0000000..078bc77 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0121-smp-preempt.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig new file mode 100644 index 0000000..3e089bd Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig b/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig new file mode 100644 index 0000000..3407a4e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig new file mode 100644 index 0000000..290ce7c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig new file mode 100644 index 0000000..89bb69e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig b/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig new file mode 100644 index 0000000..d64847c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig new file mode 100644 index 0000000..91abc02 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig new file mode 100644 index 0000000..a6bbb0d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0150-adios-3.2.0.patch.sig b/pkgbuilds/linux-omarchy-bore/0150-adios-3.2.0.patch.sig new file mode 100644 index 0000000..1f838b7 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0150-adios-3.2.0.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig b/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig new file mode 100644 index 0000000..20c9157 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig b/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig new file mode 100644 index 0000000..463f061 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig new file mode 100644 index 0000000..018259f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig b/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig new file mode 100644 index 0000000..ef0e5da Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch b/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch new file mode 100644 index 0000000..1c5c5b3 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch @@ -0,0 +1,95 @@ +diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h +--- a/arch/x86/include/asm/cpufeatures.h ++++ b/arch/x86/include/asm/cpufeatures.h +@@ -471,6 +471,8 @@ + #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* Automatic IBRS */ + #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* SMM_CTL MSR is not present */ + ++#define X86_FEATURE_L2_TLB_SIZE_X32 (20*32+14) /* L2 TLB sizes are encoded as multiples of 32 */ ++ + #define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */ + + #define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */ +diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c +--- a/arch/x86/kernel/cpu/amd.c ++++ b/arch/x86/kernel/cpu/amd.c +@@ -1190,7 +1190,7 @@ static unsigned int amd_size_cache(struct cpuinfo_x86 *c, unsigned int size) + + static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + { +- u32 ebx, eax, ecx, edx; ++ u32 ebx, eax, ecx, edx, shift, tmp; + u16 mask = 0xfff; + + if (c->x86 < 0xf) +@@ -1199,10 +1199,12 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + if (c->extended_cpuid_level < 0x80000006) + return; + ++ shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; ++ + cpuid(0x80000006, &eax, &ebx, &ecx, &edx); + +- tlb_lld_4k = (ebx >> 16) & mask; +- tlb_lli_4k = ebx & mask; ++ tlb_lld_4k = ((ebx >> 16) & mask) << shift; ++ tlb_lli_4k = (ebx & mask) << shift; + + /* + * K8 doesn't have 2M/4M entries in the L2 TLB so read out the L1 TLB +@@ -1214,16 +1216,18 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + } + + /* Handle DTLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ +- if (!((eax >> 16) & mask)) ++ tmp = ((eax >> 16) & mask) << shift; ++ if (!tmp) + tlb_lld_2m = (cpuid_eax(0x80000005) >> 16) & 0xff; + else +- tlb_lld_2m = (eax >> 16) & mask; ++ tlb_lld_2m = tmp; + + /* a 4M entry uses two 2M entries */ + tlb_lld_4m = tlb_lld_2m >> 1; + + /* Handle ITLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ +- if (!(eax & mask)) { ++ tmp = (eax & mask) << shift; ++ if (!tmp) { + /* Erratum 658 */ + if (c->x86 == 0x15 && c->x86_model <= 0x1f) { + tlb_lli_2m = 1024; +@@ -1231,8 +1235,9 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + cpuid(0x80000005, &eax, &ebx, &ecx, &edx); + tlb_lli_2m = eax & 0xff; + } +- } else +- tlb_lli_2m = eax & mask; ++ } else { ++ tlb_lli_2m = tmp; ++ } + + tlb_lli_4m = tlb_lli_2m >> 1; + +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -857,7 +857,7 @@ static void get_model_name(struct cpuinfo_x86 *c) + + void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) + { +- unsigned int n, dummy, ebx, ecx, edx, l2size; ++ unsigned int n, dummy, ebx, ecx, edx, l2size, shift __maybe_unused; + + n = c->extended_cpuid_level; + +@@ -877,7 +877,9 @@ void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) + l2size = ecx >> 16; + + #ifdef CONFIG_X86_64 ++ shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; + c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); ++ c->x86_tlbsize <<= shift; + #else + /* do processor-specific cache resizing */ + if (this_cpu->legacy_cache_size) diff --git a/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch.sig b/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch.sig new file mode 100644 index 0000000..33230cf Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0220-x86-amd-zen5-tlb-sizes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0250-zsmalloc.patch.sig b/pkgbuilds/linux-omarchy-bore/0250-zsmalloc.patch.sig new file mode 100644 index 0000000..24e9d20 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0250-zsmalloc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0260-mglru-exec-protect.patch.sig b/pkgbuilds/linux-omarchy-bore/0260-mglru-exec-protect.patch.sig new file mode 100644 index 0000000..b84157c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0260-mglru-exec-protect.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0270-ksm-rmap-walk.patch.sig b/pkgbuilds/linux-omarchy-bore/0270-ksm-rmap-walk.patch.sig new file mode 100644 index 0000000..f6989e4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0270-ksm-rmap-walk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig new file mode 100644 index 0000000..4e1b338 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0290-zstd-bmi2-fallback-aliases.patch.sig b/pkgbuilds/linux-omarchy-bore/0290-zstd-bmi2-fallback-aliases.patch.sig new file mode 100644 index 0000000..66df3c4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0290-zstd-bmi2-fallback-aliases.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig b/pkgbuilds/linux-omarchy-bore/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig new file mode 100644 index 0000000..9fb78f3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0292-crypto-zstd-defer-cstream-init.patch.sig b/pkgbuilds/linux-omarchy-bore/0292-crypto-zstd-defer-cstream-init.patch.sig new file mode 100644 index 0000000..38fc2a9 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0292-crypto-zstd-defer-cstream-init.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0293-crypto-zstd-defer-dstream-init.patch.sig b/pkgbuilds/linux-omarchy-bore/0293-crypto-zstd-defer-dstream-init.patch.sig new file mode 100644 index 0000000..5edd1fa Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0293-crypto-zstd-defer-dstream-init.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0295-af-alg-restrict.patch.sig b/pkgbuilds/linux-omarchy-bore/0295-af-alg-restrict.patch.sig new file mode 100644 index 0000000..94672f5 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0295-af-alg-restrict.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig b/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig new file mode 100644 index 0000000..b5e1f0f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig new file mode 100644 index 0000000..1586085 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig new file mode 100644 index 0000000..b319318 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0302-btrfs-zstd-decompress-direct-to-page.patch.sig b/pkgbuilds/linux-omarchy-bore/0302-btrfs-zstd-decompress-direct-to-page.patch.sig new file mode 100644 index 0000000..a75cf51 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0302-btrfs-zstd-decompress-direct-to-page.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig new file mode 100644 index 0000000..dc63cd6 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig new file mode 100644 index 0000000..ff6a782 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0312-fuse-writethrough-uptodate.patch.sig b/pkgbuilds/linux-omarchy-bore/0312-fuse-writethrough-uptodate.patch.sig new file mode 100644 index 0000000..bd3d135 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0312-fuse-writethrough-uptodate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig new file mode 100644 index 0000000..8ec069b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig b/pkgbuilds/linux-omarchy-bore/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig new file mode 100644 index 0000000..a00bf83 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0360-gpu-mem-cgroup.patch.sig b/pkgbuilds/linux-omarchy-bore/0360-gpu-mem-cgroup.patch.sig new file mode 100644 index 0000000..6e989c6 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0360-gpu-mem-cgroup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch new file mode 100644 index 0000000..6977a84 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c +--- a/drivers/gpu/drm/ttm/ttm_bo.c ++++ b/drivers/gpu/drm/ttm/ttm_bo.c +@@ -1343,7 +1343,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) + + if (ttm_tt_is_populated(tt)) { + ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); +- if (!ret) { ++ if (ret > 0) { + spin_lock(&bdev->lru_lock); + ttm_resource_del_bulk_move_unevictable(bo->resource, bo); + ttm_resource_move_to_lru_tail(bo->resource); diff --git a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig new file mode 100644 index 0000000..bd99af4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig b/pkgbuilds/linux-omarchy-bore/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig new file mode 100644 index 0000000..80b2f7b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig b/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig new file mode 100644 index 0000000..6e91496 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch.sig b/pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch.sig new file mode 100644 index 0000000..ff5430c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0411-drm-xe-display-no-stolen-framebuffers.patch.sig b/pkgbuilds/linux-omarchy-bore/0411-drm-xe-display-no-stolen-framebuffers.patch.sig new file mode 100644 index 0000000..67e82c0 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0411-drm-xe-display-no-stolen-framebuffers.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0420-safe-window.patch.sig b/pkgbuilds/linux-omarchy-bore/0420-safe-window.patch.sig new file mode 100644 index 0000000..33f021c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0420-safe-window.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig new file mode 100644 index 0000000..f0db1f3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0440-xe3-peak-bandwidth.patch.sig b/pkgbuilds/linux-omarchy-bore/0440-xe3-peak-bandwidth.patch.sig new file mode 100644 index 0000000..99d5c08 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0440-xe3-peak-bandwidth.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig new file mode 100644 index 0000000..ee35537 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig new file mode 100644 index 0000000..7077a0e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0452-amd-hdmi-frl-default.patch.sig b/pkgbuilds/linux-omarchy-bore/0452-amd-hdmi-frl-default.patch.sig new file mode 100644 index 0000000..c879559 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0452-amd-hdmi-frl-default.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig new file mode 100644 index 0000000..f76e652 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig new file mode 100644 index 0000000..d303e52 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0472-amdgpu-userq-post-reset-error.patch.sig b/pkgbuilds/linux-omarchy-bore/0472-amdgpu-userq-post-reset-error.patch.sig new file mode 100644 index 0000000..a79ea43 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0472-amdgpu-userq-post-reset-error.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig b/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig new file mode 100644 index 0000000..1048bf7 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig new file mode 100644 index 0000000..14aa526 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig b/pkgbuilds/linux-omarchy-bore/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig new file mode 100644 index 0000000..3ddfad0 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig new file mode 100644 index 0000000..cfb31fc Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig new file mode 100644 index 0000000..09b8d69 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig new file mode 100644 index 0000000..c02c1df Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch.sig b/pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch.sig new file mode 100644 index 0000000..3ded939 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch.sig b/pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch.sig new file mode 100644 index 0000000..47fe225 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig b/pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig new file mode 100644 index 0000000..edb27fd Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig b/pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig new file mode 100644 index 0000000..a16f394 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig b/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig new file mode 100644 index 0000000..e516289 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig new file mode 100644 index 0000000..92c4d7b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig b/pkgbuilds/linux-omarchy-bore/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig new file mode 100644 index 0000000..57011b1 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig b/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig new file mode 100644 index 0000000..409e402 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0565-i2c-asue140d-touchpad-100khz.patch.sig b/pkgbuilds/linux-omarchy-bore/0565-i2c-asue140d-touchpad-100khz.patch.sig new file mode 100644 index 0000000..8742aa9 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0565-i2c-asue140d-touchpad-100khz.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig new file mode 100644 index 0000000..f1805db Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig new file mode 100644 index 0000000..33208c2 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig b/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig new file mode 100644 index 0000000..8f8f07d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig b/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig new file mode 100644 index 0000000..fd0bd60 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0620-usb-string-sanitize.patch.sig b/pkgbuilds/linux-omarchy-bore/0620-usb-string-sanitize.patch.sig new file mode 100644 index 0000000..9ed271f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0620-usb-string-sanitize.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0650-wireguard-tstamp-type.patch.sig b/pkgbuilds/linux-omarchy-bore/0650-wireguard-tstamp-type.patch.sig new file mode 100644 index 0000000..fa5148e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0650-wireguard-tstamp-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig new file mode 100644 index 0000000..ceec933 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig b/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig new file mode 100644 index 0000000..a9714f1 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig b/pkgbuilds/linux-omarchy-bore/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig new file mode 100644 index 0000000..645c20d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0700-pci-target-speed-quirk.patch.sig b/pkgbuilds/linux-omarchy-bore/0700-pci-target-speed-quirk.patch.sig new file mode 100644 index 0000000..897fa75 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0700-pci-target-speed-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig b/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig new file mode 100644 index 0000000..6a23cb7 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig b/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig new file mode 100644 index 0000000..d4cd8fb Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig b/pkgbuilds/linux-omarchy-bore/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig new file mode 100644 index 0000000..281de72 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig new file mode 100644 index 0000000..03807cf Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0801-amd-pmf-util-unbind-use-after-free.patch.sig b/pkgbuilds/linux-omarchy-bore/0801-amd-pmf-util-unbind-use-after-free.patch.sig new file mode 100644 index 0000000..91d6150 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0801-amd-pmf-util-unbind-use-after-free.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0850-futex-wait-multiple.patch.sig b/pkgbuilds/linux-omarchy-bore/0850-futex-wait-multiple.patch.sig new file mode 100644 index 0000000..b8c1df3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0850-futex-wait-multiple.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0851-futex-wait-multiple-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0851-futex-wait-multiple-fixes.patch.sig new file mode 100644 index 0000000..f32092d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0851-futex-wait-multiple-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0852-futex-wait-multiple-abi-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0852-futex-wait-multiple-abi-fixes.patch.sig new file mode 100644 index 0000000..4a42013 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0852-futex-wait-multiple-abi-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig b/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig new file mode 100644 index 0000000..bbe1ce3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig b/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig new file mode 100644 index 0000000..0707c41 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig b/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig new file mode 100644 index 0000000..ddd9329 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig new file mode 100644 index 0000000..1b113b3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8205-mm-hint-uses-allocation-order.patch.sig b/pkgbuilds/linux-omarchy-bore/8205-mm-hint-uses-allocation-order.patch.sig new file mode 100644 index 0000000..7c3ef42 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8205-mm-hint-uses-allocation-order.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig new file mode 100644 index 0000000..de65251 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8207-mm-classify-huge-page-allocations-as-failable.patch.sig b/pkgbuilds/linux-omarchy-bore/8207-mm-classify-huge-page-allocations-as-failable.patch.sig new file mode 100644 index 0000000..e60df1b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8207-mm-classify-huge-page-allocations-as-failable.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig b/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig new file mode 100644 index 0000000..88c17d4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8209-xe-shrinker-use-opportunistic-hint.patch.sig b/pkgbuilds/linux-omarchy-bore/8209-xe-shrinker-use-opportunistic-hint.patch.sig new file mode 100644 index 0000000..7928b4e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8209-xe-shrinker-use-opportunistic-hint.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8210-xe-shrinker-single-backup-decision.patch.sig b/pkgbuilds/linux-omarchy-bore/8210-xe-shrinker-single-backup-decision.patch.sig new file mode 100644 index 0000000..a823434 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8210-xe-shrinker-single-backup-decision.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig b/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig new file mode 100644 index 0000000..7b95eee Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/PKGBUILD b/pkgbuilds/linux-omarchy-bore/PKGBUILD index 5670282..d9c5b41 100644 --- a/pkgbuilds/linux-omarchy-bore/PKGBUILD +++ b/pkgbuilds/linux-omarchy-bore/PKGBUILD @@ -3,7 +3,7 @@ pkgbase=linux-omarchy-bore pkgver=7.2.5 -pkgrel=3 +pkgrel=6 pkgdesc='Omarchy Linux (BORE CPU scheduler, ADIOS I/O scheduler)' url='https://omarchy.org' arch=( @@ -48,99 +48,101 @@ source=( https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/${_srcname}.tar.{xz,sign} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.xz} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.sign} - # Local patches are verified by the checksums below. - 0010-archlinux-base.patch - 0110-bore-6.8.0.patch - 0120-tlbpull.patch - 0121-smp-preempt.patch - 0130-sched-detach-tasks.patch - 0131-sched-avg-idle.patch - 0140-sched-always-inline.patch - 0141-sched-urgent-fixes.patch - 0142-sched-itmt-no-debugfs-dependency.patch - 0143-sched-hybrid-cluster-balancing.patch - 0144-sched-nohz-idle-core.patch - 0150-adios-3.2.0.patch - 0200-idle.patch - 0210-pstate.patch - 0211-amd-pstate-fixes.patch - 0212-amd-pstate-epp-cache.patch - 0250-zsmalloc.patch - 0260-mglru-exec-protect.patch - 0270-ksm-rmap-walk.patch - 0280-mm-updates.patch - 0290-zstd-bmi2-fallback-aliases.patch - 0291-zstd-bmi2-cpu-feature-dispatch.patch - 0292-crypto-zstd-defer-cstream-init.patch - 0293-crypto-zstd-defer-dstream-init.patch - 0295-af-alg-restrict.patch - 0296-x86-mm-pmd-modify-keep-dirty-bit.patch - 0300-btrfs.patch - 0301-btrfs-fixes.patch - 0302-btrfs-zstd-decompress-direct-to-page.patch - 0310-fuse-eof-zeroing.patch - 0311-fuse-perf.patch - 0312-fuse-writethrough-uptodate.patch - 0313-fuse-background-wakeup.patch - 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch - 0360-gpu-mem-cgroup.patch - 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch - 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch - 0402-psr2-early-transport-panels.patch - 0411-drm-xe-display-no-stolen-framebuffers.patch - 0420-safe-window.patch - 0430-fbc.patch - 0440-xe3-peak-bandwidth.patch - 0450-amd-hdmi-vrr-allm.patch - 0451-amd-vtem-tmds-links.patch - 0452-amd-hdmi-frl-default.patch - 0460-vesa-displayid-dsc-bpp.patch - 0461-vesa-dsc-passthru-mode-match-fix.patch - 0472-amdgpu-userq-post-reset-error.patch - 0473-i915-ptl-cdclk-sanitize.patch - 0474-amd-display-oled-vesa-backlight.patch - 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch - 0510-sound-updates.patch - 0511-sound-updates-fixes.patch - 0512-sound-fixes.patch - 0513-xps13-sof-quirk.patch - 0514-rt766-stream-config-type.patch - 0516-hda-realtek-rog-strix-g733zw-speakers.patch - 0517-asoc-amd-yc-acer-aspire-a314-23p.patch - 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch - 0541-cvs-nova-lake-acpi-id.patch - 0542-media-cvs-wake-irq-without-claiming-gpio.patch - 0560-input.patch - 0565-i2c-asue140d-touchpad-100khz.patch - 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch - 0600-usb4stream-fixes.patch - 0601-usb4stream-busy-poll.patch - 0610-typec-cable-altmode-check.patch - 0620-usb-string-sanitize.patch - 0650-wireguard-tstamp-type.patch - 0660-btusb-mediatek-mt7922-13d3-3625.patch - 0661-rtw89-command-offload-source.patch - 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch - 0700-pci-target-speed-quirk.patch - 0750-applesmc-cache-race.patch - 0751-applesmc-key-backlight-workqueue-leak.patch - 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch - 0800-platform-updates.patch - 0801-amd-pmf-util-unbind-use-after-free.patch - 0850-futex-wait-multiple.patch - 0851-futex-wait-multiple-fixes.patch - 0852-futex-wait-multiple-abi-fixes.patch - 8201-xe-shrinker-return-freed-page-count.patch - 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch - 8203-xe-shrinker-release-through-the-put-helper.patch - 8204-mm-opportunistic-compaction.patch - 8205-mm-hint-uses-allocation-order.patch - 8206-mm-carry-order-and-hint-in-one-word.patch - 8207-mm-classify-huge-page-allocations-as-failable.patch - 8208-mm-thp-deferred-split-uses-hint.patch - 8209-xe-shrinker-use-opportunistic-hint.patch - 8210-xe-shrinker-single-backup-decision.patch - 9999-bpftool-strip-wformat-bootstrap.patch + 0010-archlinux-base.patch{,.sig} + 0011-kbuild-optimize-for-performance-o3.patch{,.sig} + 0110-bore-6.8.0.patch{,.sig} + 0120-tlbpull.patch{,.sig} + 0121-smp-preempt.patch{,.sig} + 0130-sched-detach-tasks.patch{,.sig} + 0131-sched-avg-idle.patch{,.sig} + 0140-sched-always-inline.patch{,.sig} + 0141-sched-urgent-fixes.patch{,.sig} + 0142-sched-itmt-no-debugfs-dependency.patch{,.sig} + 0143-sched-hybrid-cluster-balancing.patch{,.sig} + 0144-sched-nohz-idle-core.patch{,.sig} + 0150-adios-3.2.0.patch{,.sig} + 0200-idle.patch{,.sig} + 0210-pstate.patch{,.sig} + 0211-amd-pstate-fixes.patch{,.sig} + 0212-amd-pstate-epp-cache.patch{,.sig} + 0220-x86-amd-zen5-tlb-sizes.patch{,.sig} + 0250-zsmalloc.patch{,.sig} + 0260-mglru-exec-protect.patch{,.sig} + 0270-ksm-rmap-walk.patch{,.sig} + 0280-mm-updates.patch{,.sig} + 0290-zstd-bmi2-fallback-aliases.patch{,.sig} + 0291-zstd-bmi2-cpu-feature-dispatch.patch{,.sig} + 0292-crypto-zstd-defer-cstream-init.patch{,.sig} + 0293-crypto-zstd-defer-dstream-init.patch{,.sig} + 0295-af-alg-restrict.patch{,.sig} + 0296-x86-mm-pmd-modify-keep-dirty-bit.patch{,.sig} + 0300-btrfs.patch{,.sig} + 0301-btrfs-fixes.patch{,.sig} + 0302-btrfs-zstd-decompress-direct-to-page.patch{,.sig} + 0310-fuse-eof-zeroing.patch{,.sig} + 0311-fuse-perf.patch{,.sig} + 0312-fuse-writethrough-uptodate.patch{,.sig} + 0313-fuse-background-wakeup.patch{,.sig} + 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch{,.sig} + 0360-gpu-mem-cgroup.patch{,.sig} + 0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch{,.sig} + 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch{,.sig} + 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch{,.sig} + 0402-psr2-early-transport-panels.patch{,.sig} + 0411-drm-xe-display-no-stolen-framebuffers.patch{,.sig} + 0420-safe-window.patch{,.sig} + 0430-fbc.patch{,.sig} + 0440-xe3-peak-bandwidth.patch{,.sig} + 0450-amd-hdmi-vrr-allm.patch{,.sig} + 0451-amd-vtem-tmds-links.patch{,.sig} + 0452-amd-hdmi-frl-default.patch{,.sig} + 0460-vesa-displayid-dsc-bpp.patch{,.sig} + 0461-vesa-dsc-passthru-mode-match-fix.patch{,.sig} + 0472-amdgpu-userq-post-reset-error.patch{,.sig} + 0473-i915-ptl-cdclk-sanitize.patch{,.sig} + 0474-amd-display-oled-vesa-backlight.patch{,.sig} + 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch{,.sig} + 0510-sound-updates.patch{,.sig} + 0511-sound-updates-fixes.patch{,.sig} + 0512-sound-fixes.patch{,.sig} + 0513-xps13-sof-quirk.patch{,.sig} + 0514-rt766-stream-config-type.patch{,.sig} + 0516-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} + 0517-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} + 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch{,.sig} + 0541-cvs-nova-lake-acpi-id.patch{,.sig} + 0542-media-cvs-wake-irq-without-claiming-gpio.patch{,.sig} + 0560-input.patch{,.sig} + 0565-i2c-asue140d-touchpad-100khz.patch{,.sig} + 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch{,.sig} + 0600-usb4stream-fixes.patch{,.sig} + 0601-usb4stream-busy-poll.patch{,.sig} + 0610-typec-cable-altmode-check.patch{,.sig} + 0620-usb-string-sanitize.patch{,.sig} + 0650-wireguard-tstamp-type.patch{,.sig} + 0660-btusb-mediatek-mt7922-13d3-3625.patch{,.sig} + 0661-rtw89-command-offload-source.patch{,.sig} + 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch{,.sig} + 0700-pci-target-speed-quirk.patch{,.sig} + 0750-applesmc-cache-race.patch{,.sig} + 0751-applesmc-key-backlight-workqueue-leak.patch{,.sig} + 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch{,.sig} + 0800-platform-updates.patch{,.sig} + 0801-amd-pmf-util-unbind-use-after-free.patch{,.sig} + 0850-futex-wait-multiple.patch{,.sig} + 0851-futex-wait-multiple-fixes.patch{,.sig} + 0852-futex-wait-multiple-abi-fixes.patch{,.sig} + 8201-xe-shrinker-return-freed-page-count.patch{,.sig} + 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch{,.sig} + 8203-xe-shrinker-release-through-the-put-helper.patch{,.sig} + 8204-mm-opportunistic-compaction.patch{,.sig} + 8205-mm-hint-uses-allocation-order.patch{,.sig} + 8206-mm-carry-order-and-hint-in-one-word.patch{,.sig} + 8207-mm-classify-huge-page-allocations-as-failable.patch{,.sig} + 8208-mm-thp-deferred-split-uses-hint.patch{,.sig} + 8209-xe-shrinker-use-opportunistic-hint.patch{,.sig} + 8210-xe-shrinker-single-backup-decision.patch{,.sig} + 9999-bpftool-strip-wformat-bootstrap.patch{,.sig} ) source_x86_64=(config.x86_64) validpgpkeys=( @@ -151,194 +153,390 @@ validpgpkeys=( b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97ab6c656ea8e5b29f2c4a8fe4cc143a626f4cca6b972a8105991e4c6905' 'SKIP' '6d92fb81077232b8cd1ae500b3aabc71434792e750ea6126dc095e50d4278d3e42b3cfdc3c8bc693ea14b109f6adfa3a1ed095187fc5501962fbe8f049f867bc' + 'SKIP' + 'cc2a66a097b5567e80c59b7d6c492fef5e667fc71a0390908712c69512c4136dbaf3d150b62d04eff960c796f7a94e6d0915d3b92cb336318b49f5f516a3bba6' + 'SKIP' 'a32edb39b4ee9c0378239f4998f477fe5371e6931c188d660874b360ca6d3bdcfff71cf088bed071365627d87458eca0be625e154ca3d1e8150e5e0bdf64175c' + 'SKIP' 'f6c00ac2400580dffe3605d2693809396e18185f0c59b21b86f1f22a1f8d6529c51e8cc2474bc40f74a96631f415da51521a8b35d5b096e9b9d164c2755fb091' + 'SKIP' '4651980c9d988ed73dc91fd440467de0a1de21efa3efae0fc006524c88af40173182ce6826c212e8b7faaaae1aa512fc755272ec4663c185533e65c936780db3' + 'SKIP' '57f88a64a8ec20794c008c9de70b3f01680ce9023925df8e0f2c7be69f2600e10276d54bd264f5b8b258e04d38cf483cbf83bb79df466f17fe4b6b33fec52a76' + 'SKIP' '050d38240747325e90bb86bb7c47a14573dcb297ee323b3d84f74df9ddf3691cdf32ac9e293bd237c9b09221065b002e297745f4b898a937b9347dacb2de7109' + 'SKIP' 'd21fa7cb609188720365a8d95a98c3286f2f32a772309e66913c6b16bcf871ed28ea1ff01fc15ecbb47a02def1a557512126e39ac0daa956a1ba89878acf5de5' + 'SKIP' 'cd744c8f861d2cfdfdc98ea6875419a298dcb5d478444b8e551a52fe7ce5279e2d977f8610783d79144be4927a8bf41b2f80113e6f7aa3ea02f54b9984446017' + 'SKIP' '3434a5bbcd2bd8b4e2013400bf4d771ad5c8295ec74f67ff7acaf917954a01d25931679a71ddb8995fd7703b0b9904db4e6fe5f384d3705cf4d7b80dbf5dba55' + 'SKIP' 'c8b8bb1ec676378d862f64b23445f170f2e995f3d89f4ffc727c377128ace87417e50d58c39e62d62795924ab4c62f3defad64139e3d3b472b75f6431484e2dd' + 'SKIP' 'c9c1be8f72cef610592b1815a81a34706b54f2ef4a188acbd31d98243129e2939fe0c2b679f428a67eac70b069c8a662c3629c598f89430930f5b8bb602aada8' + 'SKIP' '4f58fc77bc13fe9492fe4662e0a4b879a8a1616e271c2095959dccdffed9b0a866a4eb9dc3ac74fad9eed11c362e43a7bfa56d8ea6f571572a63ff4550c5dc23' + 'SKIP' 'd1007ee452cdf92967ec7055f1f39a3007a69842040e12f5063a1d2f24fd6d1eeaff2357abf96d0e330541a4781f93fecbcb116a4c73371913582eb72b8f9466' + 'SKIP' '0d3309140e496ad552d1a15d4fbf04b5ad03877bc73ae689f57554e91b7458578f2c88b6bbd37dd9fdf822a8d48c2cd6f961ec735ae5887f96fa51d908d203ee' + 'SKIP' '13beae6f8d23a1f075410f50c09bc94f24f505571af1feb3cee58f732c0d649a9ce5939bc11642c4f3c8dfbd967939b2a0a205220abf06f10d559c4c43a946a3' + 'SKIP' '2f24f88d8f960148046c36222fdaaadfed2ca88c60240fa1d111430898192ca45aeaa93a3afa24ad02b11c4dc8c3904a92f17c0284396f2f43352ad934a35056' + 'SKIP' + 'e59c4da548b2ea4fd6144886e3e2e765b3e73972121e9e73dbc05c066834b2b35e6ef4c17617e77efc90260566bf7c0910d2138d8d5c1c3b6094eeac9de64cfe' + 'SKIP' '9809e258eb684b80d508fa2f2fa270683b62bd1412ed05a970114aaa7d84c37c0beaea51f9ff8c6ee1bc7318c4aee3725e9a1131fefb51ba700801de6ac414c5' + 'SKIP' 'f115e8c80d723f7c24e6b1638b801cd664bef20acb1d2f6e6f5612744b916278693011782228cd3f4d0518654b8d53309bc5b06bbe8ed722fab9995f369bf548' + 'SKIP' '31abdd15ae3f9709d6912c90319a0b62d8380e1ae1169575c0e8e419f8ce264a7cf7c72558a85719cd5a1cae7341007adb3e3936268999ebd5215c2ed288f22c' + 'SKIP' '4efb9ab5258aee01b07bc322b160d2f6e3571ff82c4f3e317cab152a919c67522629d56fb79d517da034373cd6924bc3a20c98073197c93590feb1a34e902358' + 'SKIP' '55951c59286df43f069302b0b1036424812f80b45c5ea13219fce1bfc3e831731432c216288d722e44493c736cbf89a3e44801b5a4f1f242c17b5a89b236a5ef' + 'SKIP' '737db39c2f7f46550de4d6d80659bd6c1fc2bf1ca42bd7bd6fc345fc90af995acd056051e12175ac6c957333629a017051d76d5359c871158c93f0a294fb8de5' + 'SKIP' '4d0646c8ad139add6abb62d75308d2d7b5b07afbc74a0f0297ffc2148f0db38abaea16b048704187da49fe3ddee8370b10a7a41d6e62b665650cd79e8c392b4e' + 'SKIP' 'bd7bce1bf0ee24ce201fe0e5f0b8a6c829a84d224800547cf97a893326381e2e6eaed9c70529a6864b0c338276b3a307b14e26fba221e62263b90c02eb5e5f34' + 'SKIP' 'ad2cfdc3d8de2193110325395e11015577f26ef1081307bb236ed73fbd109822dd44ea026a57c2e17233e1fb7982e78aa986b71c043f42887e6c5d7f51b84ea9' + 'SKIP' '19c3dd32bbe3ed7caec86e90a6af39b07eb99d5a36515cf061bb6979e5a403ca98a0e00e584a9f746330d663e5fc0868ba7fc98ab2df264bc863bdf22ef3dd5e' + 'SKIP' 'ed2ee4849b715d80ba103fe090669062fc08c6864c64e8f8291f7f21092f0184ca5528bdd3a9318f077ef00cbe8cf28bacd1e7a9cb9f9ae1047becd930a0c42e' + 'SKIP' 'f09e80ed25fdda9b2c79bd5ea3701985596d478b0b4e6948a1ea8fb789ef944e71640ae200442afcc23a9f0e07b34d83365e223b620baf776bbd864977e633a0' + 'SKIP' 'a51e5f2a5e37c350fb4d715ece644c565a508970c3aeb1d28cb6cd03a0c7316a1935e028b64f20a03818926c611b50ae0ffd77d04c676d604d303beed0f96e1f' + 'SKIP' '25dcd2c4a4287f26c01419b7d6430629d2827b6efff473361c68d6ec44e94e6d2bb8ab8215dbae5da25781a668f571cfc28b60c5693eb4ed629bf80fe2f68ccf' + 'SKIP' '8d6eeb2330a3ca7c522cef77bf7336fac9444a0884d5d101c933abd3a31a3711529a31f9a181d4795148ecbcc24425750587314ce28e44c9ab2fd2de8365e853' + 'SKIP' 'fd487cb02212ffdae5d8beb7d895819377686085b95658dcf597a534b017adcb65dfdf06e0276e7322a46dc28118803b05c246c22e7d3c361f1778ed9e12a3c6' + 'SKIP' 'e18e5086c531a37837f6e7df72ce10a38289b02de4cea376772d45cccb5aabc628fe4f3f9607f646903a7cf59b02692703e6b26800604cf958136987df0b4d5d' + 'SKIP' '1b340a502dcf5a1680caf5320b3aa72aa4317e4cf1d877c1f6425e281a5668d5f092417e1f282371c0a211344f5c187fb3bdaf3f97c7e122951edf7e4c08ca58' + 'SKIP' '1124c5c3fd5104c886c841ead1ae9afb42683388b640b1a86523dad9919c6bd59a1f78b07ee5a213e0dca77c636a29676af0592d4ed1735ea35f282f6f23e46f' + 'SKIP' + '15855e9c28cb0abaab7ab606904dfc84d426ccab44edae59f0e1f092202b80525929f8d394d04ca1513d1d5dfc2cfc108a66a9c1cb42b9966c6417562ef20689' + 'SKIP' '505bf01a9d6b0926b557b673bb686d1a4ff73ee70539991e52272fbf58aad3ea25cf5ffc5c2d094a2d5e78b18a1a134f2ab530f595073a2d3eba9ff2c1418e1e' + 'SKIP' 'b33b46c37f12f7650a67c3731b87ff84add1ac8623cc96b52223a1ef9946bcaa990ed7c7036674e228335f57ec9ed421ea4d6c1dd72d19b516215d9ac2c7c3ee' + 'SKIP' 'b4bf4c4c0672bad91d7f8d309e741104918dd8ee8688a00d600379eba5828d1b8157953377c73d0b58050b4ff888c43013ede4e9c959be8d9c028a6143bf58ed' + 'SKIP' 'e403bdc300284c4f0f4302e501b8ccfe5e9af04e73a6070eb6615322f5f40ed6edad53af2ed278741f61a4e3811b92faf1af02f113dbcc2a19bde335a55930d7' + 'SKIP' '5de29d0eb5264e24ab9228bea2ea4b549b3fe37ea67c045df9f83064f11c2f557b9641e772d28db59f8d258418c77435b66ff471c3695043f0f8275d5e7b1685' + 'SKIP' '3c2cd960103ad97ce35679f906b3c9be79a9744959c5dfd0ea338d8075b698790934694b2f71fde026b164c5246cba71ffe3cd5dfc1464da338136fbbaea2f1c' + 'SKIP' '2b33b11e48d8dd8d6743f26170fb99eae38541e3665e2448d91cedf729cca7c0278bfba1078ecbe88201eac4407ec5bd0292a97dbab9cfc67e42646712befc1f' + 'SKIP' '71934436987ecf6914e5c559445c2b7ee92a31c467552342094c93a59d32563aa9930e710556cc1e271eeb352705a9df9b3702482f87d045d132233cee4ab48b' + 'SKIP' '9fb1318a3a181644c67b2adcac0cb04d1b88dd0e632751967239d4e8d4698f193795d00194b0f68505964c84572b67168dcbed02ad5bb7abd41e406a62111489' + 'SKIP' '353b7e89a7044b7426db730d5b900eb347907aa0c20e307d2317925d6021a969a343649ede7a32e698dad60dd4472eb197c9578fb0e36fc354a60266fd680cd7' + 'SKIP' '803223224f22aca25dfe34d73e2bd49438f0cfa83b71baa545fa6fe93ee1a631b6b62c7324dc1c0d2d2ded94c36185a83af39b4f4a3362f554a1943ec2e2ec3e' + 'SKIP' 'b0a7ab192066c472f3ebc39bc9afc9fef0528ff7c98bf67c5b9cb679cc6d93eab3d25e91d8d74c9c52f72803949a3ade7940e677b9a3114605b966df94da93bb' + 'SKIP' '824089f9f45e2ff9e788d874bb9294b9208b4486fd22c56cb59d4d88cb996ff5a81ac22f8ff136ddffa1e40330f02e53ab759ffea7e4535c11d4f8195069966b' + 'SKIP' '696309582c56fad530a0bf2be12bb744dedac11ed69642b14eade0a26baf1ce52b36074b89b963b40c9c6fbc4857cdc71b2a6c6d64afcbb2df716bbb57579986' + 'SKIP' '14212fca051d3e9a85f75616ff7f95bbd119ef8e5400dafd4447f70db2c4e9c03d971ec4fdfb4db2319d2613062d74f54dfd5d0a100ab0fa27ac6c05e16bd063' + 'SKIP' '7228e249d25a99f5106040b8ddb5705979b3a5af21ca60d0d43a9ecaf6eaccef6bde8ea69c896cffbf2b5963ec4f68987e69b184a8885c7f4c98d2b05fd59fbb' + 'SKIP' 'a54ac1c6d43fe88dee08884ed2e5bad0a0b6d11f34a46dc9801e675860afb7641b57384248a144a621e66a73541d73da11f71671116f1cc7f21534526411439a' + 'SKIP' '2e486cabad0d45aeb760d031c7946e9887c710b29dbe9c16e5f4bae6a230031dba4d817f1ee25a0bbca849d1c71b2f84052ffb73c4a6c4350a6ea5ee83c5a40e' + 'SKIP' '23e2a5def526fa886d1f1b05607bfe6b1ecbb2d6009d023145adff9eadc3135f9078faca6638966b586e81e4ab9d48d23b5758504d5a043d5f46e7a701d185fa' + 'SKIP' '5d82f32f7c04b084530722b9848254aac289c2a754a10e16821083a871ec14065dd9ee83e72b8e9e8ba2915f3fbaf17b0874df724002eb08a5f4f84d1b6aecd8' + 'SKIP' '4928cc008dc91fddb89b154b89b1ce2bbff14d8186c0ec1f81971fb9d3b622475e93e42015916699c3442e2268680779f545d183112b57dedae96fc48a75844c' + 'SKIP' 'f93494f5f3196cc35d017ff9719d146bf3e5e7bfe32a7f5e75d96468ce30ef367428ba83b1c99b665064d3edaec84cf3bf18211d8f7b4f118b3bdee495eb8ef9' + 'SKIP' 'eb79554ef0d014b94bc72d4d559c2cc5419df96486acb3b6a09d5e8e9dc48db626ce0f90338c67fe873b2cc3c45c3f308c8080532b60f74a07c9e39e01d34197' + 'SKIP' 'f657143cdd9c937d78315f4617392e60f7c46f5cec882e060f740f77a27dd4c045878cb66e568a6d9ae74e9c706ffb3052817db6f327c87b85ff2f46eea678e2' + 'SKIP' '0acbd87a09a62af860d14f342d61c82c29ec1711038d4abf4ae473df187fbda6aa49386d88928c5dc4c3379e097478b1d40d9ebe9360e64d02e59161164b2a3f' + 'SKIP' 'bb06066e90132f0172333d46be54ca816eb7d3230a193a6cae765d8edc50df4a6ba9fb7c0647cd6befc8c3bbbe1d96c285ee202812477d7295c1130ea8cdd6f4' + 'SKIP' 'f1a94986b4b3f310fecf981e3619f9787a0c8cb0b4fb3f39e5fe85ee3400e7e50278d41bf5468f65e5bd890358d2aabe28d3e11ee838ccbda2ac7af16be568d9' + 'SKIP' '44dcaad2ab326ebee62d3e63207a161dcb1b63bdb21412b5624ca7875bb6e99997528683a633d7dd526dc5ae9408e73bd066e2a1e5b34ba5c829162df2265346' + 'SKIP' '162a2c2104d5f036657ace6eef2ac2d533c0934376fab2521ff1d3fccd704e3d08fa4f6162e9100a844db2aeb57fa3ce214b8058bd227f38b058ab7606ee7816' + 'SKIP' 'd50d5a69087a5ba6c416a6391232764c8281a8094645175fdb2cfc1bb011e33335ae4e96d79364681bea61edcb58ba9acb491e08c301d0667481759d0ee1e183' + 'SKIP' '539b1d0e764f0ace3e572ac49f20a790183155ddc0bcd3a8d040f44c9891dd36b1a0accb8c0288267afbd33e6ceadf6fc3a5e3db40491c695e0c1d3dd64af40c' + 'SKIP' 'f5cae6eabf2066d5deef737a15967d5d11340de34b181f3b3d841023507fc58af2b68926eb04bb342e1adb857165400f862f2b35ffec4204a107b3b22a1571e7' + 'SKIP' 'ab28a1f4f1756b8451840566d8100cb3d0b498b13d006593c72829b9783e94900bae78ab47c576d156e1d08c56602274b3f7d5bd885de17336623b23c3e4f255' + 'SKIP' '8481f23c33a84d8efb798f4b83f78011f74cd5a3dec428b177d7e600922836c1eec61c4238724038221d8ca2e2cdf20462c5911085da3461aa315f7dd19d96d8' + 'SKIP' '19ebb39f3e53ce77dc3522d15c88270c5ca3fdd76fa8bd417852d08a1d85b30bc6eed3e11c95392b694dfc59bb8b43e41d43df4954f00593c8fbe50bd9e199cc' + 'SKIP' '4dc42e9bfb5ee0f952bfd7befc9d333044aec11fc3a3852d4636f2e38ab139eb92c497eb3e30921b6ee8ae7d6e5834dc334ad25419bac2c875a9fd7eb7945f9b' + 'SKIP' 'fea498b5c5ada6d7f57caf652b68f687081aae8552d2d04cd9e96764efd1b896208671f4f501210bb557710cca3ceb0c02ac2b5da6d4c04c210e87ec2d715384' + 'SKIP' '4a4af88fc8a8c37ae6d20b35d19e60a17672569d5489be120edbcc62ab3ba4bbc43b78cd0799b9fd73c84b089364320bcbad6fcb7128c2cb06d15ed984d6fc8c' + 'SKIP' '0959ca78ff31299adf1091f4aa1b32cd2e9066aba5e44f0658c91bcb93fe8714f7b671ffa2cd498ecbb6c6e643ac6571bfb193034af206599122fd7c2a3c23a8' + 'SKIP' '03ca740048e48c5b6948f972f7a826240cf51c9b5c8645d1b1c50366e78cbf45f5b45b7b47f87f231b50aab46ef2f8fd78bc53899ba1c319a19f9f61c7cecb90' + 'SKIP' '1b63569b589e994d1869e2f8eaf5d4ee225fe68862ac5c848e46544b827ca5fddb4bf060f5f39001608fd9bb284eec005f5f9b095307d3a008c9ea8957c2fad1' + 'SKIP' '073514dabc88f0206911eb27eb36157e425849221056438340b22ee7bfa6997b58ce7dc4470b09d7ee96f6374f117d91cc19d9032401b2355d1c0aba6fc75771' + 'SKIP' 'd7dffb68fd22db7ef41d1ac193dd297fb6150f034f257a44e6cb0a4848dc4a94e328c2e26d5296a72993e2db75d09e3ac46456d337a5bae767fb266082d3cc66' + 'SKIP' 'aca0527f5630671a319e7ecff958a3a64f29df73b427c67c1d29763cc1801fa82e96761c3a61c22d936913943a49261da64d490a0752ba9ffc8e0c1d92f99835' + 'SKIP' '803736a8e836560b3e50b7c0c8cf1f9ebb16b8642cc7bc6e5167d19037bd757431565fe4581776e50eb2693c49f778a24fecff41a553612e7e498f755b8f2f67' + 'SKIP' '4bc132d9c34deaae9b13d08dcff8ea83aa8f9ce4398531e0e0a80df30da3808815f3a587fe5627f6553b8e9c2aea6cc1749b752250a91c94f1cf8ba4f3eb5e8d' + 'SKIP' '2b007e58c0541c6ae71ac07cc8c0762343a50e1f3eb52db5631ce725c4f074f41c639fbf6d8695daa78ee1d6a8cb9a0c31d0a9d9ea2a3a10c05f7f6421dda184' + 'SKIP' 'd3b2c0e4fd9f26a0eded375522478f8e9403232799d233eed4668920f8a9942ec6bafbc928f14459e4736f63a9a66d3ae2e9f70dc810145acb5fc522b13ecbd9' + 'SKIP' 'c7b0be7a05a304e9993fe8ebccefa44944bf9d27dad9f7b3cc7111221bf50b2db71ca51cafc89dc03b237aef3aba7114543c8475cc393d13e61726e68898d3af' + 'SKIP' '4008c7443bc221a142a939deac86918bef8f09ff1ca99a899586b13120edb8007450daf0b9d143770a9f04d616fe96f527b6aac160be45e5cfa66c0da898039c' + 'SKIP' '029d9e899dd9e7bdbcacee34ee9b468e2e2a8f726e38d014891c8353f6afcc52a58c7d9289fb0487cce4d90c8d51f578c27b1dbcdab42de7308f50b7baf27877' + 'SKIP' '9ede433f4e01a5a313a133a8029d64250596582b93a33e434b8e2ee5bc53ba7f91947e1be2aaf57776028cc89e7ca62cc90067ee64650b3e503acad858777a8a' + 'SKIP' '9ac15b3542629c5333cc7d7f53b4f2995d807a31331b9e04d3da051be0ddde7e8c3cbc79837ffb49a4d364db1680cdc6d763465fa56bd4d26b16b765985babe1' + 'SKIP' '25fc5f03732a584c864ee2ad20a74e16777998b0b34f880bbe9d646cf1944bac1f276bbd62515475337eaa81c0f398d79888aca6dc18bcd06ebb7cee9e4a1d8e' + 'SKIP' '70e35154f9385156a11280294cc254cb314fd89653daedb46546a6bd64c550e3fc7e7a8df3e653c516687f0317bcc710fce82b4d649472333c501ed85fcd6cf6' + 'SKIP' '576fd826f28d945782840865f3e5b7c87d7f6f91f08e473ff0d06a37caa2f89cdd9cb4a7aafef5551363aab9c60e887ee794d5b6292874c12baf88c23ea5668e' - 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f') -b2sums_x86_64=('66da8f81bc90bd7d60cd1a5fc3cb3e721dd0d6d9ec8b82a6f0948cc9652ddbf9bef4d029d21694b346fe18e87e1b14067ea265c354a3adb020861a15131e1232') + 'SKIP' + 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f' + 'SKIP') +b2sums_x86_64=('f69a306213a769710e3429817239746406e115af29f34f37514119eb49f973c86953379b68d3df89700bef7f86e3ec43eb91d5f5821f2a8eae7f28dba589d265') # https://www.kernel.org/pub/linux/kernel/v7.x/sha256sums.asc sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '95f3e9209629044028373af987dc0e270a5a15acb8c8e49c5f05057220c75fe2' + 'SKIP' + '6cf74fc3f28a751354187b6cf610c3e43cf9088b5f730b68dbb575828c3a7478' + 'SKIP' 'daf0aaebff3cf4679d0bb8137ace6c9a2de62f75aa4655a0a02ddf759f3c7f26' + 'SKIP' 'a9171e731d08a454a50174889af8936fab962b33c37c67169b0cdf21b0b80821' + 'SKIP' '413e9994d35bc722cfe45752ab60a1bd8454443170d80987e6a5be6b7b745940' + 'SKIP' '01b9087c0cebf17d0ffbbbdd6025c9937fcdfa00c8672d310786ca3c3ce6daf9' + 'SKIP' '5c776365d391a15c23afb3969183449852f795f42355629ac563abf7ae5e4735' + 'SKIP' '3a6aaf3b29eb5b4018c5703626311cd1c7a61bd7100789633efc6a79a9b3247b' + 'SKIP' 'b28d9b548407f0da4280f4f9a6c0e92db6f3a32a35c7668471b92096eb7b093c' + 'SKIP' '9492d7db11fd327ff06a52cad945bf8ff6dfcabec72c435bf02b4432ee67475e' + 'SKIP' '66af19c43c068144699183a17b67bcf4df03578937c4cffc5d8b24e2e2828f7b' + 'SKIP' '267506a02ac723ea37adcc7c851051edbd1ed731c6aaea4c9e590aadc207855c' + 'SKIP' '07baad05112a47cb0aea81191502c87e496e13b4f23a6ec36ea65bb4219c35a8' + 'SKIP' '720720067a21e8c57d2619f4c0b46846ac060ec6db97088634d62ab8628c70a9' + 'SKIP' '3d1299d19aa9fbc96d25f67154009813e05e92340340953ac8355649cff837a4' + 'SKIP' 'dac26fb42f5df71b30c46b3c5894c6016af5a283c57b45173386767ca53b1bf8' + 'SKIP' '66eb2c49dbf708cc781d411d0e44ed613480f0ef66d522272929d9cb7bb21e93' + 'SKIP' + '718b40e15350049e03c0ed281b9257fca5432b8dec2e0e145e8b26507ee8892d' + 'SKIP' 'b7ba949eea77e169aceb2c401d4eb83a7413aab6f15ee2a7ab1a96352f0aa12d' + 'SKIP' '341424b925d506869793686e22ea8f095a34048595ebebec409c0a7bbd346ca2' + 'SKIP' '7c25d0a53a115bc1f072ff0cfdd9aa88858e754ae8b15026a2582656e8bd9c43' + 'SKIP' '79799f12cdf42de1cf6c5d483896b439d77bc2e582e10c7215f5375a76612a72' + 'SKIP' '5e3b455024fab2855b590091c9d14b0d12d5363f3d4bf025b31b9aa88b65eba5' + 'SKIP' 'abd8c9326cecc9bb85db4b3dc98ddcb66306b34a6d84fb05d0805aed537e2dc4' + 'SKIP' '60a9df54821cd98581e7476f8a7bb6cb0819762bdbed4d356332e41d164a8182' + 'SKIP' '929108ae0a8eb4782cafdd89e3d4426d02d86f2c2102ed99e760e31f0bc3794a' + 'SKIP' 'e1226c836a9c2fb6daff109e56ba21c13fd198e109cbb181de920af04fe153e6' + 'SKIP' '8d60f7dd26ef904419aa966427a9af2f2c9a079bb092ea0b58274ab905f6047a' + 'SKIP' '258597006eb96e1fb185ec9303ceb48148eedce95608b57900383f6df59d2989' + 'SKIP' 'f935409f9aeba3314adfad7db1b3d895557a07029a80d10969804e49c0e270ca' + 'SKIP' 'd8e64b9da3beab7c33832c019da50d0977b39dc32ec1f649a7c3bc93a2fdb9a5' + 'SKIP' '1780f66b157de6d249301331e31b7505f6e6fa26686fa4c618623567e4b758ae' + 'SKIP' '2501c98aaea6a53eeaa18d696f60b677e710d0f2d89f7525e47ba806de1878b3' + 'SKIP' 'e6b770d37e80509ed7ec5d8b59b4d2e5d6dba54ba659d7392de3b567f1eb6e54' + 'SKIP' 'b8596b5b5b546fdc309e10326b0e2ccea5a0538d4919630ed2845cded78580aa' + 'SKIP' '5cc215215fed4247c2d6b9732deff586331a07d54c3b00538a81921093a3a1a1' + 'SKIP' 'e303da14a3c8a15fd1cb45394138f03dd16e66d2ac0d1ebcd933977df937d771' + 'SKIP' + '503eba8d7a80b978fccdaac798f57cbb6cd2b2b04fd04c46399daa004b6646dc' + 'SKIP' '1f0c958b64ba48b8dc8b5548e1ad1934b8d4903fc16fec15014759e3681ea275' + 'SKIP' '47994d576008a377de612ed77e6b2e7bc6b24ad3a30a6d157646d380ae323142' + 'SKIP' 'd9eb3717ba98e270b0a998f8dd90cbda072f63b6a6ea8f66763bde3ea64e591d' + 'SKIP' '1e2fddb2e0c5d184a2e8c774886cf9be2f2f292e4f05832b50e5b49c48a7bacc' + 'SKIP' 'b9b8771b6a8bd7128f4344ad189e32c155abf72abd3ff646dcf5c78b08eb94c4' + 'SKIP' 'f62837e8ee51070f8139f718e0e80a4ce0c0dea9ce2328c10c63403aca5b3d2e' + 'SKIP' 'cae59282e3d1afd69f4f5dc00a180c712ef464c98b8fd7ccc9b03de944dc79b9' + 'SKIP' '5a74c8b2ba11e5876eeeb5ba530ae97e384b48943fa7a31e13396ecb82c5a8ce' + 'SKIP' '8b954ea37a2190022064c82688bc8cb1cf79a90598f078035be7d4041003cda9' + 'SKIP' '40ac31789399a5964c4fd820d4cdc7214add91109bbcf4a614619e5d8b367441' + 'SKIP' '707460d08c15f451d0a0e567c6e35522f3e1409bbe3bfbcf7160f33af06391b7' + 'SKIP' 'eebbe50deab379d09b5149ef30769ad6dfa11109696934ee66b108ec406da942' + 'SKIP' 'dab92dbc6eac02771c72801d94e6a53aabcf629a9cd79e85e0ce2ba44a65b06b' + 'SKIP' '30e2fc90c950869ab2fe1535b609f35dc1d46d6b6816de4afcbf687dbc224f0a' + 'SKIP' '1fdf8c4d8ee07c02883b827a11a84c1e5f70570545b8a4a232f6b59dd02049fa' + 'SKIP' '0057dff07a3ce9c7b7085edd1f912958e01ce8f7400151d55dfb74c0c6a084e2' + 'SKIP' '2762f72620781a672d9a21bcc591b862b04a756b271cd1009a7390eb1b77635e' + 'SKIP' '541325476efee83232ab8a6c62c14cf3c3c957c49cf053ed2869f7780f86d032' + 'SKIP' '8a8eb0934424ab6312f26e03a06be5d0401360ee60a20a87b11f7c3c6590103c' + 'SKIP' 'f7ff13ea46359217d696c6ccd49bc3d74c441ac5893d5151d95138017b0d10aa' + 'SKIP' 'd17493b3579c730d27dc723447175b70d991f50253c9fd419df5e923bdf7a244' + 'SKIP' '92971bb4dbde209cf170838cfcd75d8ac9978239821d3493c28cc12623c2bb60' + 'SKIP' '7245c6e2cb5c0b0ddf800894f087770cbf8ccc6f8b3f53b9a21c704e9dfa42c2' + 'SKIP' '135140bce40644079be7743b51f8618fc829a8868ac53137050f76c45eca4061' + 'SKIP' 'e6ec816ae309445c6c11e3433c04d0c9100e16c0e23268226d13c19e7be49edb' + 'SKIP' '29c61ec984bd342113b3c4310a3d5fddfd6d7db83847cfb26b84cc730385320a' + 'SKIP' '53425998005a4115bdf6001b9ffbe62c66bb2db205994b2e13005a3417ea6cbb' + 'SKIP' 'c28fca9ba015e96599627acf42e52737a6a846bdcbfd3d4af4147eda3109e12d' + 'SKIP' '5ce97dadb9eb1a8b0da41a8446ee74e01a5a7e7ab439bbea7a711318f04ae24f' + 'SKIP' '44f1c7bd18ea7f8772ce41e24dddd5f314e416ec8dfac6afe1ce8680c1bb4903' + 'SKIP' '0bb8ae5b6f23e13c60953eb5bbc8939e8f6c8faf2b216a20203af3b6f0217134' + 'SKIP' '907c92ef681154d10959a5f0cbe2a636688036990ba42e059af00cc2311b4be4' + 'SKIP' '534e68a989d36823f14a10299803f294eef39391619b4c329d78c20539d9fcd5' + 'SKIP' '516e6ec51ca0330989128a4eb2a8f6785e5d45d930899cbbbf37b8147ddfd854' + 'SKIP' 'bda3d6842f15998ce3f3e33d0e6e420df9c9c2abcba13716af478720c5933753' + 'SKIP' '3e7e7dba1ca88930c2c3f0d5411ddcee8c2e12fca59d859e2383dccaf9a08022' + 'SKIP' '3a271bc6b0219152047fb2b78d9e695eb791b796ea6d8233096912c77d62a5c0' + 'SKIP' '4e80f2d05591175d4ed0a1a144509e97260e5593472aae586c07956215d99162' + 'SKIP' '8ebce8f38e38bd66879d040028c2448e5bc7dcde9df0ccee2b4221015591b162' + 'SKIP' '0b0e05b615ae6eb825bf7d7fa568f551b0dff6c2cee62c9285c1b0d63224ea1c' + 'SKIP' '2e1e2a0eb039ce61c1612bf6c7ab5227b99df053bcd5592da7758715dd333fda' + 'SKIP' '17de1f76da3db42dc3c7829a22953cd2c6de916999d809e3fb0bbb741753ec8f' + 'SKIP' 'd90cc6570a47c8754e4c5d45834e0434c0a1fcabda9b8933b1fba84e3dabc083' + 'SKIP' '028bf538c870465e1752514725372fa9eef6b7178d9b8d1e2ecc15edc5a63cc8' + 'SKIP' 'a92a84405ee9845738c6b52d8f0f0eca16eb40afe0ebe0f4e9e1168597675311' + 'SKIP' '44a0b10a6dc83465ea86f5fba936b2bfbeffb1b3f0b2e04013bfed99ba4f6c5a' + 'SKIP' '229b28cee6f2b8afb5888eed453ce7f82de033900383bd91957339007c9e6cbf' + 'SKIP' 'c5d61db05b19fde06102b0b3dbd76e1989c8f07f3b7d175176bd2d73f1a71e5c' + 'SKIP' '5988d7a37b4b71a64ed09fea872f08515a3c7029d33fd053a6a1822bb811ddb5' + 'SKIP' '1ec4fb93700b0696ba2c2a371f218f13b32dcc745523c7ee26a5c3cd7af253fc' + 'SKIP' 'c67fee35a42866dd3c9056ff099fc504cdad9530c107d959331b5572b21af818' + 'SKIP' '36cb306751c57de3e61c7a4cd18a7f131eef0dc1de15a4e28751d1e5b08bace8' + 'SKIP' '07683e522d6dea3d3ce658ee80bf4317f6c3caf248259da960bc4610f4d1e807' + 'SKIP' '66a0933d2d4f2edc8999b6fdf0fe10b5b607646fd6f1a1d534bf26270543f8f1' + 'SKIP' 'c5279468d93e562f509a6b599a2b7ca7ee0032d3fe35314c5769fc6c9a9234ab' + 'SKIP' 'c0111614ec44014cbf621eedf5a4ef302a855423bc1633ee692392950e971d0e' - '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb') + 'SKIP' + '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb' + 'SKIP') export KBUILD_BUILD_HOST=omarchy export KBUILD_BUILD_USER=$pkgbase diff --git a/pkgbuilds/linux-omarchy-bore/config.x86_64 b/pkgbuilds/linux-omarchy-bore/config.x86_64 index 2a1639e..27b8b44 100644 --- a/pkgbuilds/linux-omarchy-bore/config.x86_64 +++ b/pkgbuilds/linux-omarchy-bore/config.x86_64 @@ -278,8 +278,9 @@ CONFIG_BOOT_CONFIG=y # CONFIG_BOOT_CONFIG_EMBED is not set CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=1021 CONFIG_INITRAMFS_PRESERVE_MTIME=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_LD_ORPHAN_WARN=y CONFIG_LD_ORPHAN_WARN_LEVEL="warn" CONFIG_SYSCTL=y @@ -11233,7 +11234,8 @@ CONFIG_USB4_STREAM=m # Android # # CONFIG_ANDROID_BINDER_IPC is not set -# CONFIG_ANDROID_BINDER_IPC_RUST is not set +CONFIG_ANDROID_BINDER_IPC_RUST=y +CONFIG_ANDROID_BINDER_DEVICES="" # end of Android CONFIG_LIBNVDIMM=m @@ -11871,7 +11873,7 @@ CONFIG_INIT_STACK_ALL_ZERO=y CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y # CONFIG_INIT_ON_FREE_DEFAULT_ON is not set CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y -CONFIG_ZERO_CALL_USED_REGS=y +# CONFIG_ZERO_CALL_USED_REGS is not set # end of Memory initialization # diff --git a/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig new file mode 100644 index 0000000..c790810 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch b/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch new file mode 100644 index 0000000..9506ee1 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch @@ -0,0 +1,31 @@ +diff --git a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -935,6 +935,9 @@ KBUILD_RUSTFLAGS += -Copt-level=2 + else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE + KBUILD_CFLAGS += -Os + KBUILD_RUSTFLAGS += -Copt-level=s ++else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3 ++KBUILD_CFLAGS += -O3 ++KBUILD_RUSTFLAGS += -Copt-level=3 + endif + + # Always set `debug-assertions` and `overflow-checks` because their default +diff --git a/init/Kconfig b/init/Kconfig +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1622,6 +1622,14 @@ config CC_OPTIMIZE_FOR_SIZE + Choosing this option will pass "-Os" to your compiler resulting + in a smaller kernel. + ++config CC_OPTIMIZE_FOR_PERFORMANCE_O3 ++ bool "Optimize harder for performance (-O3)" ++ help ++ Build with the "-O3" compiler flag: more inlining, loop ++ unrolling and vectorization than -O2, at the cost of a larger ++ kernel image and larger modules. Rust code is built at ++ opt-level 3. ++ + endchoice + + config HAVE_LD_DEAD_CODE_DATA_ELIMINATION diff --git a/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch.sig b/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch.sig new file mode 100644 index 0000000..90d5219 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0011-kbuild-optimize-for-performance-o3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig b/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig new file mode 100644 index 0000000..b8fcd47 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0121-smp-preempt.patch.sig b/pkgbuilds/linux-omarchy/0121-smp-preempt.patch.sig new file mode 100644 index 0000000..81da846 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0121-smp-preempt.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig new file mode 100644 index 0000000..88fd720 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig b/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig new file mode 100644 index 0000000..a9f0fa6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig new file mode 100644 index 0000000..2e7bcd9 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig new file mode 100644 index 0000000..4983403 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig b/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig new file mode 100644 index 0000000..d96b7f9 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig new file mode 100644 index 0000000..b191450 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig new file mode 100644 index 0000000..0a059ef Binary files /dev/null and b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0145-sched-eevdf-tunables.patch.sig b/pkgbuilds/linux-omarchy/0145-sched-eevdf-tunables.patch.sig new file mode 100644 index 0000000..efd3bb2 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0145-sched-eevdf-tunables.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0200-idle.patch.sig b/pkgbuilds/linux-omarchy/0200-idle.patch.sig new file mode 100644 index 0000000..5998b39 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0200-idle.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0210-pstate.patch.sig b/pkgbuilds/linux-omarchy/0210-pstate.patch.sig new file mode 100644 index 0000000..1d30612 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0210-pstate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig b/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig new file mode 100644 index 0000000..7f218b5 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig b/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig new file mode 100644 index 0000000..7371ce6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch b/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch new file mode 100644 index 0000000..1c5c5b3 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch @@ -0,0 +1,95 @@ +diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h +--- a/arch/x86/include/asm/cpufeatures.h ++++ b/arch/x86/include/asm/cpufeatures.h +@@ -471,6 +471,8 @@ + #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* Automatic IBRS */ + #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* SMM_CTL MSR is not present */ + ++#define X86_FEATURE_L2_TLB_SIZE_X32 (20*32+14) /* L2 TLB sizes are encoded as multiples of 32 */ ++ + #define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */ + + #define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */ +diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c +--- a/arch/x86/kernel/cpu/amd.c ++++ b/arch/x86/kernel/cpu/amd.c +@@ -1190,7 +1190,7 @@ static unsigned int amd_size_cache(struct cpuinfo_x86 *c, unsigned int size) + + static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + { +- u32 ebx, eax, ecx, edx; ++ u32 ebx, eax, ecx, edx, shift, tmp; + u16 mask = 0xfff; + + if (c->x86 < 0xf) +@@ -1199,10 +1199,12 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + if (c->extended_cpuid_level < 0x80000006) + return; + ++ shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; ++ + cpuid(0x80000006, &eax, &ebx, &ecx, &edx); + +- tlb_lld_4k = (ebx >> 16) & mask; +- tlb_lli_4k = ebx & mask; ++ tlb_lld_4k = ((ebx >> 16) & mask) << shift; ++ tlb_lli_4k = (ebx & mask) << shift; + + /* + * K8 doesn't have 2M/4M entries in the L2 TLB so read out the L1 TLB +@@ -1214,16 +1216,18 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + } + + /* Handle DTLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ +- if (!((eax >> 16) & mask)) ++ tmp = ((eax >> 16) & mask) << shift; ++ if (!tmp) + tlb_lld_2m = (cpuid_eax(0x80000005) >> 16) & 0xff; + else +- tlb_lld_2m = (eax >> 16) & mask; ++ tlb_lld_2m = tmp; + + /* a 4M entry uses two 2M entries */ + tlb_lld_4m = tlb_lld_2m >> 1; + + /* Handle ITLB 2M and 4M sizes, fall back to L1 if L2 is disabled */ +- if (!(eax & mask)) { ++ tmp = (eax & mask) << shift; ++ if (!tmp) { + /* Erratum 658 */ + if (c->x86 == 0x15 && c->x86_model <= 0x1f) { + tlb_lli_2m = 1024; +@@ -1231,8 +1235,9 @@ static void cpu_detect_tlb_amd(struct cpuinfo_x86 *c) + cpuid(0x80000005, &eax, &ebx, &ecx, &edx); + tlb_lli_2m = eax & 0xff; + } +- } else +- tlb_lli_2m = eax & mask; ++ } else { ++ tlb_lli_2m = tmp; ++ } + + tlb_lli_4m = tlb_lli_2m >> 1; + +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -857,7 +857,7 @@ static void get_model_name(struct cpuinfo_x86 *c) + + void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) + { +- unsigned int n, dummy, ebx, ecx, edx, l2size; ++ unsigned int n, dummy, ebx, ecx, edx, l2size, shift __maybe_unused; + + n = c->extended_cpuid_level; + +@@ -877,7 +877,9 @@ void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) + l2size = ecx >> 16; + + #ifdef CONFIG_X86_64 ++ shift = !!cpu_has(c, X86_FEATURE_L2_TLB_SIZE_X32) * 5; + c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); ++ c->x86_tlbsize <<= shift; + #else + /* do processor-specific cache resizing */ + if (this_cpu->legacy_cache_size) diff --git a/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch.sig b/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch.sig new file mode 100644 index 0000000..aa440e0 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0220-x86-amd-zen5-tlb-sizes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0250-zsmalloc.patch.sig b/pkgbuilds/linux-omarchy/0250-zsmalloc.patch.sig new file mode 100644 index 0000000..d6828f5 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0250-zsmalloc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0260-mglru-exec-protect.patch.sig b/pkgbuilds/linux-omarchy/0260-mglru-exec-protect.patch.sig new file mode 100644 index 0000000..7023e21 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0260-mglru-exec-protect.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0270-ksm-rmap-walk.patch.sig b/pkgbuilds/linux-omarchy/0270-ksm-rmap-walk.patch.sig new file mode 100644 index 0000000..3262347 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0270-ksm-rmap-walk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig b/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig new file mode 100644 index 0000000..4e1b338 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0290-zstd-bmi2-fallback-aliases.patch.sig b/pkgbuilds/linux-omarchy/0290-zstd-bmi2-fallback-aliases.patch.sig new file mode 100644 index 0000000..6707301 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0290-zstd-bmi2-fallback-aliases.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig b/pkgbuilds/linux-omarchy/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig new file mode 100644 index 0000000..e39aa6c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0291-zstd-bmi2-cpu-feature-dispatch.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0292-crypto-zstd-defer-cstream-init.patch.sig b/pkgbuilds/linux-omarchy/0292-crypto-zstd-defer-cstream-init.patch.sig new file mode 100644 index 0000000..eb7a182 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0292-crypto-zstd-defer-cstream-init.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0293-crypto-zstd-defer-dstream-init.patch.sig b/pkgbuilds/linux-omarchy/0293-crypto-zstd-defer-dstream-init.patch.sig new file mode 100644 index 0000000..697fca0 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0293-crypto-zstd-defer-dstream-init.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0295-af-alg-restrict.patch.sig b/pkgbuilds/linux-omarchy/0295-af-alg-restrict.patch.sig new file mode 100644 index 0000000..0a98371 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0295-af-alg-restrict.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig b/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig new file mode 100644 index 0000000..b01f81d Binary files /dev/null and b/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig b/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig new file mode 100644 index 0000000..60dbc9e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig b/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig new file mode 100644 index 0000000..53ac4a1 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0302-btrfs-zstd-decompress-direct-to-page.patch.sig b/pkgbuilds/linux-omarchy/0302-btrfs-zstd-decompress-direct-to-page.patch.sig new file mode 100644 index 0000000..a75cf51 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0302-btrfs-zstd-decompress-direct-to-page.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig new file mode 100644 index 0000000..c65855e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig new file mode 100644 index 0000000..ce13d31 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0312-fuse-writethrough-uptodate.patch.sig b/pkgbuilds/linux-omarchy/0312-fuse-writethrough-uptodate.patch.sig new file mode 100644 index 0000000..391ede6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0312-fuse-writethrough-uptodate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig new file mode 100644 index 0000000..e161277 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig b/pkgbuilds/linux-omarchy/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig new file mode 100644 index 0000000..f46a1a4 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0360-gpu-mem-cgroup.patch.sig b/pkgbuilds/linux-omarchy/0360-gpu-mem-cgroup.patch.sig new file mode 100644 index 0000000..ddced6d Binary files /dev/null and b/pkgbuilds/linux-omarchy/0360-gpu-mem-cgroup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch new file mode 100644 index 0000000..6977a84 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c +--- a/drivers/gpu/drm/ttm/ttm_bo.c ++++ b/drivers/gpu/drm/ttm/ttm_bo.c +@@ -1343,7 +1343,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) + + if (ttm_tt_is_populated(tt)) { + ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); +- if (!ret) { ++ if (ret > 0) { + spin_lock(&bdev->lru_lock); + ttm_resource_del_bulk_move_unevictable(bo->resource, bo); + ttm_resource_move_to_lru_tail(bo->resource); diff --git a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig new file mode 100644 index 0000000..8ca1431 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig b/pkgbuilds/linux-omarchy/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig new file mode 100644 index 0000000..b591f47 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig b/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig new file mode 100644 index 0000000..4c3c1a0 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch.sig b/pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch.sig new file mode 100644 index 0000000..1b2c039 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0411-drm-xe-display-no-stolen-framebuffers.patch.sig b/pkgbuilds/linux-omarchy/0411-drm-xe-display-no-stolen-framebuffers.patch.sig new file mode 100644 index 0000000..5bc668b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0411-drm-xe-display-no-stolen-framebuffers.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0420-safe-window.patch.sig b/pkgbuilds/linux-omarchy/0420-safe-window.patch.sig new file mode 100644 index 0000000..4564638 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0420-safe-window.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0430-fbc.patch.sig b/pkgbuilds/linux-omarchy/0430-fbc.patch.sig new file mode 100644 index 0000000..f0db1f3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0430-fbc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0440-xe3-peak-bandwidth.patch.sig b/pkgbuilds/linux-omarchy/0440-xe3-peak-bandwidth.patch.sig new file mode 100644 index 0000000..711523e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0440-xe3-peak-bandwidth.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig new file mode 100644 index 0000000..ee35537 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig new file mode 100644 index 0000000..7077a0e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0452-amd-hdmi-frl-default.patch.sig b/pkgbuilds/linux-omarchy/0452-amd-hdmi-frl-default.patch.sig new file mode 100644 index 0000000..c507d82 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0452-amd-hdmi-frl-default.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig new file mode 100644 index 0000000..f76e652 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig new file mode 100644 index 0000000..d303e52 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0472-amdgpu-userq-post-reset-error.patch.sig b/pkgbuilds/linux-omarchy/0472-amdgpu-userq-post-reset-error.patch.sig new file mode 100644 index 0000000..5de4d85 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0472-amdgpu-userq-post-reset-error.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig b/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig new file mode 100644 index 0000000..4e8c059 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig new file mode 100644 index 0000000..14aa526 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig b/pkgbuilds/linux-omarchy/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig new file mode 100644 index 0000000..3ddfad0 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig b/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig new file mode 100644 index 0000000..cfb31fc Binary files /dev/null and b/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig b/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig new file mode 100644 index 0000000..d84e156 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig b/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig new file mode 100644 index 0000000..c02c1df Binary files /dev/null and b/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch.sig b/pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch.sig new file mode 100644 index 0000000..1ccaf57 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch.sig b/pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch.sig new file mode 100644 index 0000000..2b79ff3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig b/pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig new file mode 100644 index 0000000..0cfe941 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig b/pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig new file mode 100644 index 0000000..62063af Binary files /dev/null and b/pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig b/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig new file mode 100644 index 0000000..5f2c204 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig new file mode 100644 index 0000000..6ea6cfa Binary files /dev/null and b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig b/pkgbuilds/linux-omarchy/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig new file mode 100644 index 0000000..69eeeea Binary files /dev/null and b/pkgbuilds/linux-omarchy/0542-media-cvs-wake-irq-without-claiming-gpio.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0560-input.patch.sig b/pkgbuilds/linux-omarchy/0560-input.patch.sig new file mode 100644 index 0000000..643da0a Binary files /dev/null and b/pkgbuilds/linux-omarchy/0560-input.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0565-i2c-asue140d-touchpad-100khz.patch.sig b/pkgbuilds/linux-omarchy/0565-i2c-asue140d-touchpad-100khz.patch.sig new file mode 100644 index 0000000..d5acafd Binary files /dev/null and b/pkgbuilds/linux-omarchy/0565-i2c-asue140d-touchpad-100khz.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig new file mode 100644 index 0000000..dbe7b90 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig b/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig new file mode 100644 index 0000000..753b735 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig b/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig new file mode 100644 index 0000000..45711f6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig b/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig new file mode 100644 index 0000000..939524b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0620-usb-string-sanitize.patch.sig b/pkgbuilds/linux-omarchy/0620-usb-string-sanitize.patch.sig new file mode 100644 index 0000000..030b9fa Binary files /dev/null and b/pkgbuilds/linux-omarchy/0620-usb-string-sanitize.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0650-wireguard-tstamp-type.patch.sig b/pkgbuilds/linux-omarchy/0650-wireguard-tstamp-type.patch.sig new file mode 100644 index 0000000..4dfd43e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0650-wireguard-tstamp-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig new file mode 100644 index 0000000..ade8cc2 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig b/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig new file mode 100644 index 0000000..0459681 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig b/pkgbuilds/linux-omarchy/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig new file mode 100644 index 0000000..f2bc1c6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0700-pci-target-speed-quirk.patch.sig b/pkgbuilds/linux-omarchy/0700-pci-target-speed-quirk.patch.sig new file mode 100644 index 0000000..2b1e9cc Binary files /dev/null and b/pkgbuilds/linux-omarchy/0700-pci-target-speed-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig b/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig new file mode 100644 index 0000000..d3799b1 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig b/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig new file mode 100644 index 0000000..f0da512 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig b/pkgbuilds/linux-omarchy/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig new file mode 100644 index 0000000..8744164 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig b/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig new file mode 100644 index 0000000..1afc8bb Binary files /dev/null and b/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0801-amd-pmf-util-unbind-use-after-free.patch.sig b/pkgbuilds/linux-omarchy/0801-amd-pmf-util-unbind-use-after-free.patch.sig new file mode 100644 index 0000000..6bc45ff Binary files /dev/null and b/pkgbuilds/linux-omarchy/0801-amd-pmf-util-unbind-use-after-free.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0850-futex-wait-multiple.patch.sig b/pkgbuilds/linux-omarchy/0850-futex-wait-multiple.patch.sig new file mode 100644 index 0000000..c9e1f4e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0850-futex-wait-multiple.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0851-futex-wait-multiple-fixes.patch.sig b/pkgbuilds/linux-omarchy/0851-futex-wait-multiple-fixes.patch.sig new file mode 100644 index 0000000..e3fb191 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0851-futex-wait-multiple-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0852-futex-wait-multiple-abi-fixes.patch.sig b/pkgbuilds/linux-omarchy/0852-futex-wait-multiple-abi-fixes.patch.sig new file mode 100644 index 0000000..d3db2d8 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0852-futex-wait-multiple-abi-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig b/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig new file mode 100644 index 0000000..bbe1ce3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig b/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig new file mode 100644 index 0000000..0707c41 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig b/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig new file mode 100644 index 0000000..ddd9329 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig new file mode 100644 index 0000000..1b113b3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8205-mm-hint-uses-allocation-order.patch.sig b/pkgbuilds/linux-omarchy/8205-mm-hint-uses-allocation-order.patch.sig new file mode 100644 index 0000000..7c3ef42 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8205-mm-hint-uses-allocation-order.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig new file mode 100644 index 0000000..de65251 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8207-mm-classify-huge-page-allocations-as-failable.patch.sig b/pkgbuilds/linux-omarchy/8207-mm-classify-huge-page-allocations-as-failable.patch.sig new file mode 100644 index 0000000..e60df1b Binary files /dev/null and b/pkgbuilds/linux-omarchy/8207-mm-classify-huge-page-allocations-as-failable.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig b/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig new file mode 100644 index 0000000..88c17d4 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8209-xe-shrinker-use-opportunistic-hint.patch.sig b/pkgbuilds/linux-omarchy/8209-xe-shrinker-use-opportunistic-hint.patch.sig new file mode 100644 index 0000000..7928b4e Binary files /dev/null and b/pkgbuilds/linux-omarchy/8209-xe-shrinker-use-opportunistic-hint.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8210-xe-shrinker-single-backup-decision.patch.sig b/pkgbuilds/linux-omarchy/8210-xe-shrinker-single-backup-decision.patch.sig new file mode 100644 index 0000000..a823434 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8210-xe-shrinker-single-backup-decision.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig b/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig new file mode 100644 index 0000000..303bf7f Binary files /dev/null and b/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/PKGBUILD b/pkgbuilds/linux-omarchy/PKGBUILD index 3c6eb39..cc70049 100644 --- a/pkgbuilds/linux-omarchy/PKGBUILD +++ b/pkgbuilds/linux-omarchy/PKGBUILD @@ -3,7 +3,7 @@ pkgbase=linux-omarchy pkgver=7.2.5 -pkgrel=3 +pkgrel=6 pkgdesc='Omarchy Linux' url='https://omarchy.org' arch=( @@ -48,98 +48,100 @@ source=( https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/${_srcname}.tar.{xz,sign} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.xz} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.sign} - # Local patches are verified by the checksums below. - 0010-archlinux-base.patch - 0120-tlbpull.patch - 0121-smp-preempt.patch - 0130-sched-detach-tasks.patch - 0131-sched-avg-idle.patch - 0140-sched-always-inline.patch - 0141-sched-urgent-fixes.patch - 0142-sched-itmt-no-debugfs-dependency.patch - 0143-sched-hybrid-cluster-balancing.patch - 0144-sched-nohz-idle-core.patch - 0145-sched-eevdf-tunables.patch - 0200-idle.patch - 0210-pstate.patch - 0211-amd-pstate-fixes.patch - 0212-amd-pstate-epp-cache.patch - 0250-zsmalloc.patch - 0260-mglru-exec-protect.patch - 0270-ksm-rmap-walk.patch - 0280-mm-updates.patch - 0290-zstd-bmi2-fallback-aliases.patch - 0291-zstd-bmi2-cpu-feature-dispatch.patch - 0292-crypto-zstd-defer-cstream-init.patch - 0293-crypto-zstd-defer-dstream-init.patch - 0295-af-alg-restrict.patch - 0296-x86-mm-pmd-modify-keep-dirty-bit.patch - 0300-btrfs.patch - 0301-btrfs-fixes.patch - 0302-btrfs-zstd-decompress-direct-to-page.patch - 0310-fuse-eof-zeroing.patch - 0311-fuse-perf.patch - 0312-fuse-writethrough-uptodate.patch - 0313-fuse-background-wakeup.patch - 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch - 0360-gpu-mem-cgroup.patch - 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch - 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch - 0402-psr2-early-transport-panels.patch - 0411-drm-xe-display-no-stolen-framebuffers.patch - 0420-safe-window.patch - 0430-fbc.patch - 0440-xe3-peak-bandwidth.patch - 0450-amd-hdmi-vrr-allm.patch - 0451-amd-vtem-tmds-links.patch - 0452-amd-hdmi-frl-default.patch - 0460-vesa-displayid-dsc-bpp.patch - 0461-vesa-dsc-passthru-mode-match-fix.patch - 0472-amdgpu-userq-post-reset-error.patch - 0473-i915-ptl-cdclk-sanitize.patch - 0474-amd-display-oled-vesa-backlight.patch - 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch - 0510-sound-updates.patch - 0511-sound-updates-fixes.patch - 0512-sound-fixes.patch - 0513-xps13-sof-quirk.patch - 0514-rt766-stream-config-type.patch - 0516-hda-realtek-rog-strix-g733zw-speakers.patch - 0517-asoc-amd-yc-acer-aspire-a314-23p.patch - 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch - 0541-cvs-nova-lake-acpi-id.patch - 0542-media-cvs-wake-irq-without-claiming-gpio.patch - 0560-input.patch - 0565-i2c-asue140d-touchpad-100khz.patch - 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch - 0600-usb4stream-fixes.patch - 0601-usb4stream-busy-poll.patch - 0610-typec-cable-altmode-check.patch - 0620-usb-string-sanitize.patch - 0650-wireguard-tstamp-type.patch - 0660-btusb-mediatek-mt7922-13d3-3625.patch - 0661-rtw89-command-offload-source.patch - 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch - 0700-pci-target-speed-quirk.patch - 0750-applesmc-cache-race.patch - 0751-applesmc-key-backlight-workqueue-leak.patch - 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch - 0800-platform-updates.patch - 0801-amd-pmf-util-unbind-use-after-free.patch - 0850-futex-wait-multiple.patch - 0851-futex-wait-multiple-fixes.patch - 0852-futex-wait-multiple-abi-fixes.patch - 8201-xe-shrinker-return-freed-page-count.patch - 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch - 8203-xe-shrinker-release-through-the-put-helper.patch - 8204-mm-opportunistic-compaction.patch - 8205-mm-hint-uses-allocation-order.patch - 8206-mm-carry-order-and-hint-in-one-word.patch - 8207-mm-classify-huge-page-allocations-as-failable.patch - 8208-mm-thp-deferred-split-uses-hint.patch - 8209-xe-shrinker-use-opportunistic-hint.patch - 8210-xe-shrinker-single-backup-decision.patch - 9999-bpftool-strip-wformat-bootstrap.patch + 0010-archlinux-base.patch{,.sig} + 0011-kbuild-optimize-for-performance-o3.patch{,.sig} + 0120-tlbpull.patch{,.sig} + 0121-smp-preempt.patch{,.sig} + 0130-sched-detach-tasks.patch{,.sig} + 0131-sched-avg-idle.patch{,.sig} + 0140-sched-always-inline.patch{,.sig} + 0141-sched-urgent-fixes.patch{,.sig} + 0142-sched-itmt-no-debugfs-dependency.patch{,.sig} + 0143-sched-hybrid-cluster-balancing.patch{,.sig} + 0144-sched-nohz-idle-core.patch{,.sig} + 0145-sched-eevdf-tunables.patch{,.sig} + 0200-idle.patch{,.sig} + 0210-pstate.patch{,.sig} + 0211-amd-pstate-fixes.patch{,.sig} + 0212-amd-pstate-epp-cache.patch{,.sig} + 0220-x86-amd-zen5-tlb-sizes.patch{,.sig} + 0250-zsmalloc.patch{,.sig} + 0260-mglru-exec-protect.patch{,.sig} + 0270-ksm-rmap-walk.patch{,.sig} + 0280-mm-updates.patch{,.sig} + 0290-zstd-bmi2-fallback-aliases.patch{,.sig} + 0291-zstd-bmi2-cpu-feature-dispatch.patch{,.sig} + 0292-crypto-zstd-defer-cstream-init.patch{,.sig} + 0293-crypto-zstd-defer-dstream-init.patch{,.sig} + 0295-af-alg-restrict.patch{,.sig} + 0296-x86-mm-pmd-modify-keep-dirty-bit.patch{,.sig} + 0300-btrfs.patch{,.sig} + 0301-btrfs-fixes.patch{,.sig} + 0302-btrfs-zstd-decompress-direct-to-page.patch{,.sig} + 0310-fuse-eof-zeroing.patch{,.sig} + 0311-fuse-perf.patch{,.sig} + 0312-fuse-writethrough-uptodate.patch{,.sig} + 0313-fuse-background-wakeup.patch{,.sig} + 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch{,.sig} + 0360-gpu-mem-cgroup.patch{,.sig} + 0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch{,.sig} + 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch{,.sig} + 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch{,.sig} + 0402-psr2-early-transport-panels.patch{,.sig} + 0411-drm-xe-display-no-stolen-framebuffers.patch{,.sig} + 0420-safe-window.patch{,.sig} + 0430-fbc.patch{,.sig} + 0440-xe3-peak-bandwidth.patch{,.sig} + 0450-amd-hdmi-vrr-allm.patch{,.sig} + 0451-amd-vtem-tmds-links.patch{,.sig} + 0452-amd-hdmi-frl-default.patch{,.sig} + 0460-vesa-displayid-dsc-bpp.patch{,.sig} + 0461-vesa-dsc-passthru-mode-match-fix.patch{,.sig} + 0472-amdgpu-userq-post-reset-error.patch{,.sig} + 0473-i915-ptl-cdclk-sanitize.patch{,.sig} + 0474-amd-display-oled-vesa-backlight.patch{,.sig} + 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch{,.sig} + 0510-sound-updates.patch{,.sig} + 0511-sound-updates-fixes.patch{,.sig} + 0512-sound-fixes.patch{,.sig} + 0513-xps13-sof-quirk.patch{,.sig} + 0514-rt766-stream-config-type.patch{,.sig} + 0516-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} + 0517-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} + 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch{,.sig} + 0541-cvs-nova-lake-acpi-id.patch{,.sig} + 0542-media-cvs-wake-irq-without-claiming-gpio.patch{,.sig} + 0560-input.patch{,.sig} + 0565-i2c-asue140d-touchpad-100khz.patch{,.sig} + 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch{,.sig} + 0600-usb4stream-fixes.patch{,.sig} + 0601-usb4stream-busy-poll.patch{,.sig} + 0610-typec-cable-altmode-check.patch{,.sig} + 0620-usb-string-sanitize.patch{,.sig} + 0650-wireguard-tstamp-type.patch{,.sig} + 0660-btusb-mediatek-mt7922-13d3-3625.patch{,.sig} + 0661-rtw89-command-offload-source.patch{,.sig} + 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch{,.sig} + 0700-pci-target-speed-quirk.patch{,.sig} + 0750-applesmc-cache-race.patch{,.sig} + 0751-applesmc-key-backlight-workqueue-leak.patch{,.sig} + 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch{,.sig} + 0800-platform-updates.patch{,.sig} + 0801-amd-pmf-util-unbind-use-after-free.patch{,.sig} + 0850-futex-wait-multiple.patch{,.sig} + 0851-futex-wait-multiple-fixes.patch{,.sig} + 0852-futex-wait-multiple-abi-fixes.patch{,.sig} + 8201-xe-shrinker-return-freed-page-count.patch{,.sig} + 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch{,.sig} + 8203-xe-shrinker-release-through-the-put-helper.patch{,.sig} + 8204-mm-opportunistic-compaction.patch{,.sig} + 8205-mm-hint-uses-allocation-order.patch{,.sig} + 8206-mm-carry-order-and-hint-in-one-word.patch{,.sig} + 8207-mm-classify-huge-page-allocations-as-failable.patch{,.sig} + 8208-mm-thp-deferred-split-uses-hint.patch{,.sig} + 8209-xe-shrinker-use-opportunistic-hint.patch{,.sig} + 8210-xe-shrinker-single-backup-decision.patch{,.sig} + 9999-bpftool-strip-wformat-bootstrap.patch{,.sig} ) source_x86_64=(config.x86_64) validpgpkeys=( @@ -150,192 +152,386 @@ validpgpkeys=( b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97ab6c656ea8e5b29f2c4a8fe4cc143a626f4cca6b972a8105991e4c6905' 'SKIP' '6d92fb81077232b8cd1ae500b3aabc71434792e750ea6126dc095e50d4278d3e42b3cfdc3c8bc693ea14b109f6adfa3a1ed095187fc5501962fbe8f049f867bc' + 'SKIP' + 'cc2a66a097b5567e80c59b7d6c492fef5e667fc71a0390908712c69512c4136dbaf3d150b62d04eff960c796f7a94e6d0915d3b92cb336318b49f5f516a3bba6' + 'SKIP' 'f6c00ac2400580dffe3605d2693809396e18185f0c59b21b86f1f22a1f8d6529c51e8cc2474bc40f74a96631f415da51521a8b35d5b096e9b9d164c2755fb091' + 'SKIP' '6aa6ea3690f3e4e145818650537cd110d33720d662af0b3660d95e15a3f91e48c993e31117c0a2fde852f55a77a83e3a50a1a95c0df78a33ae75d43d46a9fe15' + 'SKIP' 'f664077b274be12da33f0145ebea8fafda3f3e3bf96f449c31b9a56768d39b763c246394ee4ed144f7f2d1916c32085fa59dc18736bb4a28c94568e3e1919d56' + 'SKIP' '7fb673924d268c0e3c416c7aa6200e96d62d156eb397832de12668b8a90d6b9c7311f1958e0f2a6d93de8e82fa3166082106c2bcbaaf42bb11b13bb83d6f732d' + 'SKIP' '42597d4f8377d6f4c58c26c415b0621db16a37da89bc1eb079750eef1c3f7bf04c9a2ac6f334d6a3c4c439827df4aa1b92d53c15f6e3857f79d8be7eb79ea7bd' + 'SKIP' '35075dae2576cb2b8f94df048bbd4fc6b667fc176310306f8939cf9263a50a9a558102af99c90746fa7b17d98c6a321c0cc36b2bdffab5fa714e3e1256be889b' + 'SKIP' '3434a5bbcd2bd8b4e2013400bf4d771ad5c8295ec74f67ff7acaf917954a01d25931679a71ddb8995fd7703b0b9904db4e6fe5f384d3705cf4d7b80dbf5dba55' + 'SKIP' 'bcf83ace482aa07e64ef6cdc1d6ae9b8f52ef79c8d15421e2d8525b1fcd3bd555be49fee48a87c2daa103a5f6a564046568722f03bf768f73dbd280acf59376d' + 'SKIP' '38d2d63ca4eee7ce83d22e60ebeb9d7f41a019cf910a8a7d151e4a1eccb14478a0054e72ae6494c89e2d66effead44e59534899fd861fe05a68460ae2d2cd32a' + 'SKIP' '64a2e489285c40df691f822dbca67e2b2f21446f300465f01bb589380173f6da182eda71d09a840e7ea3e14acae940d3cde66fc3b476ef6417c6e8479841cc56' + 'SKIP' 'd1007ee452cdf92967ec7055f1f39a3007a69842040e12f5063a1d2f24fd6d1eeaff2357abf96d0e330541a4781f93fecbcb116a4c73371913582eb72b8f9466' + 'SKIP' '0d3309140e496ad552d1a15d4fbf04b5ad03877bc73ae689f57554e91b7458578f2c88b6bbd37dd9fdf822a8d48c2cd6f961ec735ae5887f96fa51d908d203ee' + 'SKIP' '13beae6f8d23a1f075410f50c09bc94f24f505571af1feb3cee58f732c0d649a9ce5939bc11642c4f3c8dfbd967939b2a0a205220abf06f10d559c4c43a946a3' + 'SKIP' '2f24f88d8f960148046c36222fdaaadfed2ca88c60240fa1d111430898192ca45aeaa93a3afa24ad02b11c4dc8c3904a92f17c0284396f2f43352ad934a35056' + 'SKIP' + 'e59c4da548b2ea4fd6144886e3e2e765b3e73972121e9e73dbc05c066834b2b35e6ef4c17617e77efc90260566bf7c0910d2138d8d5c1c3b6094eeac9de64cfe' + 'SKIP' '9809e258eb684b80d508fa2f2fa270683b62bd1412ed05a970114aaa7d84c37c0beaea51f9ff8c6ee1bc7318c4aee3725e9a1131fefb51ba700801de6ac414c5' + 'SKIP' 'f115e8c80d723f7c24e6b1638b801cd664bef20acb1d2f6e6f5612744b916278693011782228cd3f4d0518654b8d53309bc5b06bbe8ed722fab9995f369bf548' + 'SKIP' '31abdd15ae3f9709d6912c90319a0b62d8380e1ae1169575c0e8e419f8ce264a7cf7c72558a85719cd5a1cae7341007adb3e3936268999ebd5215c2ed288f22c' + 'SKIP' '4efb9ab5258aee01b07bc322b160d2f6e3571ff82c4f3e317cab152a919c67522629d56fb79d517da034373cd6924bc3a20c98073197c93590feb1a34e902358' + 'SKIP' '55951c59286df43f069302b0b1036424812f80b45c5ea13219fce1bfc3e831731432c216288d722e44493c736cbf89a3e44801b5a4f1f242c17b5a89b236a5ef' + 'SKIP' '737db39c2f7f46550de4d6d80659bd6c1fc2bf1ca42bd7bd6fc345fc90af995acd056051e12175ac6c957333629a017051d76d5359c871158c93f0a294fb8de5' + 'SKIP' '4d0646c8ad139add6abb62d75308d2d7b5b07afbc74a0f0297ffc2148f0db38abaea16b048704187da49fe3ddee8370b10a7a41d6e62b665650cd79e8c392b4e' + 'SKIP' 'bd7bce1bf0ee24ce201fe0e5f0b8a6c829a84d224800547cf97a893326381e2e6eaed9c70529a6864b0c338276b3a307b14e26fba221e62263b90c02eb5e5f34' + 'SKIP' 'ad2cfdc3d8de2193110325395e11015577f26ef1081307bb236ed73fbd109822dd44ea026a57c2e17233e1fb7982e78aa986b71c043f42887e6c5d7f51b84ea9' + 'SKIP' '19c3dd32bbe3ed7caec86e90a6af39b07eb99d5a36515cf061bb6979e5a403ca98a0e00e584a9f746330d663e5fc0868ba7fc98ab2df264bc863bdf22ef3dd5e' + 'SKIP' 'ed2ee4849b715d80ba103fe090669062fc08c6864c64e8f8291f7f21092f0184ca5528bdd3a9318f077ef00cbe8cf28bacd1e7a9cb9f9ae1047becd930a0c42e' + 'SKIP' 'f09e80ed25fdda9b2c79bd5ea3701985596d478b0b4e6948a1ea8fb789ef944e71640ae200442afcc23a9f0e07b34d83365e223b620baf776bbd864977e633a0' + 'SKIP' 'a51e5f2a5e37c350fb4d715ece644c565a508970c3aeb1d28cb6cd03a0c7316a1935e028b64f20a03818926c611b50ae0ffd77d04c676d604d303beed0f96e1f' + 'SKIP' '25dcd2c4a4287f26c01419b7d6430629d2827b6efff473361c68d6ec44e94e6d2bb8ab8215dbae5da25781a668f571cfc28b60c5693eb4ed629bf80fe2f68ccf' + 'SKIP' '8d6eeb2330a3ca7c522cef77bf7336fac9444a0884d5d101c933abd3a31a3711529a31f9a181d4795148ecbcc24425750587314ce28e44c9ab2fd2de8365e853' + 'SKIP' 'fd487cb02212ffdae5d8beb7d895819377686085b95658dcf597a534b017adcb65dfdf06e0276e7322a46dc28118803b05c246c22e7d3c361f1778ed9e12a3c6' + 'SKIP' 'e18e5086c531a37837f6e7df72ce10a38289b02de4cea376772d45cccb5aabc628fe4f3f9607f646903a7cf59b02692703e6b26800604cf958136987df0b4d5d' + 'SKIP' '1b340a502dcf5a1680caf5320b3aa72aa4317e4cf1d877c1f6425e281a5668d5f092417e1f282371c0a211344f5c187fb3bdaf3f97c7e122951edf7e4c08ca58' + 'SKIP' '1124c5c3fd5104c886c841ead1ae9afb42683388b640b1a86523dad9919c6bd59a1f78b07ee5a213e0dca77c636a29676af0592d4ed1735ea35f282f6f23e46f' + 'SKIP' + '15855e9c28cb0abaab7ab606904dfc84d426ccab44edae59f0e1f092202b80525929f8d394d04ca1513d1d5dfc2cfc108a66a9c1cb42b9966c6417562ef20689' + 'SKIP' '505bf01a9d6b0926b557b673bb686d1a4ff73ee70539991e52272fbf58aad3ea25cf5ffc5c2d094a2d5e78b18a1a134f2ab530f595073a2d3eba9ff2c1418e1e' + 'SKIP' 'b33b46c37f12f7650a67c3731b87ff84add1ac8623cc96b52223a1ef9946bcaa990ed7c7036674e228335f57ec9ed421ea4d6c1dd72d19b516215d9ac2c7c3ee' + 'SKIP' 'b4bf4c4c0672bad91d7f8d309e741104918dd8ee8688a00d600379eba5828d1b8157953377c73d0b58050b4ff888c43013ede4e9c959be8d9c028a6143bf58ed' + 'SKIP' 'e403bdc300284c4f0f4302e501b8ccfe5e9af04e73a6070eb6615322f5f40ed6edad53af2ed278741f61a4e3811b92faf1af02f113dbcc2a19bde335a55930d7' + 'SKIP' '5de29d0eb5264e24ab9228bea2ea4b549b3fe37ea67c045df9f83064f11c2f557b9641e772d28db59f8d258418c77435b66ff471c3695043f0f8275d5e7b1685' + 'SKIP' '3c2cd960103ad97ce35679f906b3c9be79a9744959c5dfd0ea338d8075b698790934694b2f71fde026b164c5246cba71ffe3cd5dfc1464da338136fbbaea2f1c' + 'SKIP' '2b33b11e48d8dd8d6743f26170fb99eae38541e3665e2448d91cedf729cca7c0278bfba1078ecbe88201eac4407ec5bd0292a97dbab9cfc67e42646712befc1f' + 'SKIP' '71934436987ecf6914e5c559445c2b7ee92a31c467552342094c93a59d32563aa9930e710556cc1e271eeb352705a9df9b3702482f87d045d132233cee4ab48b' + 'SKIP' '9fb1318a3a181644c67b2adcac0cb04d1b88dd0e632751967239d4e8d4698f193795d00194b0f68505964c84572b67168dcbed02ad5bb7abd41e406a62111489' + 'SKIP' '353b7e89a7044b7426db730d5b900eb347907aa0c20e307d2317925d6021a969a343649ede7a32e698dad60dd4472eb197c9578fb0e36fc354a60266fd680cd7' + 'SKIP' '803223224f22aca25dfe34d73e2bd49438f0cfa83b71baa545fa6fe93ee1a631b6b62c7324dc1c0d2d2ded94c36185a83af39b4f4a3362f554a1943ec2e2ec3e' + 'SKIP' 'b0a7ab192066c472f3ebc39bc9afc9fef0528ff7c98bf67c5b9cb679cc6d93eab3d25e91d8d74c9c52f72803949a3ade7940e677b9a3114605b966df94da93bb' + 'SKIP' '824089f9f45e2ff9e788d874bb9294b9208b4486fd22c56cb59d4d88cb996ff5a81ac22f8ff136ddffa1e40330f02e53ab759ffea7e4535c11d4f8195069966b' + 'SKIP' '696309582c56fad530a0bf2be12bb744dedac11ed69642b14eade0a26baf1ce52b36074b89b963b40c9c6fbc4857cdc71b2a6c6d64afcbb2df716bbb57579986' + 'SKIP' '14212fca051d3e9a85f75616ff7f95bbd119ef8e5400dafd4447f70db2c4e9c03d971ec4fdfb4db2319d2613062d74f54dfd5d0a100ab0fa27ac6c05e16bd063' + 'SKIP' '7228e249d25a99f5106040b8ddb5705979b3a5af21ca60d0d43a9ecaf6eaccef6bde8ea69c896cffbf2b5963ec4f68987e69b184a8885c7f4c98d2b05fd59fbb' + 'SKIP' 'a54ac1c6d43fe88dee08884ed2e5bad0a0b6d11f34a46dc9801e675860afb7641b57384248a144a621e66a73541d73da11f71671116f1cc7f21534526411439a' + 'SKIP' '2e486cabad0d45aeb760d031c7946e9887c710b29dbe9c16e5f4bae6a230031dba4d817f1ee25a0bbca849d1c71b2f84052ffb73c4a6c4350a6ea5ee83c5a40e' + 'SKIP' '23e2a5def526fa886d1f1b05607bfe6b1ecbb2d6009d023145adff9eadc3135f9078faca6638966b586e81e4ab9d48d23b5758504d5a043d5f46e7a701d185fa' + 'SKIP' '5d82f32f7c04b084530722b9848254aac289c2a754a10e16821083a871ec14065dd9ee83e72b8e9e8ba2915f3fbaf17b0874df724002eb08a5f4f84d1b6aecd8' + 'SKIP' '4928cc008dc91fddb89b154b89b1ce2bbff14d8186c0ec1f81971fb9d3b622475e93e42015916699c3442e2268680779f545d183112b57dedae96fc48a75844c' + 'SKIP' 'f93494f5f3196cc35d017ff9719d146bf3e5e7bfe32a7f5e75d96468ce30ef367428ba83b1c99b665064d3edaec84cf3bf18211d8f7b4f118b3bdee495eb8ef9' + 'SKIP' 'eb79554ef0d014b94bc72d4d559c2cc5419df96486acb3b6a09d5e8e9dc48db626ce0f90338c67fe873b2cc3c45c3f308c8080532b60f74a07c9e39e01d34197' + 'SKIP' 'f657143cdd9c937d78315f4617392e60f7c46f5cec882e060f740f77a27dd4c045878cb66e568a6d9ae74e9c706ffb3052817db6f327c87b85ff2f46eea678e2' + 'SKIP' '0acbd87a09a62af860d14f342d61c82c29ec1711038d4abf4ae473df187fbda6aa49386d88928c5dc4c3379e097478b1d40d9ebe9360e64d02e59161164b2a3f' + 'SKIP' 'bb06066e90132f0172333d46be54ca816eb7d3230a193a6cae765d8edc50df4a6ba9fb7c0647cd6befc8c3bbbe1d96c285ee202812477d7295c1130ea8cdd6f4' + 'SKIP' 'f1a94986b4b3f310fecf981e3619f9787a0c8cb0b4fb3f39e5fe85ee3400e7e50278d41bf5468f65e5bd890358d2aabe28d3e11ee838ccbda2ac7af16be568d9' + 'SKIP' '44dcaad2ab326ebee62d3e63207a161dcb1b63bdb21412b5624ca7875bb6e99997528683a633d7dd526dc5ae9408e73bd066e2a1e5b34ba5c829162df2265346' + 'SKIP' '162a2c2104d5f036657ace6eef2ac2d533c0934376fab2521ff1d3fccd704e3d08fa4f6162e9100a844db2aeb57fa3ce214b8058bd227f38b058ab7606ee7816' + 'SKIP' 'd50d5a69087a5ba6c416a6391232764c8281a8094645175fdb2cfc1bb011e33335ae4e96d79364681bea61edcb58ba9acb491e08c301d0667481759d0ee1e183' + 'SKIP' '539b1d0e764f0ace3e572ac49f20a790183155ddc0bcd3a8d040f44c9891dd36b1a0accb8c0288267afbd33e6ceadf6fc3a5e3db40491c695e0c1d3dd64af40c' + 'SKIP' 'f5cae6eabf2066d5deef737a15967d5d11340de34b181f3b3d841023507fc58af2b68926eb04bb342e1adb857165400f862f2b35ffec4204a107b3b22a1571e7' + 'SKIP' 'ab28a1f4f1756b8451840566d8100cb3d0b498b13d006593c72829b9783e94900bae78ab47c576d156e1d08c56602274b3f7d5bd885de17336623b23c3e4f255' + 'SKIP' '8481f23c33a84d8efb798f4b83f78011f74cd5a3dec428b177d7e600922836c1eec61c4238724038221d8ca2e2cdf20462c5911085da3461aa315f7dd19d96d8' + 'SKIP' '19ebb39f3e53ce77dc3522d15c88270c5ca3fdd76fa8bd417852d08a1d85b30bc6eed3e11c95392b694dfc59bb8b43e41d43df4954f00593c8fbe50bd9e199cc' + 'SKIP' '4dc42e9bfb5ee0f952bfd7befc9d333044aec11fc3a3852d4636f2e38ab139eb92c497eb3e30921b6ee8ae7d6e5834dc334ad25419bac2c875a9fd7eb7945f9b' + 'SKIP' 'fea498b5c5ada6d7f57caf652b68f687081aae8552d2d04cd9e96764efd1b896208671f4f501210bb557710cca3ceb0c02ac2b5da6d4c04c210e87ec2d715384' + 'SKIP' '4a4af88fc8a8c37ae6d20b35d19e60a17672569d5489be120edbcc62ab3ba4bbc43b78cd0799b9fd73c84b089364320bcbad6fcb7128c2cb06d15ed984d6fc8c' + 'SKIP' '0959ca78ff31299adf1091f4aa1b32cd2e9066aba5e44f0658c91bcb93fe8714f7b671ffa2cd498ecbb6c6e643ac6571bfb193034af206599122fd7c2a3c23a8' + 'SKIP' '03ca740048e48c5b6948f972f7a826240cf51c9b5c8645d1b1c50366e78cbf45f5b45b7b47f87f231b50aab46ef2f8fd78bc53899ba1c319a19f9f61c7cecb90' + 'SKIP' '1b63569b589e994d1869e2f8eaf5d4ee225fe68862ac5c848e46544b827ca5fddb4bf060f5f39001608fd9bb284eec005f5f9b095307d3a008c9ea8957c2fad1' + 'SKIP' '073514dabc88f0206911eb27eb36157e425849221056438340b22ee7bfa6997b58ce7dc4470b09d7ee96f6374f117d91cc19d9032401b2355d1c0aba6fc75771' + 'SKIP' 'd7dffb68fd22db7ef41d1ac193dd297fb6150f034f257a44e6cb0a4848dc4a94e328c2e26d5296a72993e2db75d09e3ac46456d337a5bae767fb266082d3cc66' + 'SKIP' 'aca0527f5630671a319e7ecff958a3a64f29df73b427c67c1d29763cc1801fa82e96761c3a61c22d936913943a49261da64d490a0752ba9ffc8e0c1d92f99835' + 'SKIP' '803736a8e836560b3e50b7c0c8cf1f9ebb16b8642cc7bc6e5167d19037bd757431565fe4581776e50eb2693c49f778a24fecff41a553612e7e498f755b8f2f67' + 'SKIP' '4bc132d9c34deaae9b13d08dcff8ea83aa8f9ce4398531e0e0a80df30da3808815f3a587fe5627f6553b8e9c2aea6cc1749b752250a91c94f1cf8ba4f3eb5e8d' + 'SKIP' '2b007e58c0541c6ae71ac07cc8c0762343a50e1f3eb52db5631ce725c4f074f41c639fbf6d8695daa78ee1d6a8cb9a0c31d0a9d9ea2a3a10c05f7f6421dda184' + 'SKIP' 'd3b2c0e4fd9f26a0eded375522478f8e9403232799d233eed4668920f8a9942ec6bafbc928f14459e4736f63a9a66d3ae2e9f70dc810145acb5fc522b13ecbd9' + 'SKIP' 'c7b0be7a05a304e9993fe8ebccefa44944bf9d27dad9f7b3cc7111221bf50b2db71ca51cafc89dc03b237aef3aba7114543c8475cc393d13e61726e68898d3af' + 'SKIP' '4008c7443bc221a142a939deac86918bef8f09ff1ca99a899586b13120edb8007450daf0b9d143770a9f04d616fe96f527b6aac160be45e5cfa66c0da898039c' + 'SKIP' '029d9e899dd9e7bdbcacee34ee9b468e2e2a8f726e38d014891c8353f6afcc52a58c7d9289fb0487cce4d90c8d51f578c27b1dbcdab42de7308f50b7baf27877' + 'SKIP' '9ede433f4e01a5a313a133a8029d64250596582b93a33e434b8e2ee5bc53ba7f91947e1be2aaf57776028cc89e7ca62cc90067ee64650b3e503acad858777a8a' + 'SKIP' '9ac15b3542629c5333cc7d7f53b4f2995d807a31331b9e04d3da051be0ddde7e8c3cbc79837ffb49a4d364db1680cdc6d763465fa56bd4d26b16b765985babe1' + 'SKIP' '25fc5f03732a584c864ee2ad20a74e16777998b0b34f880bbe9d646cf1944bac1f276bbd62515475337eaa81c0f398d79888aca6dc18bcd06ebb7cee9e4a1d8e' + 'SKIP' '70e35154f9385156a11280294cc254cb314fd89653daedb46546a6bd64c550e3fc7e7a8df3e653c516687f0317bcc710fce82b4d649472333c501ed85fcd6cf6' + 'SKIP' '576fd826f28d945782840865f3e5b7c87d7f6f91f08e473ff0d06a37caa2f89cdd9cb4a7aafef5551363aab9c60e887ee794d5b6292874c12baf88c23ea5668e' - 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f') -b2sums_x86_64=('eb44f51042ad62689fbbcf460bb2120805454ad96291c0986f699ae0874070a2e48a1737a1cf2e8dd73d482b3059d9c2c22bd83cea04558ba2815f4114c12f05') + 'SKIP' + 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f' + 'SKIP') +b2sums_x86_64=('5cbc3cba5e3abe4c77a54aeb2015b04e39dc80351accaa7c268da3e67577c0ad47109b8693bf2dee8d5366d0a0df3e6439c32d18fcf5ea07345bfcf517e5036d') # https://www.kernel.org/pub/linux/kernel/v7.x/sha256sums.asc sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '95f3e9209629044028373af987dc0e270a5a15acb8c8e49c5f05057220c75fe2' + 'SKIP' + '6cf74fc3f28a751354187b6cf610c3e43cf9088b5f730b68dbb575828c3a7478' + 'SKIP' 'a9171e731d08a454a50174889af8936fab962b33c37c67169b0cdf21b0b80821' + 'SKIP' '6a27e1b363f1bb133b9905f84b00e4d01885cac84858db46bdbf764849c9984b' + 'SKIP' 'dc3facd0cea0f1f99cfd824203db8da8cd0a970d07b2c4c88dab661655687545' + 'SKIP' '9066c85e02351d86d4c61b86eb34d45fbd8022f42ce1739aee37931e7959298f' + 'SKIP' 'bc0cc15d29d5b817201daf0f694543f932e6af319c24c1f603fc89e71b7c27cf' + 'SKIP' 'a101a8e061983d0a1cf5b47efb28840e4a8d3b00daee53a6a7f773e2472ffdc9' + 'SKIP' '9492d7db11fd327ff06a52cad945bf8ff6dfcabec72c435bf02b4432ee67475e' + 'SKIP' 'a90ad7d91ecd9c7f64c9f84a65626241bc86c08c5c255e6684b7e26bcf5a47f8' + 'SKIP' 'c206a0d1ac334a36f9ec5b8b2d706eaf6b726d32e304848d0ada50b6f25885b8' + 'SKIP' '40177376862e4d7f45684c5e2a743f3da40d59817b0afa3050a3002ac6838085' + 'SKIP' '720720067a21e8c57d2619f4c0b46846ac060ec6db97088634d62ab8628c70a9' + 'SKIP' '3d1299d19aa9fbc96d25f67154009813e05e92340340953ac8355649cff837a4' + 'SKIP' 'dac26fb42f5df71b30c46b3c5894c6016af5a283c57b45173386767ca53b1bf8' + 'SKIP' '66eb2c49dbf708cc781d411d0e44ed613480f0ef66d522272929d9cb7bb21e93' + 'SKIP' + '718b40e15350049e03c0ed281b9257fca5432b8dec2e0e145e8b26507ee8892d' + 'SKIP' 'b7ba949eea77e169aceb2c401d4eb83a7413aab6f15ee2a7ab1a96352f0aa12d' + 'SKIP' '341424b925d506869793686e22ea8f095a34048595ebebec409c0a7bbd346ca2' + 'SKIP' '7c25d0a53a115bc1f072ff0cfdd9aa88858e754ae8b15026a2582656e8bd9c43' + 'SKIP' '79799f12cdf42de1cf6c5d483896b439d77bc2e582e10c7215f5375a76612a72' + 'SKIP' '5e3b455024fab2855b590091c9d14b0d12d5363f3d4bf025b31b9aa88b65eba5' + 'SKIP' 'abd8c9326cecc9bb85db4b3dc98ddcb66306b34a6d84fb05d0805aed537e2dc4' + 'SKIP' '60a9df54821cd98581e7476f8a7bb6cb0819762bdbed4d356332e41d164a8182' + 'SKIP' '929108ae0a8eb4782cafdd89e3d4426d02d86f2c2102ed99e760e31f0bc3794a' + 'SKIP' 'e1226c836a9c2fb6daff109e56ba21c13fd198e109cbb181de920af04fe153e6' + 'SKIP' '8d60f7dd26ef904419aa966427a9af2f2c9a079bb092ea0b58274ab905f6047a' + 'SKIP' '258597006eb96e1fb185ec9303ceb48148eedce95608b57900383f6df59d2989' + 'SKIP' 'f935409f9aeba3314adfad7db1b3d895557a07029a80d10969804e49c0e270ca' + 'SKIP' 'd8e64b9da3beab7c33832c019da50d0977b39dc32ec1f649a7c3bc93a2fdb9a5' + 'SKIP' '1780f66b157de6d249301331e31b7505f6e6fa26686fa4c618623567e4b758ae' + 'SKIP' '2501c98aaea6a53eeaa18d696f60b677e710d0f2d89f7525e47ba806de1878b3' + 'SKIP' 'e6b770d37e80509ed7ec5d8b59b4d2e5d6dba54ba659d7392de3b567f1eb6e54' + 'SKIP' 'b8596b5b5b546fdc309e10326b0e2ccea5a0538d4919630ed2845cded78580aa' + 'SKIP' '5cc215215fed4247c2d6b9732deff586331a07d54c3b00538a81921093a3a1a1' + 'SKIP' 'e303da14a3c8a15fd1cb45394138f03dd16e66d2ac0d1ebcd933977df937d771' + 'SKIP' + '503eba8d7a80b978fccdaac798f57cbb6cd2b2b04fd04c46399daa004b6646dc' + 'SKIP' '1f0c958b64ba48b8dc8b5548e1ad1934b8d4903fc16fec15014759e3681ea275' + 'SKIP' '47994d576008a377de612ed77e6b2e7bc6b24ad3a30a6d157646d380ae323142' + 'SKIP' 'd9eb3717ba98e270b0a998f8dd90cbda072f63b6a6ea8f66763bde3ea64e591d' + 'SKIP' '1e2fddb2e0c5d184a2e8c774886cf9be2f2f292e4f05832b50e5b49c48a7bacc' + 'SKIP' 'b9b8771b6a8bd7128f4344ad189e32c155abf72abd3ff646dcf5c78b08eb94c4' + 'SKIP' 'f62837e8ee51070f8139f718e0e80a4ce0c0dea9ce2328c10c63403aca5b3d2e' + 'SKIP' 'cae59282e3d1afd69f4f5dc00a180c712ef464c98b8fd7ccc9b03de944dc79b9' + 'SKIP' '5a74c8b2ba11e5876eeeb5ba530ae97e384b48943fa7a31e13396ecb82c5a8ce' + 'SKIP' '8b954ea37a2190022064c82688bc8cb1cf79a90598f078035be7d4041003cda9' + 'SKIP' '40ac31789399a5964c4fd820d4cdc7214add91109bbcf4a614619e5d8b367441' + 'SKIP' '707460d08c15f451d0a0e567c6e35522f3e1409bbe3bfbcf7160f33af06391b7' + 'SKIP' 'eebbe50deab379d09b5149ef30769ad6dfa11109696934ee66b108ec406da942' + 'SKIP' 'dab92dbc6eac02771c72801d94e6a53aabcf629a9cd79e85e0ce2ba44a65b06b' + 'SKIP' '30e2fc90c950869ab2fe1535b609f35dc1d46d6b6816de4afcbf687dbc224f0a' + 'SKIP' '1fdf8c4d8ee07c02883b827a11a84c1e5f70570545b8a4a232f6b59dd02049fa' + 'SKIP' '0057dff07a3ce9c7b7085edd1f912958e01ce8f7400151d55dfb74c0c6a084e2' + 'SKIP' '2762f72620781a672d9a21bcc591b862b04a756b271cd1009a7390eb1b77635e' + 'SKIP' '541325476efee83232ab8a6c62c14cf3c3c957c49cf053ed2869f7780f86d032' + 'SKIP' '8a8eb0934424ab6312f26e03a06be5d0401360ee60a20a87b11f7c3c6590103c' + 'SKIP' 'f7ff13ea46359217d696c6ccd49bc3d74c441ac5893d5151d95138017b0d10aa' + 'SKIP' 'd17493b3579c730d27dc723447175b70d991f50253c9fd419df5e923bdf7a244' + 'SKIP' '92971bb4dbde209cf170838cfcd75d8ac9978239821d3493c28cc12623c2bb60' + 'SKIP' '7245c6e2cb5c0b0ddf800894f087770cbf8ccc6f8b3f53b9a21c704e9dfa42c2' + 'SKIP' '135140bce40644079be7743b51f8618fc829a8868ac53137050f76c45eca4061' + 'SKIP' 'e6ec816ae309445c6c11e3433c04d0c9100e16c0e23268226d13c19e7be49edb' + 'SKIP' '29c61ec984bd342113b3c4310a3d5fddfd6d7db83847cfb26b84cc730385320a' + 'SKIP' '53425998005a4115bdf6001b9ffbe62c66bb2db205994b2e13005a3417ea6cbb' + 'SKIP' 'c28fca9ba015e96599627acf42e52737a6a846bdcbfd3d4af4147eda3109e12d' + 'SKIP' '5ce97dadb9eb1a8b0da41a8446ee74e01a5a7e7ab439bbea7a711318f04ae24f' + 'SKIP' '44f1c7bd18ea7f8772ce41e24dddd5f314e416ec8dfac6afe1ce8680c1bb4903' + 'SKIP' '0bb8ae5b6f23e13c60953eb5bbc8939e8f6c8faf2b216a20203af3b6f0217134' + 'SKIP' '907c92ef681154d10959a5f0cbe2a636688036990ba42e059af00cc2311b4be4' + 'SKIP' '534e68a989d36823f14a10299803f294eef39391619b4c329d78c20539d9fcd5' + 'SKIP' '516e6ec51ca0330989128a4eb2a8f6785e5d45d930899cbbbf37b8147ddfd854' + 'SKIP' 'bda3d6842f15998ce3f3e33d0e6e420df9c9c2abcba13716af478720c5933753' + 'SKIP' '3e7e7dba1ca88930c2c3f0d5411ddcee8c2e12fca59d859e2383dccaf9a08022' + 'SKIP' '3a271bc6b0219152047fb2b78d9e695eb791b796ea6d8233096912c77d62a5c0' + 'SKIP' '4e80f2d05591175d4ed0a1a144509e97260e5593472aae586c07956215d99162' + 'SKIP' '8ebce8f38e38bd66879d040028c2448e5bc7dcde9df0ccee2b4221015591b162' + 'SKIP' '0b0e05b615ae6eb825bf7d7fa568f551b0dff6c2cee62c9285c1b0d63224ea1c' + 'SKIP' '2e1e2a0eb039ce61c1612bf6c7ab5227b99df053bcd5592da7758715dd333fda' + 'SKIP' '17de1f76da3db42dc3c7829a22953cd2c6de916999d809e3fb0bbb741753ec8f' + 'SKIP' 'd90cc6570a47c8754e4c5d45834e0434c0a1fcabda9b8933b1fba84e3dabc083' + 'SKIP' '028bf538c870465e1752514725372fa9eef6b7178d9b8d1e2ecc15edc5a63cc8' + 'SKIP' 'a92a84405ee9845738c6b52d8f0f0eca16eb40afe0ebe0f4e9e1168597675311' + 'SKIP' '44a0b10a6dc83465ea86f5fba936b2bfbeffb1b3f0b2e04013bfed99ba4f6c5a' + 'SKIP' '229b28cee6f2b8afb5888eed453ce7f82de033900383bd91957339007c9e6cbf' + 'SKIP' 'c5d61db05b19fde06102b0b3dbd76e1989c8f07f3b7d175176bd2d73f1a71e5c' + 'SKIP' '5988d7a37b4b71a64ed09fea872f08515a3c7029d33fd053a6a1822bb811ddb5' + 'SKIP' '1ec4fb93700b0696ba2c2a371f218f13b32dcc745523c7ee26a5c3cd7af253fc' + 'SKIP' 'c67fee35a42866dd3c9056ff099fc504cdad9530c107d959331b5572b21af818' + 'SKIP' '36cb306751c57de3e61c7a4cd18a7f131eef0dc1de15a4e28751d1e5b08bace8' + 'SKIP' '07683e522d6dea3d3ce658ee80bf4317f6c3caf248259da960bc4610f4d1e807' + 'SKIP' '66a0933d2d4f2edc8999b6fdf0fe10b5b607646fd6f1a1d534bf26270543f8f1' + 'SKIP' 'c5279468d93e562f509a6b599a2b7ca7ee0032d3fe35314c5769fc6c9a9234ab' + 'SKIP' 'c0111614ec44014cbf621eedf5a4ef302a855423bc1633ee692392950e971d0e' - '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb') + 'SKIP' + '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb' + 'SKIP') export KBUILD_BUILD_HOST=omarchy export KBUILD_BUILD_USER=$pkgbase diff --git a/pkgbuilds/linux-omarchy/config.x86_64 b/pkgbuilds/linux-omarchy/config.x86_64 index 5974076..283d023 100644 --- a/pkgbuilds/linux-omarchy/config.x86_64 +++ b/pkgbuilds/linux-omarchy/config.x86_64 @@ -277,8 +277,9 @@ CONFIG_BOOT_CONFIG=y # CONFIG_BOOT_CONFIG_EMBED is not set CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=1021 CONFIG_INITRAMFS_PRESERVE_MTIME=y -CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3=y CONFIG_LD_ORPHAN_WARN=y CONFIG_LD_ORPHAN_WARN_LEVEL="warn" CONFIG_SYSCTL=y @@ -11230,7 +11231,8 @@ CONFIG_USB4_STREAM=m # Android # # CONFIG_ANDROID_BINDER_IPC is not set -# CONFIG_ANDROID_BINDER_IPC_RUST is not set +CONFIG_ANDROID_BINDER_IPC_RUST=y +CONFIG_ANDROID_BINDER_DEVICES="" # end of Android CONFIG_LIBNVDIMM=m @@ -11868,7 +11870,7 @@ CONFIG_INIT_STACK_ALL_ZERO=y CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y # CONFIG_INIT_ON_FREE_DEFAULT_ON is not set CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y -CONFIG_ZERO_CALL_USED_REGS=y +# CONFIG_ZERO_CALL_USED_REGS is not set # end of Memory initialization # diff --git a/pkgbuilds/lmstudio-bin/PKGBUILD b/pkgbuilds/lmstudio-bin/PKGBUILD index 48b1a59..dc1d90e 100644 --- a/pkgbuilds/lmstudio-bin/PKGBUILD +++ b/pkgbuilds/lmstudio-bin/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: noureddinex pkgname=lmstudio-bin -pkgver=0.4.24 -pkgrel=2 +pkgver=0.4.25 +pkgrel=1 _build=1 _pkgver=${pkgver}-${_build} pkgdesc="LM Studio - A desktop app for exploring and running large language models locally" @@ -16,9 +16,7 @@ conflicts=(lmstudio) source=("https://installers.lmstudio.ai/linux/x64/${_pkgver}/LM-Studio-${_pkgver}-x64.AppImage" "lmstudio.png" "lmstudio.desktop") -sha256sums=('17cb8ac6374f9182fc127efae20680265e3c4c17d96eadf147eb5fe6111a9353' - '9f791789c959a11316328692807737a5f1bc1c170ae99ec04c56bfd8ee8263e5' - '635dec12f3e3a57136b9e6fd7c2839ed6da7287fa55b482d64debf6eacf36baa') +sha256sums=('eca467446c833824697e8befab300fe5269fdf984e3ee4385fcbad8502f07c53' '9f791789c959a11316328692807737a5f1bc1c170ae99ec04c56bfd8ee8263e5' '635dec12f3e3a57136b9e6fd7c2839ed6da7287fa55b482d64debf6eacf36baa') prepare() { chmod +x "${srcdir}/${source[0]##*/}" diff --git a/pkgbuilds/macbook8-spi-pxa2xx-nodma-dkms/PKGBUILD b/pkgbuilds/macbook8-spi-pxa2xx-nodma-dkms/PKGBUILD index eb7975a..aa9b7b8 100644 --- a/pkgbuilds/macbook8-spi-pxa2xx-nodma-dkms/PKGBUILD +++ b/pkgbuilds/macbook8-spi-pxa2xx-nodma-dkms/PKGBUILD @@ -2,12 +2,12 @@ _pkgbase=spi-pxa2xx-pci-nodma pkgname=macbook8-spi-pxa2xx-nodma-dkms pkgver=1.0 -pkgrel=1 +pkgrel=2 pkgdesc="Patched SPI PXA2xx PCI driver forcing PIO mode for MacBook8,1 Wildcat Point GSPI (8086:9ce6)" arch=('x86_64') url="https://github.com/basecamp/omarchy" license=('GPL-2.0-only') -depends=('dkms' 'linux-headers') +depends=('dkms') makedepends=() conflicts=('spi_pxa2xx_pci' 'macbook12-spi-driver-dkms') provides=('spi_pxa2xx_pci_nodma') @@ -31,4 +31,4 @@ package() { # Modprobe blacklist: prevent the in-tree spi_pxa2xx_pci from claiming this device install -Dm644 "${srcdir}/macbook8-spi-nodma.conf" "${pkgdir}/usr/lib/modprobe.d/macbook8-spi-nodma.conf" -} \ No newline at end of file +} diff --git a/pkgbuilds/mise-bin/PKGBUILD b/pkgbuilds/mise-bin/PKGBUILD index a7e07a6..68cbe05 100644 --- a/pkgbuilds/mise-bin/PKGBUILD +++ b/pkgbuilds/mise-bin/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Jeff Dickey pkgname=mise-bin -pkgver=2026.9.7 +pkgver=2026.9.12 pkgrel=1 pkgdesc="dev tools, env vars, task runner" arch=('x86_64' 'aarch64') @@ -14,8 +14,8 @@ provides=('mise') conflicts=('mise') source_x86_64=("https://github.com/jdx/mise/releases/download/v${pkgver}/mise-v${pkgver}-linux-x64.tar.xz") source_aarch64=("https://github.com/jdx/mise/releases/download/v${pkgver}/mise-v${pkgver}-linux-arm64.tar.xz") -sha256sums_x86_64=('011e71834ef919c775f8d8ba7a985295a39a3ffe322b5f87a61cc0293eb6567f') -sha256sums_aarch64=('3c43ee044bb7d8bdaea800e1796a0b5d983276384f81d477c059a2b910042e16') +sha256sums_x86_64=('30c79a0a24d8f0ad80e6c9b11ec54816be2a9b77e7eeae32c1267a5b9d34d3d7') +sha256sums_aarch64=('7bc2a5558b787a33f22e4b5955cfec58871ad3723658418ad3d2cdf5a0e693b9') package() { install -Dm755 "${srcdir}/mise/bin/mise" "${pkgdir}/usr/bin/mise" diff --git a/pkgbuilds/monologue/.omarchy/package.json b/pkgbuilds/monologue/.omarchy/package.json new file mode 100644 index 0000000..2a9719d --- /dev/null +++ b/pkgbuilds/monologue/.omarchy/package.json @@ -0,0 +1,3 @@ +{ + "source": "local" +} diff --git a/pkgbuilds/monologue/PKGBUILD b/pkgbuilds/monologue/PKGBUILD new file mode 100644 index 0000000..9b9e14e --- /dev/null +++ b/pkgbuilds/monologue/PKGBUILD @@ -0,0 +1,40 @@ +# Maintainer: David Heinemeier Hansson + +pkgname=monologue +pkgver=0.1.0 +pkgrel=3 +pkgdesc='A simple, theme-synced webcam recorder for Omarchy' +arch=('x86_64' 'aarch64') +url='https://github.com/omacom/monologue' +license=('MIT') +install='monologue.install' +options=('!debug') +depends=( + 'ffmpeg' + 'hicolor-icon-theme' + 'libpulse' + 'qt6-base' + 'qt6-declarative' + 'qt6-multimedia>=6.8' + 'xdg-desktop-portal' +) +makedepends=('gcc' 'make' 'pkgconf') +optdepends=('omacut: trim recordings directly from Monologue') +# Pin the published source until a tagged release is available. +_commit=23e0844f60feef2f9d2cf2c9d89f13eb0bf5adef +source=("$pkgname-$_commit.tar.gz::$url/archive/$_commit.tar.gz") +sha256sums=('1a04b9e47b29846e9135d110978f7f35c0274f7361729f0b6ac03796499c0ad6') + +build() { + cd "$srcdir/$pkgname-$_commit" + ./bin/build +} + +package() { + cd "$srcdir/$pkgname-$_commit" + + install -Dm755 build/monologue "$pkgdir/usr/bin/monologue" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 pkgbuild/monologue.svg "$pkgdir/usr/share/icons/hicolor/scalable/apps/monologue.svg" + install -Dm644 pkgbuild/monologue.desktop "$pkgdir/usr/share/applications/monologue.desktop" +} diff --git a/pkgbuilds/monologue/monologue.install b/pkgbuilds/monologue/monologue.install new file mode 100644 index 0000000..5357602 --- /dev/null +++ b/pkgbuilds/monologue/monologue.install @@ -0,0 +1,12 @@ +post_install() { + command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q + command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -t -f usr/share/icons/hicolor +} + +post_upgrade() { + post_install +} + +post_remove() { + post_install +} diff --git a/pkgbuilds/nvidia-utils/.omarchy/package.json b/pkgbuilds/nvidia-utils/.omarchy/package.json new file mode 100644 index 0000000..7ddd330 --- /dev/null +++ b/pkgbuilds/nvidia-utils/.omarchy/package.json @@ -0,0 +1,6 @@ +{ + "source": "local", + "channels": [ + "edge" + ] +} diff --git a/pkgbuilds/nvidia-utils/LICENSE b/pkgbuilds/nvidia-utils/LICENSE new file mode 100644 index 0000000..b87c5e4 --- /dev/null +++ b/pkgbuilds/nvidia-utils/LICENSE @@ -0,0 +1,12 @@ +Copyright Arch Linux Contributors + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pkgbuilds/nvidia-utils/PKGBUILD b/pkgbuilds/nvidia-utils/PKGBUILD new file mode 100644 index 0000000..105281e --- /dev/null +++ b/pkgbuilds/nvidia-utils/PKGBUILD @@ -0,0 +1,240 @@ +# Maintainer: Sven-Hendrik Haase +# Maintainer: Peter Jung +# Contributor: James Rayner +# Contributor: Vasiliy Stelmachenok +# Contributor: Thomas Baechler + +pkgbase=nvidia-utils +pkgname=('nvidia-utils') +pkgver=615.71.09 +pkgrel=1.1 +arch=('aarch64') +url="https://www.nvidia.com/" +license=('LicenseRef-NVIDIA-Driver-License-Agreement') +options=('!strip') +source_aarch64=("https://download.nvidia.com/XFree86/Linux-aarch64/${pkgver}/NVIDIA-Linux-aarch64-${pkgver}.run") +source=('nvidia-drm-outputclass.conf' + 'nvidia-utils.sysusers' + 'nvidia.rules' + 'systemd-homed-override.conf' + 'systemd-suspend-override.conf' + 'nvidia-utils.conf') +sha512sums=('de7116c09f282a27920a1382df84aa86f559e537664bb30689605177ce37dc5067748acf9afd66a3269a6e323461356592fdfc624c86523bf105ff8fe47d3770' + '1bcf2c6ee71686c0d32625e746ec8c0f7cf42fc63c76c3076ff2526b2661e8b9e9f76eaa2c4b213c7cc437a6f06006cc07672c4974d7f4515b2de2fd7c47a891' + '7f1457dc454144fdece5abf795744c4c948a13feb8d49c20e2a1b8b8973e86f980233b521485d73eb039c396688f287a3a5b3de8cb1fb20ed70cc62e4ba91250' + 'a0183adce78e40853edf7e6b73867e7a8ea5dabac8e8164e42781f64d5232fbe869f850ab0697c3718ebced5cde760d0e807c05da50a982071dfe1157c31d6b8' + '55def6319f6abb1a4ccd28a89cd60f1933d155c10ba775b8dfa60a2dc5696b4b472c14b252dc0891f956e70264be87c3d5d4271e929a4fc4b1a68a6902814cee' + 'a380e5faeb19293c90f613cd92bcd1cef7597ee52f79f03ffdffe5d37d2badc05b6bdb4c26a9d610868ae4c16eafd56e7d16f769e849dc0335d0d248c6235fe9') +sha512sums_aarch64=('316f90d5e0ba74db3a79a91464955240aa2c14e986653067fe902c132c771898d2866afc4e6498069b33665596e17b1b5282980936e1e6e31cabb53787e70196') + +_pkg=NVIDIA-Linux-${CARCH}-${pkgver} + +create_links() { + # create soname links + find "$pkgdir" -type f -name '*.so*' ! -path '*xorg/*' -print0 | while read -d $'\0' _lib; do + _soname=$(dirname "${_lib}")/$(readelf -d "${_lib}" | grep -Po 'SONAME.*: \[\K[^]]*' || true) + _base=$(echo ${_soname} | sed -r 's/(.*)\.so.*/\1.so/') + [[ -e "${_soname}" ]] || ln -s $(basename "${_lib}") "${_soname}" + [[ -e "${_base}" ]] || ln -s $(basename "${_soname}") "${_base}" + done +} + +prepare() { + sh ${_pkg}.run --extract-only + cd ${_pkg} + bsdtar -xf nvidia-persistenced-init.tar.bz2 + +} + +package_nvidia-utils() { + pkgdesc="NVIDIA drivers utilities" + depends=('libglvnd' 'egl-wayland' 'egl-wayland2' 'egl-gbm' 'egl-x11') + optdepends=('nvidia-settings: configuration tool' + 'xorg-server: Xorg support' + 'xorg-server-devel: nvidia-xconfig' + 'opencl-nvidia: OpenCL support') + conflicts=('nvidia-libgl') + provides=('vulkan-driver' 'opengl-driver' 'nvidia-libgl') + replaces=('nvidia-libgl') + install="${pkgname}.install" + + cd "${_pkg}" + + # Check http://us.download.nvidia.com/XFree86/Linux-x86_64/${pkgver}/README/installedcomponents.html + # for hints on what needs to be installed where. + + # X driver + install -Dm755 nvidia_drv.so "${pkgdir}/usr/lib/xorg/modules/drivers/nvidia_drv.so" + + # Wayland/GBM + mkdir -p "${pkgdir}/usr/lib/gbm" + ln -sr "${pkgdir}/usr/lib/libnvidia-allocator.so.${pkgver}" "${pkgdir}/usr/lib/gbm/nvidia-drm_gbm.so" + + # firmware + install -Dm644 -t "${pkgdir}/usr/lib/firmware/nvidia/${pkgver}/" firmware/*.bin + + # GLX extension module for X + install -Dm755 "libglxserver_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/nvidia/xorg/libglxserver_nvidia.so.${pkgver}" + # Ensure that X finds glx + ln -s "libglxserver_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/nvidia/xorg/libglxserver_nvidia.so.1" + ln -s "libglxserver_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/nvidia/xorg/libglxserver_nvidia.so" + + install -Dm755 "libGLX_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/libGLX_nvidia.so.${pkgver}" + + # OpenGL libraries + install -Dm755 "libEGL_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/libEGL_nvidia.so.${pkgver}" + install -Dm755 "libGLESv1_CM_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/libGLESv1_CM_nvidia.so.${pkgver}" + install -Dm755 "libGLESv2_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/libGLESv2_nvidia.so.${pkgver}" + install -Dm644 "10_nvidia.json" "${pkgdir}/usr/share/glvnd/egl_vendor.d/10_nvidia.json" + + # OpenGL core library + install -Dm755 "libnvidia-glcore.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-glcore.so.${pkgver}" + install -Dm755 "libnvidia-eglcore.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-eglcore.so.${pkgver}" + install -Dm755 "libnvidia-glsi.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-glsi.so.${pkgver}" + if [[ $CARCH == aarch64 ]]; then + # Required by the ARM EGL/GLX libraries. + install -Dm755 "libnvidia-rmapi-tegra.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-rmapi-tegra.so.${pkgver}" + fi + + # misc + install -Dm755 "libnvidia-api.so.1" "${pkgdir}/usr/lib/libnvidia-api.so.1" + install -Dm755 "libnvidia-fbc.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-fbc.so.${pkgver}" + install -Dm755 "libnvidia-encode.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-encode.so.${pkgver}" + install -Dm755 "libnvidia-cfg.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-cfg.so.${pkgver}" + install -Dm755 "libnvidia-ml.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-ml.so.${pkgver}" + install -Dm755 "libnvidia-fmdrv.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-fmdrv.so.${pkgver}" + install -Dm755 "libnvidia-imex.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-imex.so.${pkgver}" + install -Dm755 "libnvidia-glvkspirv.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-glvkspirv.so.${pkgver}" + install -Dm755 "libnvidia-allocator.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-allocator.so.${pkgver}" + install -Dm755 "libnvidia-gpucomp.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-gpucomp.so.${pkgver}" + + # Vulkan ICD + install -Dm644 "nvidia_icd.json" "${pkgdir}/usr/share/vulkan/icd.d/nvidia_icd.json" + install -Dm644 "nvidia_layers.json" "${pkgdir}/usr/share/vulkan/implicit_layer.d/nvidia_layers.json" + + if [[ $CARCH = x86_64 ]]; then + # VulkanSC + install -D -m755 nvidia-pcc -t "${pkgdir}/usr/bin" + install -D -m755 "libnvidia-vksc-core.so.${pkgver}" -t "${pkgdir}/usr/lib" + install -D -m644 nvidia_icd_vksc.json -t "${pkgdir}/usr/share/vulkansc/icd.d" + fi + + # VDPAU + install -Dm755 "libvdpau_nvidia.so.${pkgver}" "${pkgdir}/usr/lib/vdpau/libvdpau_nvidia.so.${pkgver}" + + # nvidia-tls library + install -Dm755 "libnvidia-tls.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-tls.so.${pkgver}" + + # CUDA + install -Dm755 "libcuda.so.${pkgver}" "${pkgdir}/usr/lib/libcuda.so.${pkgver}" + install -Dm755 "libnvcuvid.so.${pkgver}" "${pkgdir}/usr/lib/libnvcuvid.so.${pkgver}" + install -Dm755 "libcudadebugger.so.${pkgver}" "${pkgdir}/usr/lib/libcudadebugger.so.${pkgver}" + + # NVVM Compiler library loaded by the CUDA driver to do JIT link-time-optimization + install -Dm644 "libnvidia-nvvm.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-nvvm.so.${pkgver}" + install -Dm755 "libnvidia-nvvm70.so.4" "${pkgdir}/usr/lib/libnvidia-nvvm70.so.4" + + # PTX JIT Compiler (Parallel Thread Execution (PTX) is a pseudo-assembly language for CUDA) + install -Dm755 "libnvidia-ptxjitcompiler.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-ptxjitcompiler.so.${pkgver}" + + # raytracing + install -Dm755 "nvoptix.bin" "${pkgdir}/usr/share/nvidia/nvoptix.bin" + install -Dm755 "libnvoptix.so.${pkgver}" "${pkgdir}/usr/lib/libnvoptix.so.${pkgver}" + install -Dm755 "libnvidia-rtcore.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-rtcore.so.${pkgver}" + + # NGX + install -Dm755 nvidia-ngx-updater "${pkgdir}/usr/bin/nvidia-ngx-updater" + install -Dm755 "libnvidia-ngx.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-ngx.so.${pkgver}" + if [[ $CARCH = x86_64 ]]; then + install -Dm755 _nvngx.dll "${pkgdir}/usr/lib/nvidia/wine/_nvngx.dll" + install -Dm755 nvngx.dll "${pkgdir}/usr/lib/nvidia/wine/nvngx.dll" + install -Dm755 nvngx_dlssg.dll "${pkgdir}/usr/lib/nvidia/wine/nvngx_dlssg.dll" + fi + + # Optical flow + install -Dm755 "libnvidia-opticalflow.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-opticalflow.so.${pkgver}" + + if [[ $CARCH = x86_64 ]]; then + # Cryptography library wrapper + install -Dm755 "libnvidia-pkcs11.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-pkcs11.so.${pkgver}" + install -Dm755 "libnvidia-pkcs11-openssl3.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-pkcs11-openssl3.so.${pkgver}" + fi + + # Sandboxhelper + install -Dm755 "libnvidia-sandboxutils.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-sandboxutils.so.${pkgver}" + + # Present Helper + install -Dm755 "libnvidia-present.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-present.so.${pkgver}" + + # https://github.com/microsoft/TileIR + install -Dm755 "libnvidia-tileiras.so.${pkgver}" "${pkgdir}/usr/lib/libnvidia-tileiras.so.${pkgver}" + + # Debug + install -Dm755 nvidia-debugdump "${pkgdir}/usr/bin/nvidia-debugdump" + + # nvidia-xconfig + install -Dm755 nvidia-xconfig "${pkgdir}/usr/bin/nvidia-xconfig" + install -Dm644 nvidia-xconfig.1.gz "${pkgdir}/usr/share/man/man1/nvidia-xconfig.1.gz" + + # nvidia-bug-report + install -Dm755 nvidia-bug-report.sh "${pkgdir}/usr/bin/nvidia-bug-report.sh" + + # nvidia-smi + install -Dm755 nvidia-smi "${pkgdir}/usr/bin/nvidia-smi" + install -Dm644 nvidia-smi.1.gz "${pkgdir}/usr/share/man/man1/nvidia-smi.1.gz" + + # nvidia-cuda-mps + install -Dm755 nvidia-cuda-mps-server "${pkgdir}/usr/bin/nvidia-cuda-mps-server" + install -Dm755 nvidia-cuda-mps-control "${pkgdir}/usr/bin/nvidia-cuda-mps-control" + install -Dm644 nvidia-cuda-mps-control.1.gz "${pkgdir}/usr/share/man/man1/nvidia-cuda-mps-control.1.gz" + + # nvidia-modprobe + # This should be removed if nvidia fixed their uvm module! + install -Dm4755 nvidia-modprobe "${pkgdir}/usr/bin/nvidia-modprobe" + install -Dm644 nvidia-modprobe.1.gz "${pkgdir}/usr/share/man/man1/nvidia-modprobe.1.gz" + + # nvidia-persistenced + install -Dm755 nvidia-persistenced "${pkgdir}/usr/bin/nvidia-persistenced" + install -Dm644 nvidia-persistenced.1.gz "${pkgdir}/usr/share/man/man1/nvidia-persistenced.1.gz" + install -Dm644 nvidia-persistenced-init/systemd/nvidia-persistenced.service.template "${pkgdir}/usr/lib/systemd/system/nvidia-persistenced.service" + sed -i 's/__USER__/nvidia-persistenced/' "${pkgdir}/usr/lib/systemd/system/nvidia-persistenced.service" + + # application profiles + install -Dm644 "nvidia-application-profiles-${pkgver}-rc" "${pkgdir}/usr/share/nvidia/nvidia-application-profiles-${pkgver}-rc" + install -Dm644 "nvidia-application-profiles-${pkgver}-key-documentation" "${pkgdir}/usr/share/nvidia/nvidia-application-profiles-${pkgver}-key-documentation" + + install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/nvidia-utils/LICENSE" + install -Dm644 README.txt "${pkgdir}/usr/share/doc/nvidia/README" + install -Dm644 NVIDIA_Changelog "${pkgdir}/usr/share/doc/nvidia/NVIDIA_Changelog" + cp -r html "${pkgdir}/usr/share/doc/nvidia/" + ln -s nvidia "${pkgdir}/usr/share/doc/nvidia-utils" + + # new power management support + install -Dm644 systemd/system/*.service -t "${pkgdir}/usr/lib/systemd/system" + install -Dm755 systemd/system-sleep/nvidia "${pkgdir}/usr/lib/systemd/system-sleep/nvidia" + install -Dm755 systemd/nvidia-sleep.sh "${pkgdir}/usr/bin/nvidia-sleep.sh" + install -Dm755 nvidia-powerd "${pkgdir}/usr/bin/nvidia-powerd" + install -Dm644 dlsnetparams.csv "${pkgdir}/usr/share/nvidia/nvidia-powerd/dlsnetparams.csv" + install -Dm644 nvidia-dbus.conf "${pkgdir}/usr/share/dbus-1/system.d/nvidia-dbus.conf" + install -Dm644 "${srcdir}/systemd-homed-override.conf" "${pkgdir}/usr/lib/systemd/system/systemd-homed.service.d/10-nvidia-no-freeze-session.conf" + install -Dm644 "${srcdir}/systemd-suspend-override.conf" "${pkgdir}/usr/lib/systemd/system/systemd-suspend.service.d/10-nvidia-no-freeze-session.conf" + install -Dm644 "${srcdir}/systemd-suspend-override.conf" "${pkgdir}/usr/lib/systemd/system/systemd-suspend-then-hibernate.service.d/10-nvidia-no-freeze-session.conf" + install -Dm644 "${srcdir}/systemd-suspend-override.conf" "${pkgdir}/usr/lib/systemd/system/systemd-hibernate.service.d/10-nvidia-no-freeze-session.conf" + install -Dm644 "${srcdir}/systemd-suspend-override.conf" "${pkgdir}/usr/lib/systemd/system/systemd-hybrid-sleep.service.d/10-nvidia-no-freeze-session.conf" + + # distro specific files must be installed in /usr/share/X11/xorg.conf.d + install -Dm644 "${srcdir}/nvidia-drm-outputclass.conf" "${pkgdir}/usr/share/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf" + + install -Dm644 "${srcdir}/nvidia-utils.sysusers" "${pkgdir}/usr/lib/sysusers.d/$pkgname.conf" + + install -Dm644 "${srcdir}/nvidia.rules" "$pkgdir"/usr/lib/udev/rules.d/60-nvidia.rules + + # Enable kernel suspend notifiers for open modules and override TemporaryFilePath + # from default /tmp to /var/tmp + install -Dm644 "${srcdir}/nvidia-utils.conf" "${pkgdir}/usr/lib/modprobe.d/nvidia-utils.conf" + + # Lists NVIDIA driver files for container runtimes like nvidia-container-toolkit + install -Dm644 sandboxutils-filelist.json "${pkgdir}/usr/share/nvidia/files.d/sandboxutils-filelist.json" + + create_links +} diff --git a/pkgbuilds/nvidia-utils/README.package.md b/pkgbuilds/nvidia-utils/README.package.md new file mode 100644 index 0000000..791d59e --- /dev/null +++ b/pkgbuilds/nvidia-utils/README.package.md @@ -0,0 +1,36 @@ +# Temporary NVIDIA ARM packaging correction + +Carries Arch Linux's `nvidia-utils` recipe at +[f9ae10b379f8b1d0832ec92bca1c12072aa123e9](https://gitlab.archlinux.org/archlinux/packaging/packages/nvidia-utils/-/commit/f9ae10b379f8b1d0832ec92bca1c12072aa123e9), +restricted to aarch64 and the nvidia-utils output. Upstream contributor credits, +packaging license and support files are retained. The OpenCL/DKMS split outputs +and their unused kernel source preparation are omitted. + +NVIDIA 615.71.09's ARM EGL/GLX libraries require +`libnvidia-rmapi-tegra.so.615.71.09`. NVIDIA includes it in its archive, but the +Arch recipe omits it. Adding that library and its generated symlink fixes +Hyprland's EGL initialization failure on DGX Spark. No rendering overrides are +installed. The correction passed package installation, normal desktop login +and reboot on a Spark running kernel 7.2.6 with NVIDIA 615.71.09. + +This recipe builds only for edge, using release 1.1 to sit above Arch's broken +release 1 and below a prospective fixed release 2. There is deliberately no +automatic NVIDIA version watcher: driver userspace must remain compatible with +the kernel package supplied by Arch Linux ARM. Check both packages before each +release; a stale overlay must not hold back a driver transition. + +## Delivery and removal + +Publishing requires an aarch64 edge build. Consumers must put `[omarchy]` +before `[extra]` in pacman.conf for unqualified installation and updates to +select this package. A higher pkgrel alone does not overcome repository order. +The tested Spark has that ordering; this PR does not change shared runtime or +installer configuration. Deployment must verify that ordering on the intended +ARM installation/update paths. This is not a claim of fresh-ISO validation. + +The upstream Arch submission is pending account approval. Once Arch Linux ARM +ships the missing library, validate that package on the Spark, remove this +recipe AND the published overlay package/database entry, and verify that normal +updates select the fixed Arch package. A higher Arch version alone does not +bypass an earlier repository's stale package. Do not use an epoch or rename the +package to prevent that transition. diff --git a/pkgbuilds/nvidia-utils/nvidia-drm-outputclass.conf b/pkgbuilds/nvidia-utils/nvidia-drm-outputclass.conf new file mode 100644 index 0000000..9c36f59 --- /dev/null +++ b/pkgbuilds/nvidia-utils/nvidia-drm-outputclass.conf @@ -0,0 +1,8 @@ +Section "OutputClass" + Identifier "nvidia" + MatchDriver "nvidia-drm" + Driver "nvidia" + Option "AllowEmptyInitialConfiguration" + ModulePath "/usr/lib/nvidia/xorg" + ModulePath "/usr/lib/xorg/modules" +EndSection diff --git a/pkgbuilds/nvidia-utils/nvidia-utils.conf b/pkgbuilds/nvidia-utils/nvidia-utils.conf new file mode 100644 index 0000000..580794a --- /dev/null +++ b/pkgbuilds/nvidia-utils/nvidia-utils.conf @@ -0,0 +1,15 @@ +# Blacklist nouveau and nova +blacklist nouveau +blacklist nova_core +blacklist nova_drm + +# Make sure that all modules are loaded after the main one. +softdep nvidia post: nvidia-uvm nvidia-drm + +# https://download.nvidia.com/XFree86/Linux-x86_64/595.45.04/README/powermanagement.html +# Enable Suspend Notifiers for faster and more modern suspend +options nvidia NVreg_UseKernelSuspendNotifiers=1 +# +# The destination should not be using tmpfs, so we prefer +# /var/tmp instead of /tmp +options nvidia NVreg_TemporaryFilePath=/var/tmp diff --git a/pkgbuilds/nvidia-utils/nvidia-utils.install b/pkgbuilds/nvidia-utils/nvidia-utils.install new file mode 100644 index 0000000..e2bb8f8 --- /dev/null +++ b/pkgbuilds/nvidia-utils/nvidia-utils.install @@ -0,0 +1,21 @@ +post_upgrade() { + # With 595+ open kernel modules, video memory preservation is handled by + # kernel suspend notifiers, making the nvidia suspend/hibernate services unnecessary. + # Disable them for users upgrading from older versions. + if (( $(vercmp $2 595.58.03-1) < 0)); then + for service in nvidia-resume nvidia-hibernate nvidia-suspend nvidia-suspend-then-hibernate; do + if systemctl is-enabled --quiet $service 2>/dev/null; then + echo "Disabling $service (no longer needed with open kernel modules)..." + systemctl disable $service + fi + done + fi +} + +pre_remove() { + for service in nvidia-resume nvidia-hibernate nvidia-suspend nvidia-suspend-then-hibernate; do + if systemctl is-enabled --quiet $service 2>/dev/null; then + systemctl disable $service + fi + done +} diff --git a/pkgbuilds/nvidia-utils/nvidia-utils.sysusers b/pkgbuilds/nvidia-utils/nvidia-utils.sysusers new file mode 100644 index 0000000..0166d15 --- /dev/null +++ b/pkgbuilds/nvidia-utils/nvidia-utils.sysusers @@ -0,0 +1 @@ +u! nvidia-persistenced 143 'NVIDIA Persistence Daemon' diff --git a/pkgbuilds/nvidia-utils/nvidia.rules b/pkgbuilds/nvidia-utils/nvidia.rules new file mode 100644 index 0000000..0ae41b2 --- /dev/null +++ b/pkgbuilds/nvidia-utils/nvidia.rules @@ -0,0 +1,7 @@ +# Device nodes are created by nvidia-modprobe, which is called by the nvidia DDX. +# In case the DDX is not started, the device nodes are never created, so call +# nvidia-modprobe in the udev rules to cover the Wayland/EGLStream and compute +# case without a started display. In the case where vfio-pci is used +# nvidia-modprobe should not be invoked. +ACTION=="add", SUBSYSTEM=="module", KERNEL=="nvidia_drm", TEST!="/dev/nvidia-uvm", \ + RUN+="/usr/bin/nvidia-modprobe -c0 -u" diff --git a/pkgbuilds/nvidia-utils/systemd-homed-override.conf b/pkgbuilds/nvidia-utils/systemd-homed-override.conf new file mode 100644 index 0000000..605d113 --- /dev/null +++ b/pkgbuilds/nvidia-utils/systemd-homed-override.conf @@ -0,0 +1,2 @@ +[Service] +Environment="SYSTEMD_HOME_LOCK_FREEZE_SESSION=false" diff --git a/pkgbuilds/nvidia-utils/systemd-suspend-override.conf b/pkgbuilds/nvidia-utils/systemd-suspend-override.conf new file mode 100644 index 0000000..2a45482 --- /dev/null +++ b/pkgbuilds/nvidia-utils/systemd-suspend-override.conf @@ -0,0 +1,2 @@ +[Service] +Environment="SYSTEMD_SLEEP_FREEZE_USER_SESSIONS=false" diff --git a/pkgbuilds/obs-studio/.omarchy/package.json b/pkgbuilds/obs-studio/.omarchy/package.json new file mode 100644 index 0000000..2a67d7e --- /dev/null +++ b/pkgbuilds/obs-studio/.omarchy/package.json @@ -0,0 +1,9 @@ +{ + "source": "local", + "upstream": { + "watch": { + "github": "obsproject/obs-studio", + "pattern": "(?P[0-9]+(?:\\.[0-9]+)*)" + } + } +} diff --git a/pkgbuilds/obs-studio/PKGBUILD b/pkgbuilds/obs-studio/PKGBUILD new file mode 100644 index 0000000..688e890 --- /dev/null +++ b/pkgbuilds/obs-studio/PKGBUILD @@ -0,0 +1,102 @@ +# ARM build from the upstream release source bundle. The vendor CEF bundle +# is x86-only, so this build omits browser sources/docks. AJA is unavailable +# in Arch Linux ARM. Other plugins use upstream architecture detection. + +pkgname=obs-studio +pkgver=32.2.2 +pkgrel=1 +pkgdesc="Free and open source software for video recording and live streaming (without the x86-only CEF browser source)" +arch=('aarch64') +url="https://obsproject.com" +license=('GPL-2.0-or-later') +depends=( + 'alsa-lib' + 'curl' + 'ffmpeg>=8' + 'fontconfig' + 'freetype2' + 'gcc-libs' + 'glib2' + 'glibc' + 'jack' + 'jansson' + 'libdatachannel>=0.24.3' # WebRTC/WHIP output + 'libfdk-aac' + 'libgl' + 'libjuice' + 'libpipewire' + 'libpulse' + 'librist' # MPEGTS output + 'libva' + 'libx11' + 'libxcb' + 'libxcomposite' + 'libxkbcommon' + 'luajit' # Lua scripting + 'mbedtls3>=3.6.1' + 'pciutils' + 'python>=3.14' # Python scripting + 'qrcodegencpp-cmake' # obs-websocket connect QR code + 'qt6-base>=6.8' + 'qt6-svg' + 'qt6-wayland' + 'rnnoise' + 'sndio' + 'speexdsp' + 'srt' # MPEGTS output + 'systemd-libs' + 'util-linux-libs' + 'v4l-utils' + 'wayland' + 'x264' + 'zlib' +) +makedepends=( + 'asio' # header-only, obs-websocket + 'cmake' + 'extra-cmake-modules' + 'nlohmann-json' # header-only, obs-websocket and plugin manager + 'simde' # header-only, SIMD portability layer libobs uses on ARM + 'swig' # scripting bindings + 'uthash' # header-only, libobs + 'vlc' # VLC source plugin builds against libvlc headers, dlopens at runtime + 'websocketpp' # header-only, obs-websocket +) +optdepends=( + 'vlc: VLC media source' + 'v4l2loopback-dkms: V4L2 virtual camera output' +) +source=("https://github.com/obsproject/obs-studio/releases/download/$pkgver/OBS-Studio-$pkgver-Sources.tar.gz") +sha512sums=('6346b5bff255c9178ef57296489af11bc6949076b4084d3dff8d3da923c8b0d2ab64edb312629c54b8d4be1510bb4166b92838a8c6c2db55a7ee9e08108166de') + +build() { + local cmake_options=( + -S "obs-studio-$pkgver-sources" + -B build + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX=/usr + -DCMAKE_INSTALL_LIBDIR=lib + -DENABLE_BROWSER=OFF # no linux-aarch64 CEF exists + -DENABLE_AJA=OFF # libajantv2 is not in ALARM + -DENABLE_LIBFDK=ON + -DENABLE_JACK=ON + -DENABLE_SNDIO=ON + -DENABLE_VLC=ON + -DENABLE_WAYLAND=ON + + # The tarball has no .git for the version machinery to read. + -DOBS_VERSION_OVERRIDE="$pkgver" + -DOBS_COMPILE_DEPRECATION_AS_WARNING=ON + + # Use Arch's side-by-side MbedTLS 3 CMake configuration. + -DMbedTLS_DIR=/usr/lib/mbedtls3/cmake/MbedTLS + + -Wno-dev + ) + cmake "${cmake_options[@]}" + cmake --build build +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} diff --git a/pkgbuilds/omakade/PKGBUILD b/pkgbuilds/omakade/PKGBUILD index 72e502e..75a909f 100644 --- a/pkgbuilds/omakade/PKGBUILD +++ b/pkgbuilds/omakade/PKGBUILD @@ -1,5 +1,5 @@ pkgname=omakade -pkgver=1.9.2 +pkgver=1.10.0 pkgrel=1 pkgdesc='A beautiful, local-first game library for Omarchy' arch=('x86_64' 'aarch64') @@ -11,7 +11,7 @@ depends=('glib2' 'hicolor-icon-theme' 'libsecret' 'libzip' 'openssl' 'qt6-base' makedepends=('cmake' 'ninja' 'pkgconf' 'wayland-protocols') options=('!debug') source=("$pkgname-$pkgver.tar.gz::https://github.com/btsouth/omakade/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") -sha256sums=('4c0ba7eecc036b959b34a8068f5923e8927e59b8ee2f0398d470cd933343c4fc') +sha256sums=('01a4c1aa35c51aba54f57fd0d4eab4a00a14ca2bd4d1b6163264b8ba6fed5b55') build() { cmake -S "$pkgname-$pkgver" -B build -G Ninja \ diff --git a/pkgbuilds/omarchy-billboard-generator/.omarchy/package.json b/pkgbuilds/omarchy-billboard-generator/.omarchy/package.json new file mode 100644 index 0000000..d9c0d05 --- /dev/null +++ b/pkgbuilds/omarchy-billboard-generator/.omarchy/package.json @@ -0,0 +1,12 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h", + "upstream": { + "github": "llstrk/omarchy-billboard-generator", + "checksums": "SHA256SUMS", + "assets": { + "any": "omarchy-billboard-generator.tar.gz" + } + } +} diff --git a/pkgbuilds/omarchy-billboard-generator/PKGBUILD b/pkgbuilds/omarchy-billboard-generator/PKGBUILD new file mode 100644 index 0000000..b714a61 --- /dev/null +++ b/pkgbuilds/omarchy-billboard-generator/PKGBUILD @@ -0,0 +1,82 @@ +# Maintainer: David Heinemeier Hansson + +# Upstream ships a release archive plus a SHA256SUMS manifest, which is what +# the upstream feed in .omarchy/package.json follows. Chromium and ffmpeg are +# runtime dependencies found on PATH, never downloaded. + +pkgname=omarchy-billboard-generator +pkgver=0.2.0 +pkgrel=1 +pkgdesc='Desktop app and CLI that renders animated OMARCHY domain videos' +arch=('any') +url='https://github.com/llstrk/omarchy-billboard-generator' +# MIT covers the project's own code and Apache-2.0 and 0BSD the vendored npm +# dependencies. The Omarchy wordmark, website palettes, taglines and the ttfx +# animation runtime are Omarchy's own material carried over from omarchy-site, +# and PROVENANCE.md says which file is which. +license=('MIT' 'Apache-2.0' '0BSD' 'OFL-1.1' 'LicenseRef-Omarchy') +depends=('nodejs>=22' 'chromium' 'ffmpeg' 'xdg-utils') +makedepends=('npm') +source=("${pkgname}-${pkgver}.tar.gz::${url}/releases/download/v${pkgver}/${pkgname}.tar.gz") +sha256sums=('f499dfc1201ed3d6b8fba271496fcd4c8fc6301427f4f2fc53aab8d9c3f64f67') +# Pure JavaScript and WebAssembly; nothing here is an ELF to strip. +options=('!strip' '!debug') + +_appdir="/usr/lib/${pkgname}" + +build() { + cd "${srcdir}/${pkgname}" + # The same install the upstream release installer performs: locked production + # dependencies, no lifecycle scripts. + npm ci --omit=dev --ignore-scripts --cache "${srcdir}/npm-cache" +} + +check() { + cd "${srcdir}/${pkgname}" + # The catalogs prefer a synced snapshot in the user data directory, so point + # them at an empty one: the build must read the bundled data, not whatever + # the builder's home happens to hold. + export BILLBOARD_DATA_DIR="${srcdir}/data-home" BILLBOARD_CACHE_DIR="${srcdir}/cache-home" + # Upstream's own startup check, plus the catalogs that load the bundled data. + node bin/omarchy-billboard --help >/dev/null + node bin/omarchy-billboard --list-themes >/dev/null + node bin/omarchy-billboard --list-languages >/dev/null + node bin/omarchy-billboard --list-animations >/dev/null + node bin/omarchy-billboard-app --help >/dev/null +} + +package() { + cd "${srcdir}/${pkgname}" + + install -dm755 "${pkgdir}${_appdir}" + # Only what runs: no tests, release scripts, CI, or the curl|bash installer. + cp -a bin src app web assets data examples node_modules package.json "${pkgdir}${_appdir}/" + + # The entry points resolve their sources through the real path of the + # script, so a symlink is enough and keeps the launcher's node on PATH. + install -dm755 "${pkgdir}/usr/bin" + ln -s "${_appdir}/bin/omarchy-billboard" "${pkgdir}/usr/bin/omarchy-billboard" + ln -s "${_appdir}/bin/omarchy-billboard-app" "${pkgdir}/usr/bin/omarchy-billboard-app" + + # Written here rather than shipped as a second source: sync-upstream rewrites + # sha256sums wholesale from the release manifest, so a local file's checksum + # would not survive the first version bump. The WM class is what Chromium + # derives for this app-mode window, so the launcher's icon follows it. + install -dm755 "${pkgdir}/usr/share/applications" + cat > "${pkgdir}/usr/share/applications/${pkgname}.desktop" <<'DESKTOP' +[Desktop Entry] +Version=1.0 +Type=Application +Name=Omarchy Billboard Generator +Comment=Create animated Omarchy domain videos locally +Exec=omarchy-billboard-app +Icon=omarchy-billboard-generator +Terminal=false +Categories=AudioVideo;Video; +StartupWMClass=chrome-127.0.0.1__omarchy-billboard-Default +DESKTOP + install -Dm644 app/icon.svg "${pkgdir}/usr/share/icons/hicolor/scalable/apps/${pkgname}.svg" + + install -Dm644 -t "${pkgdir}/usr/share/doc/${pkgname}" README.md THEMES.md PROVENANCE.md + install -Dm644 -t "${pkgdir}/usr/share/licenses/${pkgname}" LICENSE PROVENANCE.md assets/fonts/*-OFL.txt +} diff --git a/pkgbuilds/omarchy-nvim/PKGBUILD b/pkgbuilds/omarchy-nvim/PKGBUILD index d8f6414..f0026da 100644 --- a/pkgbuilds/omarchy-nvim/PKGBUILD +++ b/pkgbuilds/omarchy-nvim/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Ryan Hughes pkgname=omarchy-nvim -pkgver=2026.8.13 +pkgver=2026.9.21 pkgrel=1 pkgdesc="Pre-built LazyVim configuration with cached plugins" arch=('any') diff --git a/pkgbuilds/omarchy-nvim/lua/plugins/all-themes.lua b/pkgbuilds/omarchy-nvim/lua/plugins/all-themes.lua index 3bbf7a4..0897b96 100644 --- a/pkgbuilds/omarchy-nvim/lua/plugins/all-themes.lua +++ b/pkgbuilds/omarchy-nvim/lua/plugins/all-themes.lua @@ -77,7 +77,7 @@ return { priority = 1000, }, { - "gthelding/monokai-pro.nvim", + "loctvl842/monokai-pro.nvim", lazy = true, priority = 1000, }, diff --git a/pkgbuilds/omarchy-settings/PKGBUILD b/pkgbuilds/omarchy-settings/PKGBUILD index 9a10ab7..5eb5ca8 100644 --- a/pkgbuilds/omarchy-settings/PKGBUILD +++ b/pkgbuilds/omarchy-settings/PKGBUILD @@ -10,9 +10,9 @@ # provenance only (empty when cut from a bare commit). omarchy and # omarchy-settings must always carry identical _tag/_commit/pkgver/sha256sums. pkgname='omarchy-settings' -_tag='v4.0.3' -_commit='0534987009061cbe2dacdde4ad564092ab698d12' -pkgver=4.0.3 +_tag='v4.0.4' +_commit='c668141e9c42b13c80c9ca4ea108e11708c5e8a5' +pkgver=4.0.4 pkgrel=1 pkgdesc='Omarchy user defaults, /etc/skel content, fonts, plymouth theme, and support helpers' # Arch-specific because the shipped /etc tree is not the same on every @@ -125,7 +125,7 @@ if [[ -n "${OMARCHY_SRC:-}" ]]; then sha256sums=() else source=("omarchy::git+https://github.com/basecamp/omarchy.git#commit=${_commit}") - sha256sums=('3558399c3ddc0b9067d63c0d617d695829d8293689e9319a304839c3a6e0f9f3') + sha256sums=('8371b148aa06e9d0627c9e11668ef2dfdf54c4a9e5ffbee5aee08c3945073618') fi prepare() { diff --git a/pkgbuilds/omarchy-steam-fex/.omarchy/package.json b/pkgbuilds/omarchy-steam-fex/.omarchy/package.json new file mode 100644 index 0000000..2a9719d --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/.omarchy/package.json @@ -0,0 +1,3 @@ +{ + "source": "local" +} diff --git a/pkgbuilds/omarchy-steam-fex/LICENSE b/pkgbuilds/omarchy-steam-fex/LICENSE new file mode 100644 index 0000000..f12cfa7 --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) David Heinemeier Hansson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/pkgbuilds/omarchy-steam-fex/PKGBUILD b/pkgbuilds/omarchy-steam-fex/PKGBUILD new file mode 100644 index 0000000..c984a57 --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/PKGBUILD @@ -0,0 +1,25 @@ +pkgname=omarchy-steam-fex +pkgver=1.0.0 +pkgrel=2 +pkgdesc='Steam launcher with login workarounds for Apple Silicon using muvm and FEX' +arch=('aarch64') +url='https://github.com/omacom/omarchy-pkgs/tree/master/pkgbuilds/omarchy-steam-fex' +license=('MIT') +checkdepends=('python') +source=('omarchy-launch-steam' 'LICENSE' 'test-launcher.py') +sha256sums=('d89559ef88b3589e7d972b44a4a29e418605542f44f2756afd24ed3ce3227609' + '717ba1949502290f8e47688ae2e323acd06c8ca47aec9f7596b15f678c1af4a2' + '1d31b9ed09292b79dc86d9d4b3fff01d19e8210fdc5e3b13e78b846671808ed5') + +check() { + python test-launcher.py +} + +package() { + # These are runtime-only dependencies; the Asahi stack is not needed to + # assemble or test the scripts in a standard Arch Linux ARM builder. + depends=('bash' 'coreutils' 'python' 'steam' 'muvm' 'FEX-Emu') + + install -Dm755 omarchy-launch-steam "$pkgdir/usr/bin/omarchy-launch-steam" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} diff --git a/pkgbuilds/omarchy-steam-fex/README.md b/pkgbuilds/omarchy-steam-fex/README.md new file mode 100644 index 0000000..a4f6513 --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/README.md @@ -0,0 +1,17 @@ +# Steam launcher for Apple Silicon + +`omarchy-steam-fex` provides `omarchy-launch-steam` for the Asahi Linux `steam`, `muvm`, and `FEX-Emu` stack. Those runtime packages come from `asahi-alarm`; `FEX-Emu` provides `FEXBash`. The package is restricted to aarch64 and assumes that stack's `~/.local/share/fex-steam/steam-launcher/bin_steam.sh` layout. + +The launcher runs Steam through `muvm` and `FEXBash` with the CEF occlusion workaround. Once Steam's client files are present, it also disables bootstrap verification and repair and patches the Steam UI network initialization block that can leave login waiting indefinitely. Initial bootstrap keeps the normal bootstrap flags. If the FEX launcher is unavailable, it falls back to `steam`. + +`omarchy-launch-steam --prepare` writes the current user's desktop override with `Exec=omarchy-launch-steam %U` and applies the same UI patch. Each matching chunk is backed up as `.omarchy-bak` before its first patch; existing backups are preserved, and already-patched and unrecognized chunks are left unchanged. The regex depends on Valve's client code and may need updating when that code changes. + +Only the launcher and license are installed. Steam's system desktop entry and downloaded client files remain outside this package's ownership; preparation runs as the desktop user, never in a package installation hook. A downstream Omarchy package that already owns the launcher must release that path before this package is installed, or in the same upgrade transaction. + +Runtime dependencies are declared in `package()` so assembling the scripts does not require the Asahi stack in the build container. They remain required dependencies in the resulting package. `check()` runs the offline launcher tests using temporary homes and mocked Steam/muvm commands. To run them directly: + +```sh +python pkgbuilds/omarchy-steam-fex/test-launcher.py +``` + +The launcher was extracted from [omarchy-mx-mac commit 5e8e1188](https://github.com/scottjones/omarchy-mx-mac/commit/5e8e1188e39fae70cf4f7bda1a1d85d66eccae51), credited to Scott Jones, dl-alexandre, and Santeri Hernejärvi. The extraction replaces `omarchy-cmd-present` with `command -v`, removing the dependency on Omarchy itself. The launcher matches the version published in [omarchy-pkgs-aarch64](https://github.com/omarchy-mac/omarchy-pkgs-aarch64/tree/main/pkgbuilds/omarchy-steam-fex). diff --git a/pkgbuilds/omarchy-steam-fex/omarchy-launch-steam b/pkgbuilds/omarchy-steam-fex/omarchy-launch-steam new file mode 100755 index 0000000..f61d0db --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/omarchy-launch-steam @@ -0,0 +1,110 @@ +#!/bin/bash + +# omarchy:summary=Launch Steam, applying Asahi muvm login workarounds on Apple Silicon +# omarchy:group=launch +# omarchy:args=[--prepare|steam-args...] + +set -euo pipefail + +steam_root="$HOME/.local/share/Steam" + +write_steam_desktop() { + local app_dir="$HOME/.local/share/applications" + mkdir -p "$app_dir" + cat >"$app_dir/steam.desktop" <<'EOF' +[Desktop Entry] +Name=Steam +Comment=Application for managing and playing games on Steam +Exec=omarchy-launch-steam %U +Icon=steam +Terminal=false +Type=Application +Categories=Network;FileTransfer;Game; +MimeType=x-scheme-handler/steam;x-scheme-handler/steamlink; +EOF +} + +steam_client_ready() { + [[ -d $steam_root/steamui ]] && compgen -G "$steam_root/*/steamui.so" >/dev/null +} + +launch_fex_steam() { + local launcher="$1" + shift + + exec muvm -- FEXBash -c 'exec "$@"' omarchy-steam "$launcher" "$@" +} + +patch_steam_ui() { + local steamui="$steam_root/steamui" + [[ -d $steamui ]] || return 0 + + python3 - "$steamui" <<'PY' +import pathlib +import re +import sys + +steamui = pathlib.Path(sys.argv[1]) +pattern = re.compile( + r"const t=\(0,(\w+)\.(\w+)\)\(\"System\.Network\.RegisterForDeviceChanges\"\);" + r"t&&SteamClient\.System\.Network\.RegisterForDeviceChanges\(this\.OnNetworkDevicesChanged\)," + r"\(0,\1\.\2\)\(\"System\.Network\.GetProxyInfo\"\)&&SteamClient\.System\.Network\.GetProxyInfo\(\)\.then\(e=>this\.m_proxyInfo=e\)," + r"\(0,\1\.\2\)\(\"System\.Network\.RegisterForConnectivityTestChanges\"\)&&SteamClient\.System\.Network\.RegisterForConnectivityTestChanges\(this\.OnConnectivityTestStateChanged\)," + r"t\|\|\(this\.m_bIsAwaitingInitialNetworkState=!1\)" +) +replacement = ( + r'const t=(0,\1.\2)("System.Network.RegisterForDeviceChanges")' + r'&&!!(SteamClient.System&&SteamClient.System.Network&&"function"==typeof SteamClient.System.Network.RegisterForDeviceChanges);' + r'try{t&&SteamClient.System.Network.RegisterForDeviceChanges(this.OnNetworkDevicesChanged)}catch(e){}' + r'try{(0,\1.\2)("System.Network.GetProxyInfo")&&SteamClient.System.Network.GetProxyInfo().then(e=>this.m_proxyInfo=e)}catch(e){}' + r'try{(0,\1.\2)("System.Network.RegisterForConnectivityTestChanges")&&SteamClient.System.Network.RegisterForConnectivityTestChanges(this.OnConnectivityTestStateChanged)}catch(e){}' + r'this.m_bIsAwaitingInitialNetworkState=!1,this.m_bIsConnectedToANetwork=!0' +) + +for path in sorted(steamui.glob("chunk~*.js")): + text = path.read_text(errors="replace") + if "m_bIsConnectedToANetwork=!0" in text and "RegisterForDeviceChanges" in text: + continue + updated, count = pattern.subn(replacement, text, count=1) + if count != 1: + continue + backup = path.with_suffix(path.suffix + ".omarchy-bak") + if not backup.exists(): + backup.write_text(text) + path.write_text(updated) +PY +} + +prepare_asahi() { + [[ $(uname -m) == aarch64 ]] || return 0 + write_steam_desktop + patch_steam_ui +} + +if [[ ${1:-} == --prepare ]]; then + prepare_asahi + exit 0 +fi + +if [[ $(uname -m) == aarch64 ]] && command -v muvm >/dev/null && command -v FEXBash >/dev/null; then + launcher="$HOME/.local/share/fex-steam/steam-launcher/bin_steam.sh" + + if [[ -f $launcher ]]; then + steam_args=(-cef-force-occlusion) + if steam_client_ready; then + prepare_asahi + steam_args+=( + -noverifyfiles + -nobootstrapupdate + -skipinitialbootstrap + -norepairfiles + ) + else + write_steam_desktop + fi + + launch_fex_steam "$launcher" "${steam_args[@]}" "$@" + fi +fi + +exec steam "$@" diff --git a/pkgbuilds/omarchy-steam-fex/test-launcher.py b/pkgbuilds/omarchy-steam-fex/test-launcher.py new file mode 100644 index 0000000..cb4fcd9 --- /dev/null +++ b/pkgbuilds/omarchy-steam-fex/test-launcher.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Offline launcher tests: fake Steam/muvm/FEX, real Bash and Python, temporary HOME.""" + +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + + +LAUNCHER = Path(os.environ.get( + 'STEAM_LAUNCHER_TEST_SCRIPT', + Path(__file__).with_name('omarchy-launch-steam'), +)) + +# A representative minified Steam network initialization block, including +# surrounding code that must survive the patch. Deliberately not a regex. +ORIGINAL = ( + 'before();const t=(0,Ab.cd)("System.Network.RegisterForDeviceChanges");' + 't&&SteamClient.System.Network.RegisterForDeviceChanges(this.OnNetworkDevicesChanged),' + '(0,Ab.cd)("System.Network.GetProxyInfo")&&SteamClient.System.Network.GetProxyInfo().then(e=>this.m_proxyInfo=e),' + '(0,Ab.cd)("System.Network.RegisterForConnectivityTestChanges")&&SteamClient.System.Network.RegisterForConnectivityTestChanges(this.OnConnectivityTestStateChanged),' + 't||(this.m_bIsAwaitingInitialNetworkState=!1);after();' +) +SKIP_BOOTSTRAP = ['-noverifyfiles', '-nobootstrapupdate', '-skipinitialbootstrap', '-norepairfiles'] + +MOCK = ''' +import json, os +from pathlib import Path +import shutil, sys +name = Path(sys.argv[0]).name +if name == 'uname': + print(os.environ.get('TEST_ARCH', 'aarch64')) + sys.exit(0) +with open(os.environ['TEST_CALLS'], 'a') as log: + log.write(json.dumps([name, *sys.argv[1:]]) + '\\n') +if name == 'muvm': + assert sys.argv[1:3] == ['--', 'FEXBash'] + os.execv(shutil.which('FEXBash'), sys.argv[2:]) +if name == 'FEXBash': + os.execv('/bin/bash', ['/bin/bash', *sys.argv[1:]]) +sys.exit(int(os.environ.get('TEST_EXIT', '0'))) +''' + + +class SteamLauncherTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix='steam-fex-test-') + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.user_home = self.root / 'home with spaces' + self.user_home.mkdir() + self.tools = self.root / 'tools' + self.tools.mkdir() + self.calls_path = self.root / 'calls.jsonl' + self.env = dict(os.environ, HOME=str(self.user_home), PATH=str(self.tools), + TEST_CALLS=str(self.calls_path), TEST_ARCH='aarch64', TEST_EXIT='0') + for name in ['mkdir', 'cat', 'python3']: + (self.tools / name).symlink_to(shutil.which(name)) + for name in ['uname', 'muvm', 'FEXBash', 'steam']: + self.mock(self.tools / name) + self.fex_launcher = self.user_home / '.local/share/fex-steam/steam-launcher/bin_steam.sh' + self.mock(self.fex_launcher) + self.steam_root = self.user_home / '.local/share/Steam' + self.ui = self.steam_root / 'steamui' + self.desktop = self.user_home / '.local/share/applications/steam.desktop' + + def mock(self, path): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(f'#!{sys.executable}\n' + MOCK) + path.chmod(0o755) + + def run_launcher(self, *args, expected=0): + result = subprocess.run(['/bin/bash', str(LAUNCHER), *args], env=self.env, + capture_output=True, text=True, timeout=10) + self.assertEqual(result.returncode, expected, result.stderr) + return [json.loads(line) for line in self.calls_path.read_text().splitlines()] if self.calls_path.exists() else [] + + def chunk(self, content=ORIGINAL, name='chunk~network.js'): + self.ui.mkdir(parents=True, exist_ok=True) + path = self.ui / name + path.write_text(content) + return path + + def client_ready(self): + self.ui.mkdir(parents=True, exist_ok=True) + binary = self.steam_root / 'ubuntu12_64/steamui.so' + binary.parent.mkdir(parents=True, exist_ok=True) + binary.touch() + + def assert_desktop(self): + text = self.desktop.read_text() + self.assertIn('\nExec=omarchy-launch-steam %U\n', text) + self.assertIn('x-scheme-handler/steam;x-scheme-handler/steamlink;', text) + + def assert_fex(self, calls, flags, user_args): + args = [str(self.fex_launcher), *flags, *user_args] + self.assertEqual(calls, [ + ['muvm', '--', 'FEXBash', '-c', 'exec "$@"', 'omarchy-steam', *args], + ['FEXBash', '-c', 'exec "$@"', 'omarchy-steam', *args], + ['bin_steam.sh', *flags, *user_args], + ]) + + def test_prepare_without_client_only_writes_user_desktop(self): + self.assertEqual(self.run_launcher('--prepare'), []) + self.assert_desktop() + self.assertFalse(self.steam_root.exists()) + + def test_prepare_patches_matching_chunks_and_preserves_original(self): + for identifier in ['Ab.cd', '_A2.x9']: + with self.subTest(identifier=identifier): + original = ORIGINAL.replace('Ab.cd', identifier) + path = self.chunk(original, f'chunk~{identifier}.js') + self.assertEqual(self.run_launcher('--prepare'), []) + patched = path.read_text() + self.assertTrue(patched.startswith('before();')) + self.assertTrue(patched.endswith(';after();')) + self.assertIn(f'const t=(0,{identifier})("System.Network.RegisterForDeviceChanges")&&!!', patched) + self.assertEqual(patched.count('catch(e){}'), 3) + self.assertIn('this.m_bIsAwaitingInitialNetworkState=!1,this.m_bIsConnectedToANetwork=!0', patched) + self.assertEqual(path.with_suffix('.js.omarchy-bak').read_text(), original) + self.assert_desktop() + + def test_repeated_prepare_is_idempotent_and_keeps_first_backup(self): + path = self.chunk() + self.run_launcher('--prepare') + patched, modified = path.read_bytes(), path.stat().st_mtime_ns + self.run_launcher('--prepare') + self.assertEqual(path.read_bytes(), patched) + self.assertEqual(path.stat().st_mtime_ns, modified) + path.write_text(ORIGINAL.replace('Ab.cd', 'New.api')) + self.run_launcher('--prepare') + self.assertIn('(0,New.api)', path.read_text()) + self.assertEqual(path.with_suffix('.js.omarchy-bak').read_text(), ORIGINAL) + + def test_unmatched_code_and_other_filenames_are_untouched(self): + for name, content in [('chunk~changed.js', 'otherNetworkCode();'), ('other.js', ORIGINAL)]: + path = self.chunk(content, name) + self.run_launcher('--prepare') + self.assertEqual(path.read_text(), content) + self.assertFalse(path.with_suffix('.js.omarchy-bak').exists()) + + def test_only_first_matching_block_is_patched(self): + path = self.chunk(ORIGINAL + ORIGINAL) + self.run_launcher('--prepare') + self.assertEqual(path.read_text().count('m_bIsConnectedToANetwork=!0'), 1) + self.assertIn(ORIGINAL, path.read_text()) + + def test_initial_launch_keeps_bootstrap_enabled_and_preserves_arguments(self): + args = ['steam://rungameid/123', 'argument with spaces', '$(touch unwanted)', ''] + calls = self.run_launcher(*args) + self.assert_fex(calls, ['-cef-force-occlusion'], args) + self.assert_desktop() + self.assertFalse(self.steam_root.exists()) + + def test_ui_directory_alone_does_not_disable_bootstrap(self): + path = self.chunk() + self.assert_fex(self.run_launcher(), ['-cef-force-occlusion'], []) + self.assertEqual(path.read_text(), ORIGINAL) + + def test_ready_launch_patches_and_disables_bootstrap(self): + path = self.chunk() + self.client_ready() + args = ['steam://open/main'] + self.assert_fex(self.run_launcher(*args), ['-cef-force-occlusion', *SKIP_BOOTSTRAP], args) + self.assertIn('m_bIsConnectedToANetwork=!0', path.read_text()) + self.assert_desktop() + + def test_missing_fex_components_fall_back_without_modifying_user_files(self): + for missing in [self.tools / 'muvm', self.tools / 'FEXBash', self.fex_launcher]: + with self.subTest(missing=missing.name): + missing.unlink() + self.env['TEST_EXIT'] = '23' + self.assertEqual(self.run_launcher('arg with spaces', expected=23), [['steam', 'arg with spaces']]) + self.assertFalse(self.desktop.exists()) + self.calls_path.unlink() + self.mock(missing) + + def test_x86_prepare_is_noop_and_launch_falls_back(self): + self.env['TEST_ARCH'] = 'x86_64' + path = self.chunk() + self.assertEqual(self.run_launcher('--prepare'), []) + self.assertFalse(self.desktop.exists()) + self.assertEqual(path.read_text(), ORIGINAL) + self.assertEqual(self.run_launcher('steam://open/main'), [['steam', 'steam://open/main']]) + + def test_fex_exit_status_is_preserved(self): + self.env['TEST_EXIT'] = '29' + self.assert_fex(self.run_launcher(expected=29), ['-cef-force-occlusion'], []) + + +if __name__ == '__main__': + unittest.main() diff --git a/pkgbuilds/omarchy-task-manager/.omarchy/package.json b/pkgbuilds/omarchy-task-manager/.omarchy/package.json new file mode 100644 index 0000000..8b66e2d --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/.omarchy/package.json @@ -0,0 +1,11 @@ +{ + "source": "local", + "channels": ["edge"], + "upstream": { + "watch": { + "github": "tcballard/omarchy-task-manager", + "pattern": "v(?P[0-9]+\\.[0-9]+\\.[0-9]+)", + "allow_prerelease": true + } + } +} diff --git a/pkgbuilds/omarchy-task-manager/PKGBUILD b/pkgbuilds/omarchy-task-manager/PKGBUILD new file mode 100644 index 0000000..94f01cd --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/PKGBUILD @@ -0,0 +1,44 @@ +# Canonical recipe. scripts/package-source.sh fills the release source checksum. +# Maintainer: Tom Ballard (tcballard) +pkgname=omarchy-task-manager +pkgver=0.0.3 +pkgrel=3 +url='https://github.com/tcballard/omarchy-task-manager' +pkgdesc='Floating native task manager for Omarchy (preview)' +arch=('x86_64') +license=('MIT') +depends=('qt6-base' 'qt6-declarative' 'qt6-wayland' 'qt6-svg' 'hicolor-icon-theme' 'gcc-libs' 'glibc' 'glib2' 'systemd' 'coreutils') +optdepends=('gdb: live process core dumps' 'nvidia-utils: NVIDIA device telemetry') +makedepends=('cmake' 'ninja' 'rust' 'cargo') +checkdepends=('python' 'desktop-file-utils') +source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz" + "pause-resume-test.patch" + "worker-shutdown.patch") +sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85' + 'ce191fc8dc7e4f7018193aa4982d58fd463f66b37e3129b3acce46b7a1c9a89e' + '1142aaedf8739bf2ed1cb823a03ed7e406d7ff59d7dfb72748815cc81cfda96b') + +# Backport upstream PR #11 (sample observation) and PR #12 (worker shutdown). +prepare() { + cd "$srcdir/$pkgname-$pkgver" + patch -Np1 -i "$srcdir/pause-resume-test.patch" + patch -Np1 -i "$srcdir/worker-shutdown.patch" +} + +build() { + cmake -S "$srcdir/$pkgname-$pkgver" -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib + cmake --build build +} + +check() { + cd "$srcdir/$pkgname-$pkgver" + cargo test --locked + ctest --test-dir "$srcdir/build" --output-on-failure + desktop-file-validate packaging/io.github.tcballard.TaskManager.desktop + python tests/protocol.py "$srcdir/build/omarchy-task-manager-core" +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} diff --git a/pkgbuilds/omarchy-task-manager/pause-resume-test.patch b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch new file mode 100644 index 0000000..e256547 --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch @@ -0,0 +1,63 @@ +diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp +index bab79f2..9bed7be 100644 +--- a/tests/bridge_test.cpp ++++ b/tests/bridge_test.cpp +@@ -79,37 +79,53 @@ private slots: + const auto before = bridge.history().size(); + QTest::qWait(1100); + QCOMPARE(bridge.history().size(), before); +- QSignalSpy samples(&bridge, &Bridge::snapshotChanged); ++ // Capture at emission: QTRY processes events, so another timer sample can ++ // replace bridge.snapshot() before the waiting assertion runs. ++ QVariantList samples; ++ QList historySizes; ++ QObject sampleObserver; // Disconnect before the captured lists are destroyed. ++ connect(&bridge, &Bridge::snapshotChanged, &sampleObserver, [&] { ++ samples.append(bridge.snapshot()); ++ historySizes.append(bridge.history().size()); ++ }); + bridge.setPaused(false); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); +- QCOMPARE(bridge.history().size(), 1); ++ QCOMPARE(historySizes.first(), 1); + bridge.active(false); + QTest::qWait(600); + samples.clear(); ++ historySizes.clear(); + bridge.active(true); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); + QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000); + samples.clear(); ++ historySizes.clear(); + bridge.refresh(); + QVERIFY(bridge.busy()); + bridge.setPaused(true); + bridge.setPaused(false); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); ++ QCOMPARE(historySizes.first(), 1); ++ // Keep automatic refresh enabled. The baseline must be followed by a ++ // continuous sample; inspecting only the latest snapshot misses this order. ++ QTRY_VERIFY_WITH_TIMEOUT(samples.count() >= 2, 8000); ++ QVERIFY(samples.at(1).toMap().value("system").toMap() ++ .value("continuous").toBool()); + QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000); + QVERIFY(bridge.prepareManagement({{"category", "startup"}}).isEmpty()); + QVERIFY( diff --git a/pkgbuilds/omarchy-task-manager/worker-shutdown.patch b/pkgbuilds/omarchy-task-manager/worker-shutdown.patch new file mode 100644 index 0000000..d71f21b --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/worker-shutdown.patch @@ -0,0 +1,90 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c940e5e..d1cd40f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -29,7 +29,8 @@ if(BUILD_TESTING) + target_include_directories(bridge-test PRIVATE ui) + target_link_libraries(bridge-test PRIVATE Qt6::Core Qt6::Gui Qt6::Test) + add_dependencies(bridge-test core) +- add_test(NAME bridge COMMAND bridge-test) ++ add_test(NAME bridge COMMAND bridge-test -nocrashhandler) ++ set_tests_properties(bridge PROPERTIES TIMEOUT 60) + endif() + if(BUILD_TESTING) + qt_add_executable(ui-test tests/ui_test.cpp ui/bridge.cpp ui/bridge.h) +@@ -37,5 +38,6 @@ if(BUILD_TESTING) + qt_add_resources(ui-test test_qml PREFIX "/" FILES ${TASK_MANAGER_QML}) + target_link_libraries(ui-test PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::Test) + add_dependencies(ui-test core) +- add_test(NAME ui COMMAND ui-test) ++ add_test(NAME ui COMMAND ui-test -nocrashhandler) ++ set_tests_properties(ui PROPERTIES TIMEOUT 60) + endif() +diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp +index 9bed7be..d6737a4 100644 +--- a/tests/bridge_test.cpp ++++ b/tests/bridge_test.cpp +@@ -3,9 +3,46 @@ + #include + #include + #include ++#include ++#include + class BridgeTest : public QObject { + Q_OBJECT + private slots: ++ void shutdownDoesNotPublish_data() { ++ QTest::addColumn("inFlight"); ++ QTest::addColumn("stopped"); ++ QTest::newRow("idle") << false << false; ++ QTest::newRow("sample-in-flight") << true << false; ++ QTest::newRow("stopped-worker") << true << true; ++ } ++ void shutdownDoesNotPublish() { ++ QFETCH(bool, inFlight); ++ QFETCH(bool, stopped); ++ auto bridge = std::make_unique(); ++ QTRY_VERIFY_WITH_TIMEOUT(!bridge->snapshot().isEmpty(), 8000); ++ bridge->m_timer.stop(); ++ QTRY_VERIFY_WITH_TIMEOUT(!bridge->busy(), 8000); ++ // Stop only the disposable worker owned by this bridge, forcing shutdown ++ // through terminate/kill rather than the normal stdin-EOF exit. ++ if (stopped) { ++ const auto workerPid = bridge->m_worker.processId(); ++ QVERIFY(workerPid > 0); ++ QCOMPARE(::kill(workerPid, SIGSTOP), 0); ++ } ++ if (inFlight) { ++ bridge->refresh(); ++ QVERIFY(bridge->busy()); ++ } ++ QSignalSpy snapshots(bridge.get(), &Bridge::snapshotChanged); ++ QSignalSpy statuses(bridge.get(), &Bridge::statusChanged); ++ QSignalSpy busy(bridge.get(), &Bridge::busyChanged); ++ // Closing the app must not publish late samples or worker-exit errors while ++ // its bridge and UI are being destroyed. Spies outlive the bridge on purpose. ++ bridge.reset(); ++ QCOMPARE(snapshots.count(), 0); ++ QCOMPARE(statuses.count(), 0); ++ QCOMPARE(busy.count(), 0); ++ } + void inspectionErrorsAndDismissal() { + Bridge bridge; + QTRY_VERIFY_WITH_TIMEOUT(!bridge.snapshot().isEmpty(), 8000); +diff --git a/ui/bridge.cpp b/ui/bridge.cpp +index 86b5b47..b1fb303 100644 +--- a/ui/bridge.cpp ++++ b/ui/bridge.cpp +@@ -82,7 +82,12 @@ Bridge::Bridge(QObject *p) : QObject(p), m_rows(this) { + m_clock.start(); + } + Bridge::~Bridge() { ++ // waitForFinished() can emit readyRead/error/finished synchronously. Quiesce ++ // callbacks before waiting, including if QProcess outlives our final wait and ++ // emits from its destructor after the other Bridge members are gone. + m_timer.stop(); ++ m_timeout.stop(); ++ m_worker.disconnect(this); + m_worker.closeWriteChannel(); + if (!m_worker.waitForFinished(600)) { + m_worker.terminate(); diff --git a/pkgbuilds/omarchy/PKGBUILD b/pkgbuilds/omarchy/PKGBUILD index 684b448..41314ea 100644 --- a/pkgbuilds/omarchy/PKGBUILD +++ b/pkgbuilds/omarchy/PKGBUILD @@ -10,9 +10,9 @@ # provenance only (empty when cut from a bare commit). omarchy and # omarchy-settings must always carry identical _tag/_commit/pkgver/sha256sums. pkgname='omarchy' -_tag='v4.0.3' -_commit='0534987009061cbe2dacdde4ad564092ab698d12' -pkgver=4.0.3 +_tag='v4.0.4' +_commit='c668141e9c42b13c80c9ca4ea108e11708c5e8a5' +pkgver=4.0.4 pkgrel=1 pkgdesc='Beautiful, modern, and opinionated Arch Linux by DHH' # The payload is architecture-independent, but the dependency set is not: the @@ -94,7 +94,7 @@ if [[ -n "${OMARCHY_SRC:-}" ]]; then sha256sums=() else source=("omarchy::git+https://github.com/basecamp/omarchy.git#commit=${_commit}") - sha256sums=('3558399c3ddc0b9067d63c0d617d695829d8293689e9319a304839c3a6e0f9f3') + sha256sums=('8371b148aa06e9d0627c9e11668ef2dfdf54c4a9e5ffbee5aee08c3945073618') fi prepare() { diff --git a/pkgbuilds/omasnap/PKGBUILD b/pkgbuilds/omasnap/PKGBUILD index 24b7e08..c5cf340 100644 --- a/pkgbuilds/omasnap/PKGBUILD +++ b/pkgbuilds/omasnap/PKGBUILD @@ -1,11 +1,11 @@ # Maintainer: Tobi Lütke pkgname=omasnap -pkgver=1.20.1 +pkgver=1.21.0 pkgrel=1 pkgdesc="Native Wayland screenshot and annotation overlay for Hyprland" arch=('x86_64' 'aarch64') -url="https://github.com/tobi/omasnap" +url="https://github.com/omacom/omasnap" license=('MIT' 'OFL-1.1') depends=( 'hyprland' @@ -25,7 +25,7 @@ makedepends=( options=('!debug') source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") -sha256sums=('daf8fd17a81890661eebab791e6e78216c331e040043a1a6b72e638d3626b431') +sha256sums=('2f842edf67631825fa1e102876020040ea3e21341221a428b6aeee5580a9d928') build() { cmake -S "$pkgname-$pkgver" -B build -G Ninja \ @@ -35,8 +35,24 @@ build() { } check() { - QT_QPA_PLATFORM=offscreen \ - ./build/omasnap-smoke "$srcdir/omasnap-smoke-output" + # The smoke suite fsyncs its working documents under /tmp. On the CI + # droplets the build leaves about a gigabyte of dirty pages, and the + # flush that starts a few seconds into the suite makes those fsyncs stall + # long enough to overrun the suite's 5-second settle windows. Flush first. + local runtime_dir status started + started=$(date +%s%N); sync + echo "flushed dirty pages in $(( ($(date +%s%N) - started) / 1000000 )) ms" + runtime_dir=$(mktemp -d /dev/shm/omasnap-runtime.XXXXXX) + if QT_QPA_PLATFORM=offscreen QT_FORCE_STDERR_LOGGING=1 \ + XDG_RUNTIME_DIR="$runtime_dir" \ + ./build/omasnap-smoke "$srcdir/omasnap-smoke-output"; then + status=0 + else + status=$? + echo "omasnap-smoke exited with status $status" >&2 + fi + rm -r -- "$runtime_dir" + return "$status" } package() { diff --git a/pkgbuilds/omaspeak-bin/.omarchy/package.json b/pkgbuilds/omaspeak-bin/.omarchy/package.json new file mode 100644 index 0000000..db153c3 --- /dev/null +++ b/pkgbuilds/omaspeak-bin/.omarchy/package.json @@ -0,0 +1,4 @@ +{ + "source": "local", + "release_ring": "fast" +} diff --git a/pkgbuilds/omaspeak-bin/PKGBUILD b/pkgbuilds/omaspeak-bin/PKGBUILD new file mode 100644 index 0000000..c60ac9e --- /dev/null +++ b/pkgbuilds/omaspeak-bin/PKGBUILD @@ -0,0 +1,84 @@ +# Maintainer: Jacob Vincent Mink + +pkgname=omaspeak-bin +_pkgname=${pkgname%-bin} +pkgver=0.0.3 +_upstream_ver=0.0.3 +pkgrel=4 +pkgdesc='Local-first text-to-speech application and daemon (pre-built binary)' +arch=('x86_64' 'aarch64') +url='https://github.com/jacob-vincent-mink/omaspeak' +license=('MIT' 'Apache-2.0' 'BSD-3-Clause') +depends=( + 'alsa-utils' + 'gcc-libs' + 'glibc' +) +optdepends=( + 'pipewire-audio: audio playback through pw-play' + 'openvino: Intel CPU acceleration runtime' + 'openvino-intel-gpu-plugin: Intel GPU device support for OpenVINO' + 'openvino-intel-npu-plugin: Intel NPU device support for OpenVINO' + 'cuda: NVIDIA CUDA libraries for an external CUDA runtime bundle' + 'cudnn: NVIDIA neural-network libraries for an external CUDA runtime bundle' +) +provides=("${_pkgname}=${pkgver}") +conflicts=("${_pkgname}") +install="${pkgname}.install" +options=('!strip' '!debug') + +source=('package-remove' 'remove-user-services.hook') +sha256sums=('f1b527448529b45fee2f96b4c0a19b11087377c0ac9f43842bab0d04fcfd3b9f' + '9f1a0c2f5031fcd5905de77643a8727b792e07c582c09c2ba179f0714f118b20') + +source_x86_64=("${_pkgname}-${_upstream_ver}-linux-x86_64.tar.xz::${url}/releases/download/v${_upstream_ver}/${_pkgname}-${_upstream_ver}-linux-x86_64.tar.xz") +source_aarch64=("${_pkgname}-${_upstream_ver}-linux-aarch64.tar.xz::${url}/releases/download/v${_upstream_ver}/${_pkgname}-${_upstream_ver}-linux-aarch64.tar.xz") + +sha256sums_x86_64=('c72428bf6989582b5aa802f39e9390e4cacf26f7fbfacf76645118286c7d1ab2') +sha256sums_aarch64=('88fc4ea8c275b9d5b9d41602d32dbca77ee30de0fc7dcbc06ad0e819fd41545a') + +package() { + install -Dm755 "${srcdir}/package-remove" "${pkgdir}/usr/lib/${_pkgname}/package-remove" + install -Dm644 "${srcdir}/remove-user-services.hook" \ + "${pkgdir}/usr/share/libalpm/hooks/30-${_pkgname}-remove-user-services.hook" + + local release_root="${srcdir}/${_pkgname}-${_upstream_ver}-linux-${CARCH}" + + install -Dm755 "${release_root}/${_pkgname}" "${pkgdir}/usr/bin/${_pkgname}" + + install -Dm755 \ + "${release_root}/lib/libaudiocpp.so.0.1.0" \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so.0.1.0" + ln -s libaudiocpp.so.0.1.0 \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so.0" + ln -s libaudiocpp.so.0 \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so" + + install -Dm644 \ + "${release_root}/packaging/systemd/${_pkgname}.service" \ + "${pkgdir}/usr/lib/systemd/user/${_pkgname}.service" + + local document + for document in \ + README.md \ + INSTALL.md \ + ACCELERATOR_SETUP.md \ + CHANGELOG.md \ + RELEASE_NOTES.md \ + DEMO.md \ + RUNTIME.md \ + config.example.toml; do + install -Dm644 \ + "${release_root}/${document}" \ + "${pkgdir}/usr/share/doc/${pkgname}/${document}" + done + cp -r \ + "${release_root}/assets" \ + "${release_root}/benchmarks" \ + "${pkgdir}/usr/share/doc/${pkgname}/" + + install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" + cp -r \ + "${release_root}/licenses/." \ + "${pkgdir}/usr/share/licenses/${pkgname}/" +} diff --git a/pkgbuilds/omaspeak-bin/omaspeak-bin.install b/pkgbuilds/omaspeak-bin/omaspeak-bin.install new file mode 100644 index 0000000..4a33226 --- /dev/null +++ b/pkgbuilds/omaspeak-bin/omaspeak-bin.install @@ -0,0 +1,11 @@ +# shellcheck shell=sh + +post_install() { + echo ':: Run omaspeak setup to configure a model and runtime.' + echo ':: The optional user service remains disabled; on-demand speech works without it.' + echo ':: Accelerator instructions: /usr/share/doc/omaspeak-bin/ACCELERATOR_SETUP.md' +} + +post_upgrade() { + echo ':: Run omaspeak setup check to verify the current configuration.' +} diff --git a/pkgbuilds/omaspeak-bin/package-remove b/pkgbuilds/omaspeak-bin/package-remove new file mode 100644 index 0000000..1b45b95 --- /dev/null +++ b/pkgbuilds/omaspeak-bin/package-remove @@ -0,0 +1,150 @@ +#!/bin/bash +# Invoked only by the package's Remove/PreTransaction ALPM hook. +set -eu + +# systemd's configuration parser throws the whitespace around an assignment away +# (parse_line() runs both halves through strstrip()), so a drop-in written as +# ExecStart = /home/alice/build/omawake daemon +# picks the executable just as surely as the unspaced form does. +readonly execstart_assignment='^[[:space:]]*ExecStart[[:space:]]*=' + +owned_unit() { + local commands + [[ -f $1 && ! -L $1 ]] || return 1 + # Only a unit in the very shape the application generates is ever deleted; + # anything else, however it is spaced, stays somebody's own file. + commands=$(grep '^ExecStart=' "$1") || return 1 + [[ $commands != *$'\n'* ]] || return 1 + grep -Eq "^ExecStart=\"?/usr/bin/$2\"?([[:space:]]|$)" <<< "$commands" +} + +# Decode systemd's shell_maybe_quote() output, including cescape_char() escapes. +# Never evaluate manager-controlled values as shell syntax. +unquote_manager_value() { + local text=$1 decoded= character octal + if [[ $text != '$'* ]]; then + unquoted_value=$text + return 0 + fi + [[ $text == \$\'*\' ]] || return 1 + text=${text:2:${#text}-3} + while [[ -n $text ]]; do + if [[ ${text:0:1} != \\ ]]; then + decoded+=${text:0:1} + text=${text:1} + continue + fi + case ${text:1:1} in + \\ | "'") decoded+=${text:1:1} ;; + a | b | f | n | r | t | v) + printf -v character '%b' "\\${text:1:1}" + decoded+=$character + ;; + [0-3]) + octal=${text:1:3} + [[ $octal =~ ^[0-3][0-7]{2}$ && $octal != 000 ]] || return 1 + printf -v character '%b' "\\0$octal" + decoded+=$character + text=${text:4} + continue + ;; + *) return 1 ;; + esac + text=${text:2} + done + unquoted_value=$decoded +} + +remove_for_user() { + local app=$1 user_home=$2 runtime=$3 config="$2/.config" unit="$1.service" + local online=false effective path target manager_environment load_state + if [[ -S $runtime/bus || -S $runtime/systemd/private ]]; then + online=true + # Do not evaluate shell syntax from a user manager's environment. + manager_environment=$(systemctl --user show-environment) || return 1 + while IFS= read -r line; do + case $line in + XDG_CONFIG_HOME=*) + if ! unquote_manager_value "${line#XDG_CONFIG_HOME=}"; then + echo ":: Cannot tell which directory $user_home's manager reads $unit from; removal aborted." >&2 + return 1 + fi + # An XDG_CONFIG_HOME that is not an absolute path is no setting at all: + # the manager itself falls back to the home's .config directory then. + if [[ $unquoted_value == /* ]]; then config=$unquoted_value; fi + ;; + esac + done <<< "$manager_environment" + effective=$(systemctl --user show "$unit" --property=ExecStart --value) || return 1 + if [[ -n $effective && $effective != *"path=/usr/bin/$app ;"* ]]; then + echo ":: Preserving $unit for $user_home: it uses another executable." + return 0 + fi + fi + path="$config/systemd/user/$unit" + if ! $online && grep -qs "$execstart_assignment" "$path.d/"*.conf; then + echo ":: Preserving offline service with an executable override: $path." + return 0 + fi + if [[ -e $path || -L $path ]]; then + if ! owned_unit "$path" "$app"; then + echo ":: Preserving custom or masked unit $path." + return 0 + fi + fi + if $online; then + # Stop first, and fail the package transaction if stopping fails. + load_state=$(systemctl --user show "$unit" --property=LoadState --value) || return 1 + if [[ $load_state != not-found ]]; then + systemctl --user stop "$unit" || return 1 + # A unit that had failed stays failed once it is stopped, which is the one + # state systemd will reset. For every other state it answers that the unit + # is not loaded and exits non-zero, so the reset is asked for only where it + # applies: a healthy unit leaving the transaction never aborts a removal. + if [[ $(systemctl --user show "$unit" --property=ActiveState --value) == failed ]]; then + systemctl --user reset-failed "$unit" || return 1 + fi + # Disabling also removes enablement links outside the normal target. + systemctl --user disable "$unit" || return 1 + fi + fi + # Offline users have no bus. Remove only exact enablement links for this unit. + # All filesystem operations run as the owning user, never as pacman's root. + for target in "$config/systemd/user/"*.wants/"$unit" "$config/systemd/user/"*.requires/"$unit"; do + [[ -L $target ]] || continue + case $(realpath -m -- "$target") in + "$path"|"/usr/lib/systemd/user/$unit") rm -- "$target" ;; + esac + done + if owned_unit "$path" "$app"; then rm -- "$path"; fi + if $online; then systemctl --user daemon-reload || return 1; fi +} + +if [[ ${1-} == --user ]]; then + shift + case ${1-} in omawake|omaspeak) ;; *) exit 2 ;; esac + [[ $# == 3 ]] || exit 2 + remove_for_user "$@" + exit +fi + +[[ $# == 1 ]] || exit 2 +case $1 in omawake|omaspeak) ;; *) exit 2 ;; esac +app=$1 +result=0 +# Include logged-out users as well as active/lingering user managers. +accounts=$(getent passwd) || exit 1 +while IFS=: read -r account _ user_id _ _ user_home _; do + [[ $user_home == /* ]] || continue + runtime="/run/user/$user_id" + if [[ ! -d $user_home/.config/systemd/user && ! -S $runtime/bus && ! -S $runtime/systemd/private ]]; then + continue + fi + if ! runuser -u "$account" -- env -u XDG_CONFIG_HOME \ + XDG_RUNTIME_DIR="$runtime" DBUS_SESSION_BUS_ADDRESS="unix:path=$runtime/bus" \ + "$0" --user "$app" "$user_home" "$runtime"; then + echo ":: Could not clean up $app for $account; removal aborted. Stop/remove the user service and retry." >&2 + result=1 + fi +done <<< "$accounts" +exit "$result" diff --git a/pkgbuilds/omaspeak-bin/remove-user-services.hook b/pkgbuilds/omaspeak-bin/remove-user-services.hook new file mode 100644 index 0000000..f3eec8c --- /dev/null +++ b/pkgbuilds/omaspeak-bin/remove-user-services.hook @@ -0,0 +1,10 @@ +[Trigger] +Operation = Remove +Type = Package +Target = omaspeak-bin + +[Action] +Description = Stop and remove omaspeak user services before package removal +When = PreTransaction +Exec = /usr/lib/omaspeak/package-remove omaspeak +AbortOnFail diff --git a/pkgbuilds/omawake-bin/.omarchy/package.json b/pkgbuilds/omawake-bin/.omarchy/package.json new file mode 100644 index 0000000..db153c3 --- /dev/null +++ b/pkgbuilds/omawake-bin/.omarchy/package.json @@ -0,0 +1,4 @@ +{ + "source": "local", + "release_ring": "fast" +} diff --git a/pkgbuilds/omawake-bin/PKGBUILD b/pkgbuilds/omawake-bin/PKGBUILD new file mode 100644 index 0000000..2ee1d54 --- /dev/null +++ b/pkgbuilds/omawake-bin/PKGBUILD @@ -0,0 +1,84 @@ +# Maintainer: Jacob Vincent Mink + +pkgname=omawake-bin +_pkgname=${pkgname%-bin} +pkgver=0.0.3 +_upstream_ver=0.0.3 +pkgrel=4 +pkgdesc='Configurable local wake-word daemon (pre-built binary)' +arch=('x86_64' 'aarch64') +url='https://github.com/jacob-vincent-mink/omawake' +license=('MIT' 'Apache-2.0' 'BSD-3-Clause') +depends=( + 'alsa-lib' + 'gcc-libs' + 'glibc' +) +optdepends=( + 'pipewire-audio: PipeWire audio support' + 'openvino: Intel runtime for an externally supplied OpenVINO provider bundle' + 'openvino-intel-gpu-plugin: Intel GPU device support for OpenVINO' + 'openvino-intel-npu-plugin: Intel NPU device support for OpenVINO' + 'cuda: NVIDIA CUDA libraries for an external CUDA runtime bundle' + 'cudnn: NVIDIA neural-network libraries for an external CUDA runtime bundle' +) +provides=("${_pkgname}=${pkgver}") +conflicts=("${_pkgname}") +install="${pkgname}.install" +options=('!strip' '!debug') + +source=('package-remove' 'remove-user-services.hook') +sha256sums=('f1b527448529b45fee2f96b4c0a19b11087377c0ac9f43842bab0d04fcfd3b9f' + 'a0bb2e9de807bdb2cc8d0076eac3c21555c910eb8baa06acfba18fea716b9014') + +source_x86_64=("${_pkgname}-${_upstream_ver}-linux-x86_64.tar.xz::${url}/releases/download/v${_upstream_ver}/${_pkgname}-${_upstream_ver}-linux-x86_64.tar.xz") +source_aarch64=("${_pkgname}-${_upstream_ver}-linux-aarch64.tar.xz::${url}/releases/download/v${_upstream_ver}/${_pkgname}-${_upstream_ver}-linux-aarch64.tar.xz") + +sha256sums_x86_64=('fa374341f60760b04c9a97d76f7ad2679463f5b2b673ee6d9c1ceee97d3f0669') +sha256sums_aarch64=('384eb11872c873a33332acf51f567dc4d4e327e57563b61056e4d0aa7fe470eb') + +package() { + install -Dm755 "${srcdir}/package-remove" "${pkgdir}/usr/lib/${_pkgname}/package-remove" + install -Dm644 "${srcdir}/remove-user-services.hook" \ + "${pkgdir}/usr/share/libalpm/hooks/30-${_pkgname}-remove-user-services.hook" + + local release_root="${srcdir}/${_pkgname}-${_upstream_ver}-linux-${CARCH}" + + install -Dm755 "${release_root}/${_pkgname}" "${pkgdir}/usr/bin/${_pkgname}" + + install -Dm755 \ + "${release_root}/lib/libaudiocpp.so.0.1.0" \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so.0.1.0" + ln -s libaudiocpp.so.0.1.0 \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so.0" + ln -s libaudiocpp.so.0 \ + "${pkgdir}/usr/lib/${_pkgname}/libaudiocpp.so" + + install -Dm644 \ + "${release_root}/packaging/systemd/${_pkgname}.service" \ + "${pkgdir}/usr/lib/systemd/user/${_pkgname}.service" + + local document + for document in \ + README.md \ + INSTALL.md \ + ACCELERATOR_SETUP.md \ + CHANGELOG.md \ + RELEASE_NOTES.md \ + DEMO.md \ + RUNTIME.md \ + config.example.toml; do + install -Dm644 \ + "${release_root}/${document}" \ + "${pkgdir}/usr/share/doc/${pkgname}/${document}" + done + cp -r \ + "${release_root}/assets" \ + "${release_root}/benchmarks" \ + "${pkgdir}/usr/share/doc/${pkgname}/" + + install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" + cp -r \ + "${release_root}/licenses/." \ + "${pkgdir}/usr/share/licenses/${pkgname}/" +} diff --git a/pkgbuilds/omawake-bin/omawake-bin.install b/pkgbuilds/omawake-bin/omawake-bin.install new file mode 100644 index 0000000..a5a993e --- /dev/null +++ b/pkgbuilds/omawake-bin/omawake-bin.install @@ -0,0 +1,11 @@ +# shellcheck shell=sh + +post_install() { + echo ':: Run omawake setup to configure a model and runtime.' + echo ':: The optional user service remains disabled; enable it only when desired.' + echo ':: Accelerator instructions: /usr/share/doc/omawake-bin/ACCELERATOR_SETUP.md' +} + +post_upgrade() { + echo ':: Run omawake setup check to verify the current configuration.' +} diff --git a/pkgbuilds/omawake-bin/package-remove b/pkgbuilds/omawake-bin/package-remove new file mode 100644 index 0000000..1b45b95 --- /dev/null +++ b/pkgbuilds/omawake-bin/package-remove @@ -0,0 +1,150 @@ +#!/bin/bash +# Invoked only by the package's Remove/PreTransaction ALPM hook. +set -eu + +# systemd's configuration parser throws the whitespace around an assignment away +# (parse_line() runs both halves through strstrip()), so a drop-in written as +# ExecStart = /home/alice/build/omawake daemon +# picks the executable just as surely as the unspaced form does. +readonly execstart_assignment='^[[:space:]]*ExecStart[[:space:]]*=' + +owned_unit() { + local commands + [[ -f $1 && ! -L $1 ]] || return 1 + # Only a unit in the very shape the application generates is ever deleted; + # anything else, however it is spaced, stays somebody's own file. + commands=$(grep '^ExecStart=' "$1") || return 1 + [[ $commands != *$'\n'* ]] || return 1 + grep -Eq "^ExecStart=\"?/usr/bin/$2\"?([[:space:]]|$)" <<< "$commands" +} + +# Decode systemd's shell_maybe_quote() output, including cescape_char() escapes. +# Never evaluate manager-controlled values as shell syntax. +unquote_manager_value() { + local text=$1 decoded= character octal + if [[ $text != '$'* ]]; then + unquoted_value=$text + return 0 + fi + [[ $text == \$\'*\' ]] || return 1 + text=${text:2:${#text}-3} + while [[ -n $text ]]; do + if [[ ${text:0:1} != \\ ]]; then + decoded+=${text:0:1} + text=${text:1} + continue + fi + case ${text:1:1} in + \\ | "'") decoded+=${text:1:1} ;; + a | b | f | n | r | t | v) + printf -v character '%b' "\\${text:1:1}" + decoded+=$character + ;; + [0-3]) + octal=${text:1:3} + [[ $octal =~ ^[0-3][0-7]{2}$ && $octal != 000 ]] || return 1 + printf -v character '%b' "\\0$octal" + decoded+=$character + text=${text:4} + continue + ;; + *) return 1 ;; + esac + text=${text:2} + done + unquoted_value=$decoded +} + +remove_for_user() { + local app=$1 user_home=$2 runtime=$3 config="$2/.config" unit="$1.service" + local online=false effective path target manager_environment load_state + if [[ -S $runtime/bus || -S $runtime/systemd/private ]]; then + online=true + # Do not evaluate shell syntax from a user manager's environment. + manager_environment=$(systemctl --user show-environment) || return 1 + while IFS= read -r line; do + case $line in + XDG_CONFIG_HOME=*) + if ! unquote_manager_value "${line#XDG_CONFIG_HOME=}"; then + echo ":: Cannot tell which directory $user_home's manager reads $unit from; removal aborted." >&2 + return 1 + fi + # An XDG_CONFIG_HOME that is not an absolute path is no setting at all: + # the manager itself falls back to the home's .config directory then. + if [[ $unquoted_value == /* ]]; then config=$unquoted_value; fi + ;; + esac + done <<< "$manager_environment" + effective=$(systemctl --user show "$unit" --property=ExecStart --value) || return 1 + if [[ -n $effective && $effective != *"path=/usr/bin/$app ;"* ]]; then + echo ":: Preserving $unit for $user_home: it uses another executable." + return 0 + fi + fi + path="$config/systemd/user/$unit" + if ! $online && grep -qs "$execstart_assignment" "$path.d/"*.conf; then + echo ":: Preserving offline service with an executable override: $path." + return 0 + fi + if [[ -e $path || -L $path ]]; then + if ! owned_unit "$path" "$app"; then + echo ":: Preserving custom or masked unit $path." + return 0 + fi + fi + if $online; then + # Stop first, and fail the package transaction if stopping fails. + load_state=$(systemctl --user show "$unit" --property=LoadState --value) || return 1 + if [[ $load_state != not-found ]]; then + systemctl --user stop "$unit" || return 1 + # A unit that had failed stays failed once it is stopped, which is the one + # state systemd will reset. For every other state it answers that the unit + # is not loaded and exits non-zero, so the reset is asked for only where it + # applies: a healthy unit leaving the transaction never aborts a removal. + if [[ $(systemctl --user show "$unit" --property=ActiveState --value) == failed ]]; then + systemctl --user reset-failed "$unit" || return 1 + fi + # Disabling also removes enablement links outside the normal target. + systemctl --user disable "$unit" || return 1 + fi + fi + # Offline users have no bus. Remove only exact enablement links for this unit. + # All filesystem operations run as the owning user, never as pacman's root. + for target in "$config/systemd/user/"*.wants/"$unit" "$config/systemd/user/"*.requires/"$unit"; do + [[ -L $target ]] || continue + case $(realpath -m -- "$target") in + "$path"|"/usr/lib/systemd/user/$unit") rm -- "$target" ;; + esac + done + if owned_unit "$path" "$app"; then rm -- "$path"; fi + if $online; then systemctl --user daemon-reload || return 1; fi +} + +if [[ ${1-} == --user ]]; then + shift + case ${1-} in omawake|omaspeak) ;; *) exit 2 ;; esac + [[ $# == 3 ]] || exit 2 + remove_for_user "$@" + exit +fi + +[[ $# == 1 ]] || exit 2 +case $1 in omawake|omaspeak) ;; *) exit 2 ;; esac +app=$1 +result=0 +# Include logged-out users as well as active/lingering user managers. +accounts=$(getent passwd) || exit 1 +while IFS=: read -r account _ user_id _ _ user_home _; do + [[ $user_home == /* ]] || continue + runtime="/run/user/$user_id" + if [[ ! -d $user_home/.config/systemd/user && ! -S $runtime/bus && ! -S $runtime/systemd/private ]]; then + continue + fi + if ! runuser -u "$account" -- env -u XDG_CONFIG_HOME \ + XDG_RUNTIME_DIR="$runtime" DBUS_SESSION_BUS_ADDRESS="unix:path=$runtime/bus" \ + "$0" --user "$app" "$user_home" "$runtime"; then + echo ":: Could not clean up $app for $account; removal aborted. Stop/remove the user service and retry." >&2 + result=1 + fi +done <<< "$accounts" +exit "$result" diff --git a/pkgbuilds/omawake-bin/remove-user-services.hook b/pkgbuilds/omawake-bin/remove-user-services.hook new file mode 100644 index 0000000..4bac03f --- /dev/null +++ b/pkgbuilds/omawake-bin/remove-user-services.hook @@ -0,0 +1,10 @@ +[Trigger] +Operation = Remove +Type = Package +Target = omawake-bin + +[Action] +Description = Stop and remove omawake user services before package removal +When = PreTransaction +Exec = /usr/lib/omawake/package-remove omawake +AbortOnFail diff --git a/pkgbuilds/once-bin/PKGBUILD b/pkgbuilds/once-bin/PKGBUILD index 7784085..8b0f952 100644 --- a/pkgbuilds/once-bin/PKGBUILD +++ b/pkgbuilds/once-bin/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Kevin McConnell pkgname=once-bin -pkgver=0.3.2 +pkgver=0.3.3 pkgrel=1 pkgdesc='CLI/TUI for installing and managing self-hosted web applications' arch=('x86_64' 'aarch64') @@ -22,8 +22,8 @@ source=("MIT-LICENSE-${pkgver}::https://raw.githubusercontent.com/basecamp/once/ sha256sums=('fa0d1454375cbc7701bc13d916c3ae71e613b8ae718321641e678e09409393c4' 'aa314fe79677eb5f120fcc3d4c42007a93ff7b1ef917382c0c1fdca5633ad46a') -sha256sums_x86_64=('e1da40a0952879580e43623d6fd6002a391ee469b642c98ecddbe00374facbb6') -sha256sums_aarch64=('9bd644e1557521b0b8cab93ba3841747cbee0390aa3aa020bd91bfa66ac51dec') +sha256sums_x86_64=('aef855da263721c6c1072ff5ebc4c17a52af8c8e80c46c5a9dd458e7ca3a7f35') +sha256sums_aarch64=('97e32ba0fdac0ad5e6010851b306e3cb2616285a9eeb2e869ff7e71f4b442bbb') package() { install -Dm755 "once-${pkgver}-${CARCH}" "${pkgdir}/usr/bin/once" diff --git a/pkgbuilds/openai-codex-bin/PKGBUILD b/pkgbuilds/openai-codex-bin/PKGBUILD index 6cdbbb0..64e5687 100644 --- a/pkgbuilds/openai-codex-bin/PKGBUILD +++ b/pkgbuilds/openai-codex-bin/PKGBUILD @@ -2,7 +2,7 @@ # shellcheck disable=SC2034 # Maintainer: Chmouel Boudjnah pkgname=openai-codex-bin -pkgver=0.154.0 +pkgver=0.155.1 pkgrel=1 pkgdesc="Arch Linux package for OpenAI's Codex CLI - Auto Updated" arch=('x86_64' 'aarch64') @@ -21,10 +21,8 @@ source_x86_64=( "codex-${pkgver}-x86_64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-x86_64-unknown-linux-musl.tar.gz" "codex-code-mode-host-${pkgver}-x86_64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-code-mode-host-x86_64-unknown-linux-musl.tar.gz" ) -sha256sums_x86_64=('d7e18b2597ae8f242f5f31ee9e90deef48dbc9edd634d9868fb6435d08c07f02' - 'a68df7cca23c6da7cde175677df7de61c73a234add1333a1254b86d641af01f7') -sha256sums_aarch64=('583b48df32804213bdcd338c2e5adb06b34340821fa757a726cc0a524fa33c27' - '20aefa302c2022b496e32911bf954a5f76c7fd749c6bdb9fbd711e32b66dcbfa') +sha256sums_x86_64=('a0ef8b2debc3bf747e07b1a039354de31300ac0dcc2276498ba281470b5d9115' '9fd083743af55be818aceb351d371fb5136f5b6aa3938f167087373d27067b2d') +sha256sums_aarch64=('d6c7e62fbd688d52ee04f3929d0613705d32a920a42db7a139e366eaf1f4a2d7' '516f2ed76d4ae96c2074d3c08f4576ed1bdc5c3a97e26734d7319de8b6861683') source_aarch64=( "codex-${pkgver}-aarch64.tar.gz::https://github.com/openai/codex/releases/download/rust-v${pkgver}/codex-aarch64-unknown-linux-musl.tar.gz" diff --git a/pkgbuilds/openai-codex-desktop/PKGBUILD b/pkgbuilds/openai-codex-desktop/PKGBUILD index f875388..8ca352d 100644 --- a/pkgbuilds/openai-codex-desktop/PKGBUILD +++ b/pkgbuilds/openai-codex-desktop/PKGBUILD @@ -5,7 +5,7 @@ # the version and checksums below from that repository's package index. pkgname=openai-codex-desktop -pkgver=26.908.70816 +pkgver=26.915.31945 pkgrel=1 pkgdesc="Official ChatGPT desktop app with Codex" arch=('x86_64' 'aarch64') @@ -71,8 +71,8 @@ source_x86_64=("${_deb_x86_64}::${_pool}/${_deb_x86_64}") source_aarch64=("${_deb_aarch64}::${_pool}/${_deb_aarch64}") noextract=("${_deb_x86_64}" "${_deb_aarch64}") sha256sums=('b3a4503b5931f102444bc7015c3cf4e40266cf034e0d682bd2a407dc5b3ee58c') -sha256sums_x86_64=('10ed0c1a880b9975d1f185bf7911a7f514e06b9863cd4ed9561d40063617c854') -sha256sums_aarch64=('d3ec8f1d73b92f203715c26dbf2e0e64375192d00ddaf26f7fbade7777124de8') +sha256sums_x86_64=('d27a9c02919cfe484dcc5f34584b9ea9fd0d7a65c69dcc872b5bdcfa0efb5983') +sha256sums_aarch64=('b94c494b5f0fd7c720fa6fccd5ef609879affc62332ca930ed29b907d537bc6d') package() { cd "${srcdir}" diff --git a/pkgbuilds/openclaw/PKGBUILD b/pkgbuilds/openclaw/PKGBUILD index 71acd80..2cf1665 100644 --- a/pkgbuilds/openclaw/PKGBUILD +++ b/pkgbuilds/openclaw/PKGBUILD @@ -7,7 +7,7 @@ # upstream's release cadence outruns the AUR. pkgname=openclaw -pkgver=2026.9.4 +pkgver=2026.9.5 pkgrel=1 pkgdesc='Multi-channel AI gateway with extensible messaging integrations' arch=(x86_64 aarch64) @@ -29,7 +29,7 @@ optdepends=( 'go: for installing skill tools not packaged for Arch' ) source=($pkgname-$pkgver.tgz::https://registry.npmjs.org/$pkgname/-/$pkgname-$pkgver.tgz) -sha256sums=('4f1f656770461d4677dea755b1899cba12b912b06798c89a59e2f0c18688b761') +sha256sums=('1fb6ef4fae447af14f1e3b1028334f39146d181a66a4cce2848d4f741c636340') options=(!debug !strip) install=$pkgname.install noextract=($pkgname-$pkgver.tgz) diff --git a/pkgbuilds/openvino-genai/.omarchy/package.json b/pkgbuilds/openvino-genai/.omarchy/package.json new file mode 100644 index 0000000..2a9719d --- /dev/null +++ b/pkgbuilds/openvino-genai/.omarchy/package.json @@ -0,0 +1,3 @@ +{ + "source": "local" +} diff --git a/pkgbuilds/openvino-genai/PKGBUILD b/pkgbuilds/openvino-genai/PKGBUILD new file mode 100644 index 0000000..41f52cd --- /dev/null +++ b/pkgbuilds/openvino-genai/PKGBUILD @@ -0,0 +1,95 @@ +# Maintainer: Spencer Bull + +pkgname=openvino-genai +pkgver=2026.3.1.0 +pkgrel=3 +pkgdesc="OpenVINO GenAI C and C++ runtime libraries" +arch=('x86_64') +url="https://github.com/openvinotoolkit/openvino.genai" +license=('Apache-2.0') +options=('!debug' '!lto') +depends=( + 'gcc-libs' + 'glibc' + 'onetbb' + 'openvino=2026.3.1' # Includes libopenvino_c.so. +) +makedepends=( + 'cmake' + 'git' + 'ninja' + 'python' +) +optdepends=( + 'openvino-intel-gpu-plugin: inference on Intel GPUs' + 'openvino-intel-npu-plugin: inference on Intel NPUs' +) + +_commit=56d9685302da2fc5cc7c9689cfab500fd0660a02 +source=( + "openvino.genai::git+$url.git#commit=$_commit" + 'gcc-16-char8_t.patch' + 'format-template-linkage.patch::https://github.com/openvinotoolkit/openvino.genai/commit/398fbc1450f7485368edf52dd82f45eba215d6c9.patch' +) +sha256sums=( + 'SKIP' + '6e685c1e45d4b2314fd55e2f791846cc9086413ba62a6bb5e31be5a5878a243c' + '8c2a3e4bf1d33e00b780da7bec2d5e40b6fd26a4e518359d6ff7c59ca7f649e3' +) + +prepare() { + cd openvino.genai + patch -Np1 -i "$srcdir/gcc-16-char8_t.patch" + # Backport upstream 398fbc14: GCC -O3 can otherwise leave format + # unresolved in libopenvino_genai.so. + patch -Np1 -i "$srcdir/format-template-linkage.patch" + git submodule update --init --recursive +} + +build() { + CFLAGS+=" -ffile-prefix-map=$srcdir=/usr/src/$pkgname" + CXXFLAGS+=" -ffile-prefix-map=$srcdir=/usr/src/$pkgname" + + cmake -S openvino.genai -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_SKIP_RPATH=ON \ + -DENABLE_JS=OFF \ + -DENABLE_MISAKI_CPP=OFF \ + -DENABLE_PYTHON=OFF \ + -DENABLE_SAMPLES=OFF \ + -DENABLE_TESTS=OFF \ + -DENABLE_TOOLS=OFF \ + -DENABLE_XGRAMMAR=OFF + + cmake --build build +} + +check() { + LD_LIBRARY_PATH="$srcdir/build/openvino_genai:$srcdir/build/src/c${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ + python -c 'import ctypes; ctypes.CDLL("libopenvino_c.so"); ctypes.CDLL("libopenvino_genai_c.so")' +} + +package() { + install -d "$pkgdir/usr/lib" "$pkgdir/usr/share/licenses/$pkgname" + + cp -a build/openvino_genai/libopenvino_genai.so* "$pkgdir/usr/lib/" + cp -a build/openvino_genai/libopenvino_tokenizers.so* "$pkgdir/usr/lib/" + cp -a build/src/c/libopenvino_genai_c.so* "$pkgdir/usr/lib/" + + # Consumers such as omawake load the unversioned name dynamically. + # Reject a missing/empty library or a broken symlink before publishing. + if [[ ! -s "$pkgdir/usr/lib/libopenvino_genai_c.so" ]]; then + error 'Required C binding is missing or empty: libopenvino_genai_c.so' + return 1 + fi + + install -Dm644 openvino.genai/LICENSE \ + "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 openvino.genai/third-party-programs.txt \ + "$pkgdir/usr/share/licenses/$pkgname/third-party-programs.txt" + install -Dm644 openvino.genai/thirdparty/openvino_tokenizers/LICENSE \ + "$pkgdir/usr/share/licenses/$pkgname/openvino-tokenizers-LICENSE" + install -Dm644 openvino.genai/thirdparty/openvino_tokenizers/third-party-programs.txt \ + "$pkgdir/usr/share/licenses/$pkgname/openvino-tokenizers-third-party-programs.txt" +} diff --git a/pkgbuilds/openvino-genai/gcc-16-char8_t.patch b/pkgbuilds/openvino-genai/gcc-16-char8_t.patch new file mode 100644 index 0000000..c94559d --- /dev/null +++ b/pkgbuilds/openvino-genai/gcc-16-char8_t.patch @@ -0,0 +1,7 @@ +diff --git a/src/cpp/src/whisper/word_level_timestamps.cpp b/src/cpp/src/whisper/word_level_timestamps.cpp +index 7b3da124..d1f9212f 100644 +--- a/src/cpp/src/whisper/word_level_timestamps.cpp ++++ b/src/cpp/src/whisper/word_level_timestamps.cpp +@@ -327 +327 @@ std::pair, std::vector>> split_toke +- const std::string replacement_char = u8"\uFFFD"; ++ const std::string replacement_char = "\xEF\xBF\xBD"; diff --git a/pkgbuilds/owe-lockfeed/.omarchy/package.json b/pkgbuilds/owe-lockfeed/.omarchy/package.json new file mode 100644 index 0000000..bc11813 --- /dev/null +++ b/pkgbuilds/owe-lockfeed/.omarchy/package.json @@ -0,0 +1,10 @@ +{ + "source": "local", + "release_ring": "fast", + "upstream": { + "watch": { + "github": "omacom/owe", + "pattern": "v(?P[0-9]+(?:\\.[0-9]+)*)" + } + } +} diff --git a/pkgbuilds/owe-lockfeed/PKGBUILD b/pkgbuilds/owe-lockfeed/PKGBUILD new file mode 100644 index 0000000..9df2a24 --- /dev/null +++ b/pkgbuilds/owe-lockfeed/PKGBUILD @@ -0,0 +1,28 @@ +# Maintainer: owe contributors +pkgname=owe-lockfeed +pkgver=0.2.6 +pkgrel=1 +pkgdesc="Lock screen video feed module for the OWE wallpaper engine" +arch=('x86_64' 'aarch64') +url="https://github.com/omacom/owe" +license=('MIT') +depends=('qt6-declarative') +makedepends=('cmake' 'qt6-declarative') +source=("$pkgname-$pkgver.tar.gz::https://github.com/omacom/owe/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('e5c10e60bdfaebed861a3b7515c691a5a78fc0fe3ab29c0e96d934eb1a957cf8') + +build() { + cmake -S "$srcdir/owe-$pkgver/qml-plugin" -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib + cmake --build build +} + +check() { + ctest --test-dir build --output-on-failure +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} diff --git a/pkgbuilds/owe/.omarchy/package.json b/pkgbuilds/owe/.omarchy/package.json new file mode 100644 index 0000000..4f7a928 --- /dev/null +++ b/pkgbuilds/owe/.omarchy/package.json @@ -0,0 +1,14 @@ +{ + "source": "local", + "release_ring": "fast", + "upstream": { + "watch": { + "github": "omacom/owe", + "pattern": "v(?P[0-9]+(?:\\.[0-9]+)*)" + } + }, + "origin": { + "aur": "owe", + "commit": "884100f82cf2940e4c1ef50dff39a99e37e9895e" + } +} diff --git a/pkgbuilds/owe/PKGBUILD b/pkgbuilds/owe/PKGBUILD new file mode 100644 index 0000000..8815f55 --- /dev/null +++ b/pkgbuilds/owe/PKGBUILD @@ -0,0 +1,34 @@ +# Maintainer: owe contributors +pkgname=owe +pkgver=0.2.6 +pkgrel=1 +pkgdesc="High-performance wallpaper engine for Omarchy (mp4, gif, stills)" +arch=('x86_64' 'aarch64') +url="https://github.com/omacom/owe" +license=('MIT') +depends=('mpv' 'ffmpeg' 'wayland' 'libglvnd' 'libepoxy' 'systemd-libs' 'socat') +makedepends=('meson' 'ninja' 'gcc' 'pkgconf' 'wayland-protocols') +checkdepends=('python') +optdepends=('intel-media-driver: VAAPI hardware decode on Intel GPUs' + 'libva-mesa-driver: VAAPI hardware decode on AMD GPUs') +source=("$pkgname-$pkgver.tar.gz::https://github.com/omacom/owe/archive/refs/tags/v$pkgver.tar.gz") +sha256sums=('e5c10e60bdfaebed861a3b7515c691a5a78fc0fe3ab29c0e96d934eb1a957cf8') + +build() { + meson setup build "$srcdir/owe-$pkgver" -Dbuildtype=release -Dprefix=/usr + ninja -C build +} + +check() { + meson test -C build +} + +package() { + DESTDIR="$pkgdir" ninja -C build install + install -d "$pkgdir/usr/lib/systemd/user" + sed 's|%h/.local/bin/owed|/usr/bin/owed|' "$srcdir/owe-$pkgver/systemd/owed.service" \ + >"$pkgdir/usr/lib/systemd/user/owed.service" + install -Dm755 "$srcdir/owe-$pkgver/hooks/owe-idle" "$pkgdir/usr/bin/owe-idle" + install -Dm644 "$srcdir/owe-$pkgver/hooks/theme-set.d/10-owe-sync" "$pkgdir/usr/share/owe/10-owe-sync" + install -Dm644 "$srcdir/owe-$pkgver/config/config.toml" "$pkgdir/usr/share/doc/$pkgname/config.toml.example" +} diff --git a/pkgbuilds/pinta/.omarchy/package.json b/pkgbuilds/pinta/.omarchy/package.json new file mode 100644 index 0000000..aad5b86 --- /dev/null +++ b/pkgbuilds/pinta/.omarchy/package.json @@ -0,0 +1,9 @@ +{ + "source": "local", + "upstream": { + "watch": { + "github": "PintaProject/Pinta", + "pattern": "(?P[0-9]+(?:\\.[0-9]+)*)" + } + } +} diff --git a/pkgbuilds/pinta/PKGBUILD b/pkgbuilds/pinta/PKGBUILD new file mode 100644 index 0000000..5f1475f --- /dev/null +++ b/pkgbuilds/pinta/PKGBUILD @@ -0,0 +1,34 @@ +# Pinta for Arch Linux ARM. The official repositories do not supply its +# .NET dependencies on ARM; use the dotnet-core-bin split package family. + +pkgname=pinta +pkgver=3.1.2 +pkgrel=1 +pkgdesc="Drawing/editing program modeled after Paint.NET. It's goal is to provide a simplified alternative to GIMP for casual users" + +arch=('aarch64') +license=('MIT') +url="https://pinta-project.com" + +makedepends=('pkgconf' 'intltool' 'dotnet-sdk-bin') +depends=('dotnet-runtime-bin' 'dotnet-host-bin' 'libadwaita' 'hicolor-icon-theme' 'webp-pixbuf-loader') + +conflicts=($pkgname-git) + +source=("https://github.com/PintaProject/Pinta/releases/download/${pkgver}/pinta-${pkgver}.tar.gz") +sha256sums=('27f55a026b51f6f18197e6bcfdd7c79544e41529d166daf6c93a1117e9ed45f0') + +build() { + cd "${srcdir}/pinta-${pkgver}" + ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var + make PINTA_BUILD_OPTS='--configuration Release -p:BuildTranslations=true -p:RuntimeIdentifier=linux-arm64' +} + +package() { + cd "${srcdir}/pinta-${pkgver}" + + make DESTDIR="${pkgdir}" install + + install -Dm644 -t "${pkgdir}/usr/share/doc/${pkgname}/" readme.md + install -Dm644 -t "${pkgdir}/usr/share/licenses/${pkgname}/" license-*.txt +} diff --git a/pkgbuilds/schist-bin/PKGBUILD b/pkgbuilds/schist-bin/PKGBUILD index 06ee0b3..e144cd5 100644 --- a/pkgbuilds/schist-bin/PKGBUILD +++ b/pkgbuilds/schist-bin/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Infrawrench LLC pkgname=schist-bin -pkgver=0.12.0 +pkgver=0.14.0 pkgrel=1 # Upstream's own package release, embedded in the asset name. It is # packages.sh's "release=" and only moves when the packaging changes under @@ -32,8 +32,8 @@ options=(!strip !debug) # script. source_x86_64=("$url/releases/download/v$pkgver/schist-$pkgver-$_relver-x86_64.pkg.tar.zst") source_aarch64=("$url/releases/download/v$pkgver/schist-$pkgver-$_relver-aarch64.pkg.tar.zst") -sha256sums_x86_64=('baff8848274f0121911a6408fd3dd082c5ab023c0ae5c9e5be35968c80beb069') -sha256sums_aarch64=('a7a4f062289a140ff0aa5c0c9c4b3cc9f386759e3bfe929a0382de2d20c04eda') +sha256sums_x86_64=('1e7f51ed0141f4573c65296f73d9e7005c897c1b2fb39085f3d6f7f95bbcefbf') +sha256sums_aarch64=('52c0d6810094183ea1dc2248a147e60afae98ce63f0b41975dafa7f63644ef20') package() { # makepkg has already extracted the payload into srcdir; its .PKGINFO diff --git a/pkgbuilds/strata/PKGBUILD b/pkgbuilds/strata/PKGBUILD index 6672d2a..4b6a14b 100644 --- a/pkgbuilds/strata/PKGBUILD +++ b/pkgbuilds/strata/PKGBUILD @@ -1,6 +1,6 @@ pkgname=strata -pkgver=0.17.0 -pkgrel=1 +pkgver=0.19.0 +pkgrel=2 pkgdesc='Fast, keyboard-first file manager for modern Linux desktops' arch=('x86_64' 'aarch64') url='https://github.com/lgse/strata' @@ -39,7 +39,7 @@ conflicts=('strata-git') options=('!debug' '!lto') source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") -sha256sums=('d1f54c1a2d88b958f5d463c9c37fac73a982de7ae893b7c731820cf557d24f5f') +sha256sums=('51701930728625ce1d6394949a4b6b2705f0999fd08be87f1a26d900209d62e0') prepare() { cd "$pkgname-$pkgver" @@ -69,7 +69,16 @@ build() { check() { cd "$pkgname-$pkgver" - export CARGO_TARGET_DIR=target + # Upstream's release profile is fat LTO with one codegen unit, which + # compiles the final crate on a single thread. That is the right trade + # for the binary users run, and build() keeps it. The test harness is a + # second compile of the same crate under the same profile, thrown away + # afterwards; build it in parallel, in its own target directory so the + # shipped binary is untouched. Measured on the x86_64 builder: 708 s + # serial, 223 s with thin LTO and 16 units. + export CARGO_TARGET_DIR=target-check + export CARGO_PROFILE_RELEASE_LTO=thin + export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 export STRATA_BUILD_COMMIT STRATA_BUILD_COMMIT=$(<.build-commit) export STRATA_RELEASE_TAG="v$pkgver" @@ -84,8 +93,22 @@ check() { rm -rf -- "$search_tmp" (( test_status == 0 )) || return "$test_status" + # restart_waiter_* run `sh -c 'while kill -0 "$1"; do sleep; done'` with + # the pid u32::MAX. Arch's sh is bash, and bash's kill builtin in POSIX + # mode prints "not a pid or valid job spec" for that number but exits 0, + # so the loop never ends and the job hits its 180-minute timeout. + # Upstream CI runs on Ubuntu, where sh is dash and rejects the number. + # Skip until upstream waits on a real pid. + local skip=(--skip services::search::tests:: --skip ui::settings::tests::restart_waiter_) + # ownership_probe_errors_disable_in_place_updates points the pacman path + # at a directory and expects Command::output() to fail. aarch64 builds + # run under QEMU user-mode emulation, where glibc's posix_spawn cannot + # observe the child's failed execve (natively it returns EACCES); the + # spawn "succeeds" with exit 127, the probe reads that as "not owned", + # and the assertion fails. Passes natively. + [[ $CARCH == aarch64 ]] && skip+=(--skip services::update_install::tests::ownership_probe_errors_disable_in_place_updates) cargo test --frozen --release --all-targets --all-features \ - -- --test-threads=1 --skip services::search::tests:: + -- --test-threads=1 "${skip[@]}" } package() { diff --git a/pkgbuilds/sublime-text-4/PKGBUILD b/pkgbuilds/sublime-text-4/PKGBUILD index 7114b57..09cdc85 100644 --- a/pkgbuilds/sublime-text-4/PKGBUILD +++ b/pkgbuilds/sublime-text-4/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Manuel Hüsers pkgname=sublime-text-4 -pkgver=4.4200 +pkgver=4.4213 pkgrel=1 pkgdesc='Sophisticated text editor for code, html and prose - stable build' arch=('x86_64' 'aarch64') @@ -16,8 +16,8 @@ source_x86_64=("${pkgname//-/_}_${pkgver/./_}_${pkgrel}_x64.tar.xz::https://down source_aarch64=("${pkgname//-/_}_${pkgver/./_}_${pkgrel}_arm64.tar.xz::https://download.sublimetext.com/sublime_text_build_${pkgver:2}_arm64.tar.xz") sha512sums=('ac56e9b7dddaebb3d222795cfc644109c93cc3f79695b8f9ee56022c74fe04a1134dd54cab07c74ff1f96b783cb3dbc026c16095552f1d2dd83115ea274dc2e9') -sha512sums_x86_64=('0a6ff4be7ae35ce80d568a2bf8dc5ed6fcf9f845517f7a1b8b24f180842f72ef16f9792e91fc70a277cfaf66bf1be482bb1328c98252d7c524d3412cfe9f22e3') -sha512sums_aarch64=('bb8f314e3c0ffff2536f91331fe43fe55d42fed27fca32bced5e779331296c7ee4b619dacc0193bc7f2cfa16a770177a81783ed959c786522e9688b028c7c221') +sha512sums_x86_64=('0d222ba954d7f6c5c7b03ce1eff3751e2d92b233058524208eb8e007c347b9a3628b9487e832c3c5599e7d2bcb940407466bd7b000a4e389f9ed916950bd049e') +sha512sums_aarch64=('e2ee9de786d1ca6ef28f6703626be226dc0b15f74a61ca4de58522fa7c9f295c8a38b052463d95878a8930366bf1f5d4740a876c7022e1655c4b906a0a83cef1') prepare() { sed -i -e "s|@ST_PATH@|/opt/sublime_text|g" "${pkgname}.sh" diff --git a/pkgbuilds/sunshine/PKGBUILD b/pkgbuilds/sunshine/PKGBUILD index 0e4d20c..170bb51 100644 --- a/pkgbuilds/sunshine/PKGBUILD +++ b/pkgbuilds/sunshine/PKGBUILD @@ -6,11 +6,11 @@ : "${_support_headless_testing:=false}" : "${_use_cuda:=detect}" # nvenc -_commit=cb72dffa3233c5815cd5ba88f09f049dd679ba75 +_commit=63d35f702ee9e362e43263742981836ec0710384 pkgname='sunshine' -pkgver=2026.906.222525 -pkgrel=1.2 +pkgver=2026.914.233613 +pkgrel=1 pkgdesc="Self-hosted game stream host for Moonlight" arch=('x86_64' 'aarch64') url=https://app.lizardbyte.dev/Sunshine diff --git a/pkgbuilds/t3code-bin/.omarchy/upstream.sh b/pkgbuilds/t3code-bin/.omarchy/upstream.sh index 6bc010c..63c436d 100755 --- a/pkgbuilds/t3code-bin/.omarchy/upstream.sh +++ b/pkgbuilds/t3code-bin/.omarchy/upstream.sh @@ -1,8 +1,9 @@ #!/bin/bash # T3 Code publishes electron-builder's update feed beside every release, so the -# newest version costs one small request. The feed's checksum is a base64 +# newest version costs one small request. Each feed's checksum is a base64 # SHA-512 and makepkg wants hex SHA-256, so a release that is actually new still -# has to be downloaded once to hash -- hence the version check before the fetch. +# has to be downloaded once per architecture to hash -- hence the version check +# before the fetch. set -euo pipefail FEED_URL="https://github.com/pingdotgg/t3code/releases/latest/download/latest-linux.yml" @@ -23,7 +24,7 @@ if [[ -n "$current" ]] && [[ "$(vercmp "$version" "$current")" -le 0 ]]; then exit 0 fi -# The PKGBUILD builds one fixed asset name, so a feed naming anything else -- +# The PKGBUILD builds fixed asset names, so a feed naming anything else -- # a rename, or an arm64 build reaching the Linux feed first -- has to stop the # sync rather than pin that file's checksum to a URL nobody will fetch. expected="T3-Code-${version}-x86_64.AppImage" @@ -32,7 +33,18 @@ if [[ "$asset" != "$expected" ]]; then exit 1 fi -sha256=$(curl -fsSL "$RELEASE_URL/v${version}/${asset}" | sha256sum | cut -d' ' -f1) +# Pin the ARM feed to the same release, so a partially published release or a +# latest-release change cannot mix versions between architectures. +arm_feed=$(curl -fsSL "$RELEASE_URL/v${version}/latest-linux-arm64.yml") +arm_version=$(awk '/^version:/ { print $2; exit }' <<<"$arm_feed" | tr -d '"'\''') +arm_asset=$(awk '/^path:/ { print $2; exit }' <<<"$arm_feed" | tr -d '"'\''') +if [[ "$arm_version" != "$version" || "$arm_asset" != "T3-Code-${version}-arm64.AppImage" ]]; then + echo "Upstream ARM feed does not match T3-Code-${version}-arm64.AppImage" >&2 + exit 1 +fi -jq -n --arg pkgver "$version" --arg sha256 "$sha256" \ - '{pkgver: $pkgver, sha256sums: {x86_64: [$sha256]}}' +sha256=$(curl -fsSL "$RELEASE_URL/v${version}/${asset}" | sha256sum | cut -d' ' -f1) +arm_sha256=$(curl -fsSL "$RELEASE_URL/v${version}/${arm_asset}" | sha256sum | cut -d' ' -f1) + +jq -n --arg pkgver "$version" --arg sha256 "$sha256" --arg arm_sha256 "$arm_sha256" \ + '{pkgver: $pkgver, sha256sums: {x86_64: [$sha256], aarch64: [$arm_sha256]}}' diff --git a/pkgbuilds/t3code-bin/PKGBUILD b/pkgbuilds/t3code-bin/PKGBUILD index d848b90..235022d 100644 --- a/pkgbuilds/t3code-bin/PKGBUILD +++ b/pkgbuilds/t3code-bin/PKGBUILD @@ -1,18 +1,19 @@ # Maintainer: David Heinemeier Hansson -# T3 Code ships Linux as an AppImage and nothing else, so Omarchy unpacks it and +# T3 Code ships its Linux desktop as an AppImage, so Omarchy unpacks it and # keeps only the Electron tree. AppRun, the compatibility libraries bundled for # distributions that do not ship their own, and the AppImage's icon shims are all -# dead weight here. .omarchy/upstream.sh rewrites the version and checksum below -# from the release feed the app updates itself from. +# dead weight here. .omarchy/upstream.sh rewrites the version and checksums below +# from the release feeds the app updates itself from. pkgname=t3code-bin -pkgver=0.0.40 -pkgrel=1 +pkgver=0.0.42 +pkgrel=2 pkgdesc="Open-source control plane for coding agents" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://t3.codes" license=('MIT') +makedepends=('7zip') depends=( 'alsa-lib' @@ -54,19 +55,28 @@ provides=("t3code=${pkgver}") conflicts=('t3code') options=('!debug' '!strip') -_appimage="T3-Code-${pkgver}-x86_64.AppImage" +_appimage_x86_64="T3-Code-${pkgver}-x86_64.AppImage" +_appimage_aarch64="T3-Code-${pkgver}-arm64.AppImage" source=('t3code-launcher.sh' 't3-launcher.sh' 'LICENSE') -source_x86_64=("${_appimage}::https://github.com/pingdotgg/t3code/releases/download/v${pkgver}/${_appimage}") -noextract=("${_appimage}") +source_x86_64=("https://github.com/pingdotgg/t3code/releases/download/v${pkgver}/${_appimage_x86_64}") +source_aarch64=("https://github.com/pingdotgg/t3code/releases/download/v${pkgver}/${_appimage_aarch64}") +noextract=("${_appimage_x86_64}" "${_appimage_aarch64}") sha256sums=('cb905ff341372ef2ef6e402cf485959f8bd1df8f0efebee4cda1afdd5e6abc0a' 'c5b3f2a9f0b14b12cfd973b79319f0f018b7ae49a1d43d8ca346100c3f7de28f' '935d8f2af0c703f9c39517ee57cc4930b19d02d533be930b63f0e82f93614b43') -sha256sums_x86_64=('8bf5fd44cb7fad0c43191d54fefdf974a8227d50505ecb8abcf76326209f264a') +sha256sums_x86_64=('8dc1fccdabc2ed3a59a3944cc772ef11931b9351401c0963ed305d5f96e3cdf4') +sha256sums_aarch64=('c256d872d358e9f2c91328b0154c6311fa2eb16386ef6f788fcda12d73cf2836') prepare() { - chmod +x "${srcdir}/${_appimage}" + local _appimage + case "$CARCH" in + x86_64) _appimage="${_appimage_x86_64}" ;; + aarch64) _appimage="${_appimage_aarch64}" ;; + esac rm -rf "${srcdir}/squashfs-root" - "${srcdir}/${_appimage}" --appimage-extract >/dev/null + # Extract without executing the runtime: AppImage's ELF magic does not match + # QEMU's binfmt registration when building ARM packages on an x86_64 host. + 7z x "${srcdir}/${_appimage}" -o"${srcdir}/squashfs-root" >/dev/null } package() { diff --git a/pkgbuilds/ttfx/PKGBUILD b/pkgbuilds/ttfx/PKGBUILD index 4079045..1a386f7 100644 --- a/pkgbuilds/ttfx/PKGBUILD +++ b/pkgbuilds/ttfx/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: David Heinemeier Hansson pkgname=ttfx -pkgver=0.3.2 +pkgver=0.3.3 pkgrel=1 pkgdesc="Terminal text effects as a single static binary — Rust port of terminaltexteffects" arch=('x86_64' 'aarch64') @@ -12,7 +12,7 @@ makedepends=('cargo') options=('!debug') source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") -sha256sums=('d0c0df4867e7f03142fb7f77c66670d0e8da15534239c1a7abfd89f19dfc00f6') +sha256sums=('d040da0da2f4a952a367fa3d934ac25999265405f1d2a9f0475625e41211fa7d') prepare() { cd "$pkgname-$pkgver" diff --git a/pkgbuilds/visual-studio-code-bin/PKGBUILD b/pkgbuilds/visual-studio-code-bin/PKGBUILD index 08ad707..4d43a57 100644 --- a/pkgbuilds/visual-studio-code-bin/PKGBUILD +++ b/pkgbuilds/visual-studio-code-bin/PKGBUILD @@ -2,7 +2,7 @@ pkgname=visual-studio-code-bin _pkgname=visual-studio-code -pkgver=1.137.0 +pkgver=1.138.0 pkgrel=1 pkgdesc="Visual Studio Code (vscode): Editor for building and debugging modern web and cloud applications (official binary version)" arch=('x86_64' 'aarch64' 'armv7h') @@ -27,9 +27,9 @@ source_x86_64=(code_${pkgver}_amd64.deb::https://update.code.visualstudio.com/${ source_aarch64=(code_${pkgver}_arm64.deb::https://update.code.visualstudio.com/${pkgver}/linux-deb-arm64/stable) source_armv7h=(code_${pkgver}_armhf.deb::https://update.code.visualstudio.com/${pkgver}/linux-deb-armhf/stable) sha256sums=('bd0d9edf69283ebdf4e73e0a7b168d2fcf50acbd01f63674cad93ed4fe42fdad') -sha256sums_x86_64=('fd4dff72c44598d3acb885b448256f5d82cf53f59538d97fc7d3c8d8d9d574d3') -sha256sums_aarch64=('8bff558a659d351328f5a1e319802073b59dac42f95cc0f9b2ef1b0c27387431') -sha256sums_armv7h=('b86cdd666e972a5a555d34298c91239bcbaada69ee882c4a36fe1e74d1df2070') +sha256sums_x86_64=('73389cdcef7e66171a2039d1e49b9530e5ed02937e6159d3e6af93484e63cbad') +sha256sums_aarch64=('09760b73fb96ca19f8c6e483ec5b69123f34edd2c762cc9e0faf73fa3d400145') +sha256sums_armv7h=('bb74a3023aced544c71d4274d5942ce69c59a2ec0ffaf9094fa7c0fddb506366') package() { bsdtar -xf data.tar.xz -C "${pkgdir}/" diff --git a/pkgbuilds/yaru-icon-theme/PKGBUILD b/pkgbuilds/yaru-icon-theme/PKGBUILD index 03a6710..e421772 100644 --- a/pkgbuilds/yaru-icon-theme/PKGBUILD +++ b/pkgbuilds/yaru-icon-theme/PKGBUILD @@ -11,7 +11,7 @@ pkgname=('yaru-sound-theme' 'yaru-icon-theme' 'yaru-session') pkgver=26.10.3 -pkgrel=1 +pkgrel=2 pkgdesc="Yaru default ubuntu theme" arch=(any) url="https://github.com/ubuntu/yaru" @@ -56,7 +56,8 @@ _delete_all_from_pkgdir_except() { rm -r "${pkgdir}"/usr/share/icons fi if [[ "$1" != "session" ]]; then - rm -r "${pkgdir}"/usr/share/{glib-2.0,xsessions,wayland-sessions} + # Newer Yaru releases no longer install the X11 session directory. + rm -rf "${pkgdir}"/usr/share/{glib-2.0,xsessions,wayland-sessions} rm -r "${pkgdir}"/usr/share/gnome-shell/{extensions,modes} fi # Delete remaining empty directories: diff --git a/tests/artifact-helpers.sh b/tests/artifact-helpers.sh new file mode 100755 index 0000000..e3ea1a6 --- /dev/null +++ b/tests/artifact-helpers.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Self-test for helpers/artifact-helpers.sh: package files survive the +# artifact hop between build-pr.yml and publish.yml with makepkg's names +# intact, including the colon an epoch puts in them. +set -euo pipefail +ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +source "$ROOT/helpers/artifact-helpers.sh" +T=$(mktemp -d); trap 'rm -rf "$T"' EXIT +pass() { echo "PASS: $1"; } +fail() { echo "FAIL: $1"; exit 1; } + +EPOCH='cursor-cli-1:2026.09.18.1.9a7762b-1-x86_64.pkg.tar.zst' +PLAIN='beta-1.0-1-x86_64.pkg.tar.zst' +mkdir -p "$T/built" "$T/artifact" "$T/out" +echo epoch > "$T/built/$EPOCH" +echo plain > "$T/built/$PLAIN" +echo sig > "$T/built/$PLAIN.sig" +echo db > "$T/built/omarchy.db.tar.zst" + +pack_packages "$T/built" "$T/artifact/packages.tar" || fail "pack" +[[ "$(tar -tf "$T/artifact/packages.tar" | sort | tr '\n' ' ')" == "$PLAIN $EPOCH " ]] \ + && pass "packages.tar holds the packages only, no signature or database" || fail "tar contents: $(tar -tf "$T/artifact/packages.tar" | tr '\n' ' ')" +[[ "$(ls "$T/artifact")" == "packages.tar" ]] && pass "the artifact path carries no colon" || fail "artifact listing" + +unpack_packages "$T/artifact" "$T/out" || fail "unpack" +[[ "$(ls "$T/out" | sort | tr '\n' ' ')" == "$PLAIN $EPOCH " && "$(cat "$T/out/$EPOCH")" == epoch ]] \ + && pass "epoch filename and bytes survive the round trip" || fail "round trip: $(ls "$T/out" | tr '\n' ' ')" + +# An artifact uploaded before packing existed: bare package files. +mkdir -p "$T/old" "$T/out2"; cp "$T/built"/*.pkg.tar.zst "$T/old/" +unpack_packages "$T/old" "$T/out2" && [[ "$(ls "$T/out2" | sort | tr '\n' ' ')" == "$PLAIN $EPOCH " ]] \ + && pass "a bare pre-packing artifact still unpacks" || fail "bare artifact" + +# The workflows call these bare under `bash -e`, so any non-zero status +# inside them ends the step. (The first version used `shopt -p nullglob`, +# which exits 1 when the option is off; the tests above never saw it because +# `||` suppresses errexit.) +mkdir -p "$T/out4" "$T/out5" +bash -e -c "source '$ROOT/helpers/artifact-helpers.sh'; pack_packages '$T/built' '$T/out4/packages.tar'; tar -tf '$T/out4/packages.tar' >/dev/null; unpack_packages '$T/out4' '$T/out5'" \ + && [[ "$(ls "$T/out5" | sort | tr '\n' ' ')" == "$PLAIN $EPOCH " ]] \ + && pass "pack and unpack succeed under bash -e, as the workflows call them" || fail "bash -e" + +mkdir -p "$T/empty" +if pack_packages "$T/empty" "$T/x.tar" 2>/dev/null; then fail "packing an empty build dir should fail"; else pass "empty build dir refused"; fi +if unpack_packages "$T/empty" "$T/out3" 2>/dev/null; then fail "an empty artifact should fail"; else pass "empty artifact refused"; fi diff --git a/tests/builder-image.cjs b/tests/builder-image.cjs new file mode 100644 index 0000000..c20f306 --- /dev/null +++ b/tests/builder-image.cjs @@ -0,0 +1,130 @@ +const assert = require('node:assert/strict'); +const { spawnSync } = require('node:child_process'); +const { chmodSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, utimesSync, writeFileSync } = require('node:fs'); +const { tmpdir } = require('node:os'); +const { join } = require('node:path'); +const { test } = require('node:test'); + +const root = join(__dirname, '..'); +const workflow = readFileSync(join(root, '.github/workflows/builder-images.yml'), 'utf8'); + +function fixture(t) { + const directory = mkdtempSync(join(tmpdir(), 'builder-image-test-')); + t.after(() => rmSync(directory, { recursive: true, force: true })); + mkdirSync(join(directory, 'bin')); + mkdirSync(join(directory, 'build')); + cpSync(join(root, 'helpers'), join(directory, 'helpers'), { recursive: true }); + cpSync(join(root, 'bin/builder-image'), join(directory, 'bin/builder-image')); + writeFileSync(join(directory, 'build/Dockerfile'), 'FROM scratch\nCOPY input /input\n'); + writeFileSync(join(directory, 'build/input'), 'original input\n'); + const engine = join(directory, 'engine'); + mkdirSync(engine); + const log = join(directory, 'engine.jsonl'); + writeFileSync(join(engine, 'docker'), `#!/usr/bin/env node +const fs = require('node:fs'); +const args = process.argv.slice(2); +fs.appendFileSync(process.env.ENGINE_LOG, JSON.stringify(args) + '\\n'); +if (args[0] === 'manifest' && process.env.PRIVATE_IMAGE === '1') process.exit(1); +if (args[0] === 'push' && process.env.PUSH_FAIL === '1') process.exit(1); +if (args[0] === 'image' && args[1] === 'inspect') console.log('ghcr.io/omacom/omarchy-pkg-builder@sha256:' + 'a'.repeat(64)); +`); + chmodSync(join(engine, 'docker'), 0o755); + const env = { + ...process.env, PATH: `${engine}:${process.env.PATH}`, CONTAINER_ENGINE: 'docker', + ENGINE_LOG: log, ARCH: 'x86_64', MIRROR: 'edge', + }; + const run = (args, extraEnv = {}) => spawnSync(join(directory, 'bin/builder-image'), args, { + cwd: directory, env: { ...env, ...extraEnv }, encoding: 'utf8', + }); + const key = (...args) => { + const result = run(['key', ...args]); + assert.equal(result.status, 0, result.stderr); + return result.stdout.trim(); + }; + const calls = () => readFileSync(log, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse); + return { directory, env, run, key, calls }; +} + +test('image keys are stable across checkout location and timestamp changes', t => { + const a = fixture(t); + const b = fixture(t); + const key = a.key(); + assert.match(key, /^v1-x86_64-edge-[a-f0-9]{64}$/); + utimesSync(join(b.directory, 'build/input'), new Date(0), new Date(0)); + assert.equal(b.key(), key); +}); + +test('image keys separate architectures, mirrors, content, modes and symlink targets', t => { + const f = fixture(t); + const keys = new Set([f.key(), f.key('--arch', 'aarch64'), f.key('--mirror', 'rc'), f.key('--mirror', 'stable')]); + writeFileSync(join(f.directory, 'build/input'), 'new input\n'); + keys.add(f.key()); + chmodSync(join(f.directory, 'build/input'), 0o755); + keys.add(f.key()); + symlinkSync('input', join(f.directory, 'build/link')); + keys.add(f.key()); + rmSync(join(f.directory, 'build/link')); + symlinkSync('Dockerfile', join(f.directory, 'build/link')); + keys.add(f.key()); + assert.equal(keys.size, 8); + mkdirSync(join(f.directory, 'pkgbuilds/example'), { recursive: true }); + const key = f.key(); + writeFileSync(join(f.directory, 'pkgbuilds/example/PKGBUILD'), 'pkgver=2\n'); + assert.equal(f.key(), key, 'package changes must not invalidate the build environment'); +}); + +test('fresh builds refresh package layers and record their compatibility key', t => { + const f = fixture(t); + const key = f.key('--arch', 'aarch64'); + const result = f.run(['build', '--arch', 'aarch64', '--tag', 'candidate:test', '--fresh']); + assert.equal(result.status, 0, result.stderr); + const build = f.calls().find(args => args[0] === 'buildx'); + assert.ok(build.includes('--no-cache')); + assert.ok(build.includes('--pull')); + assert.ok(build.includes('--load')); + assert.ok(build.includes('--platform=linux/arm64')); + assert.ok(build.includes(`org.omarchy.builder.key=${key}`)); + assert.ok(build.includes('candidate:test')); +}); + +test('invalid targets fail before starting an image build', t => { + const f = fixture(t); + for (const args of [['key', '--arch', 'invalid'], ['build', '--mirror', 'invalid'], ['key', '--fresh']]) { + assert.notEqual(f.run(args).status, 0); + } +}); + +function publish(f, extraEnv = {}) { + const script = workflow.split(' - name: Publish tested image\n')[1].split(' run: |\n')[1] + .replaceAll('${{ matrix.arch }}', 'x86_64'); + return spawnSync('bash', ['-e', '-o', 'pipefail', '-c', script], { + cwd: f.directory, encoding: 'utf8', env: { + ...f.env, REGISTRY_IMAGE: 'ghcr.io/omacom/omarchy-pkg-builder', CANDIDATE_IMAGE: 'candidate:test', + GH_TOKEN: 'fixture', GH_ACTOR: 'fixture', DOCKER_CONFIG: join(f.directory, 'auth'), + RUNNER_TEMP: f.directory, GITHUB_RUN_ID: '123', GITHUB_RUN_ATTEMPT: '1', + GITHUB_STEP_SUMMARY: join(f.directory, 'summary'), ...extraEnv, + }, + }); +} + +test('a public tested image gets a version tag before the compatible-image tag advances', t => { + const f = fixture(t); + const key = f.key(); + const result = publish(f); + assert.equal(result.status, 0, result.stderr); + const calls = f.calls(); + assert.deepEqual(calls.filter(args => args[0] === 'push').map(args => args[1]), [ + `ghcr.io/omacom/omarchy-pkg-builder:${key}-123-1`, `ghcr.io/omacom/omarchy-pkg-builder:${key}`, + ]); + assert.ok(calls.findIndex(args => args[0] === 'manifest') < calls.findLastIndex(args => args[0] === 'push')); +}); + +test('a private image or failed push never replaces the previous compatible-image tag', t => { + for (const extraEnv of [{ PRIVATE_IMAGE: '1' }, { PUSH_FAIL: '1' }]) { + const f = fixture(t); + const key = f.key(); + const result = publish(f, extraEnv); + assert.notEqual(result.status, 0); + assert.equal(f.calls().some(args => args[0] === 'push' && args[1] === `ghcr.io/omacom/omarchy-pkg-builder:${key}`), false); + } +}); diff --git a/tests/controller.sh b/tests/controller.sh new file mode 100755 index 0000000..d777fc5 --- /dev/null +++ b/tests/controller.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Self-test for ci/controller.sh: every decision, no cloud. +# +# The controller's two API functions are overridden with canned responses and +# a recorder, then each scenario asserts which creates and deletes it issued. +set -euo pipefail +ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") + +export REPO=o/r DIGITALOCEAN_TOKEN=x GITHUB_TOKEN=x +export CLOUD_INIT="$ROOT/ci/runner-cloud-init.yaml" LOCK=/tmp/controller-test.lock +CONTROLLER_LIBRARY_ONLY=1 source "$ROOT/ci/controller.sh" + +# Calls are recorded to a file: the controller invokes the API functions +# inside command substitutions, and a subshell cannot append to an array. +CALLS_FILE=$(mktemp); trap 'rm -f "$CALLS_FILE"' EXIT +NOW=$(date -u +%FT%TZ) +OLD=$(date -u -d '5 hours ago' +%FT%TZ) + +# Scenario state: DROPLETS is "id status created" lines, QUEUED a count, +# BUSY a count. +do_api() { + local path=$1; shift + echo "do $path $*" >>"$CALLS_FILE" + case "$path" in + droplets\?*) printf '%s\n' "$DROPLETS" | jq -Rs '{droplets: [split("\n")[] | select(length>0) | split(" ") | {id: .[0]|tonumber, status: .[1], created_at: .[2]}]}' ;; + droplets) echo '{"droplet":{"id":999}}' ;; + droplets/*) echo '{}' ;; + esac +} +gh_api() { + local path=$1; shift + echo "gh $path $*" >>"$CALLS_FILE" + case "$path" in + */actions/runs\?*) jq -nc --argjson n "$QUEUED" '{workflow_runs: [range($n) | {id: .}]}' ;; + */actions/runs/*/jobs) echo '{"jobs":[{"id":1,"status":"queued","labels":["self-hosted","omarchy-builder"]}]}' ;; + */actions/runners\?*) jq -nc --argjson n "$BUSY" '{runners: [range($n) | {busy: true, labels: [{name: "omarchy-builder"}]}]}' ;; + */registration-token) echo '{"token":"T"}' ;; + esac +} + +creates() { grep -c '^do droplets -X POST' "$CALLS_FILE" || true; } +deletes() { grep -c '^do droplets/.* -X DELETE' "$CALLS_FILE" || true; } +run() { : >"$CALLS_FILE"; controller_tick >/dev/null; } +check() { # check + local c d; c=$(creates); d=$(deletes) + if [[ "$c" == "$2" && "$d" == "$3" ]]; then echo "PASS: $1"; else echo "FAIL: $1 (creates=$c want $2, deletes=$d want $3)"; cat "$CALLS_FILE"; exit 1; fi +} + +DROPLETS="" QUEUED=0 BUSY=0; run; check "idle: nothing queued, nothing to reap" 0 0 +DROPLETS="" QUEUED=2 BUSY=0; run; check "two queued, none live: create two" 2 0 +DROPLETS="1 active $NOW" QUEUED=1 BUSY=1; run; check "one queued, one live but busy: create one" 1 0 +DROPLETS="1 active $NOW" QUEUED=1 BUSY=0; run; check "one queued, one live and idle: it will take it" 0 0 +DROPLETS="1 off $NOW" QUEUED=0 BUSY=0; run; check "powered-off droplet reaped" 0 1 +DROPLETS="1 active $OLD" QUEUED=0 BUSY=0; run; check "over-age droplet reaped even if active" 0 1 +DROPLETS=$'1 active '"$NOW"$'\n2 active '"$NOW"$'\n3 active '"$NOW"$'\n4 active '"$NOW" QUEUED=3 BUSY=4; MAX_DROPLETS=4; run; check "at cap: no creates" 0 0 +DROPLETS=$'1 active '"$NOW"$'\n2 active '"$NOW" QUEUED=5 BUSY=2; MAX_DROPLETS=3; run; check "cap limits creates to remaining room" 1 0 +DROPLETS="1 off $NOW" QUEUED=1 BUSY=0; MAX_DROPLETS=4; run; check "off droplet is not capacity: reaped and replaced" 1 1 + +# The create body must carry the tag (reaper scope) and substituted user-data. +BODY_FILE=$(mktemp); trap 'rm -f "$CALLS_FILE" "$BODY_FILE"' EXIT +do_api() { if [[ $1 == droplets ]]; then printf '%s' "${*: -1}" >"$BODY_FILE"; echo '{"droplet":{"id":1}}'; else echo '{"droplets":[]}'; fi; } +gh_api() { echo '{"token":"TOK"}'; } +create_droplet >/dev/null +jq -e '.tags == ["omarchy-builder"] and .size == "g5-32vcpu-64gb-50gb" and (.user_data | test("--token \"TOK\"")) and (.user_data | test("__") | not)' "$BODY_FILE" >/dev/null \ + && echo "PASS: create body carries tag, size, substituted user-data" \ + || { echo "FAIL: create body"; jq . "$BODY_FILE" | head -20; exit 1; } diff --git a/tests/dell-xps-touchpad-haptics-install.sh b/tests/dell-xps-touchpad-haptics-install.sh new file mode 100755 index 0000000..2a5583c --- /dev/null +++ b/tests/dell-xps-touchpad-haptics-install.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -euo pipefail + +REPO_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +INSTALL_SCRIPT="$REPO_ROOT/pkgbuilds/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install" +TEST_ROOT=$(mktemp -d) +trap 'rm -rf "$TEST_ROOT"' EXIT + +# shellcheck source=/dev/null +source "$INSTALL_SCRIPT" + +home="$TEST_ROOT/home" +config_dir="$home/.config/omarchy" +config_path="$config_dir/dell-haptic.conf" +protected_file="$TEST_ROOT/protected" +runuser_call="$TEST_ROOT/runuser-call" +chown_call="$TEST_ROOT/chown-call" +runuser_stub="$TEST_ROOT/runuser" +mkdir -p "$config_dir" +printf 'must remain unchanged\n' >"$protected_file" +ln -s "$protected_file" "$config_path" + +chown() { + printf '%s\n' "$*" >>"$chown_call" +} + +_ensure_user_config test-user "$home" +[[ ! -e $chown_call ]] +[[ ! -e $runuser_call ]] +[[ $(cat "$protected_file") == 'must remain unchanged' ]] + +rm "$config_path" +printf '%s\n' \ + '#!/bin/bash' \ + 'set -euo pipefail' \ + '[[ $1 == --user && $2 == test-user && $3 == -- && $4 == /usr/bin/env ]]' \ + '[[ $5 == "HOME=$EXPECTED_HOME" && $6 == USER=test-user && $7 == LOGNAME=test-user ]]' \ + '[[ $8 == /usr/bin/dell-xps-touchpad-haptics && $9 == set && ${10} == high ]]' \ + 'printf "%s\n" "$*" >>"$RUNUSER_CALL"' \ + 'printf "INTENSITY=100\n" >"$EXPECTED_CONFIG"' >"$runuser_stub" +chmod +x "$runuser_stub" +export EXPECTED_HOME="$home" +export EXPECTED_CONFIG="$config_path" +export RUNUSER_CALL="$runuser_call" +_runuser_path="$runuser_stub" + +_ensure_user_config test-user "$home" +[[ ! -e $chown_call ]] +[[ -f $config_path && ! -L $config_path ]] +[[ $(cat "$config_path") == 'INTENSITY=100' ]] +grep -q '^--user test-user -- /usr/bin/env ' "$runuser_call" + +echo 'PASS: user config creation drops privileges and never chowns symlink targets' diff --git a/tests/oma-service-removal.py b/tests/oma-service-removal.py new file mode 100644 index 0000000..5686144 --- /dev/null +++ b/tests/oma-service-removal.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +"""Removal regression fixtures. No real systemd manager, user home or package is touched.""" +import os +from pathlib import Path +import socket +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] + +# Characters that make systemd's shell_maybe_quote() quote a value, a copy of +# SHELL_NEED_ESCAPE, GLOB_CHARS and the rest of SHELL_NEED_QUOTES in escape.h. +SHELL_NEED_QUOTES = '"\\`$*?[]' + "'()<>|&;!" + + +def systemd_environment_value(value): + r"""Return VALUE as ``systemctl show-environment`` would print it. + + print_variable() in systemctl-set-environment.c hands every value to + shell_maybe_quote(SHELL_ESCAPE_POSIX) quotes special values and uses + cescape_char() for control bytes. + """ + if not any(c in SHELL_NEED_QUOTES or c.isspace() or ord(c) < 0x20 or c == "\x7f" + for c in value): + return value + escapes = dict(zip("\a\b\f\n\r\t\v\\'", (r"\a", r"\b", r"\f", r"\n", r"\r", r"\t", r"\v", r"\\", r"\'"))) + return "$'" + "".join(escapes.get(c, f"\\{ord(c):03o}" if ord(c) < 0x20 or c == "\x7f" else c) + for c in value) + "'" + + +class Removal(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="oma-removal-") + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.home = self.root / "home" + self.runtime = self.root / "runtime" + self.units = self.home / ".config/systemd/user" + self.units.mkdir(parents=True) + self.runtime.mkdir() + self.bin = self.root / "bin" + self.bin.mkdir() + self.log = self.root / "calls" + self.env = dict(os.environ, PATH=f"{self.bin}:{os.environ['PATH']}", CALLS=str(self.log)) + self.executable("systemctl", '''#!/bin/bash +printf '%s\\n' "$*" >> "$CALLS" +case "$*" in + *show-environment*) [[ -z ${MANAGER_FAIL-} ]] || exit 1 + # print_variable() prints every value the way a shell would read it, so the + # fixture, not this stub, decides how the value is quoted. + echo "XDG_CONFIG_HOME=${CONFIG_HOME_RAW-${CONFIG_HOME-}}" ;; + *property=ExecStart*) echo "${EFFECTIVE-}" ;; + *property=LoadState*) echo "${LOAD_STATE-loaded}" ;; + # A unit that failed stays failed after it is stopped, and systemd refuses + # reset-failed for every other state, reporting the unit as not loaded. + *property=ActiveState*) echo "${ACTIVE_STATE-inactive}" ;; + *" reset-failed "*) [[ ${ACTIVE_STATE-inactive} == failed && -z ${RESET_FAIL-} ]] || { + printf 'Failed to reset failed state of unit: Unit is not loaded.\n' >&2; exit 1; } ;; + *" stop "*) [[ -z ${STOP_FAIL-} ]] || exit 1 ;; +esac +''') + + def executable(self, name, source): + path = self.bin / name + path.write_text(source) + path.chmod(0o755) + + def online(self): + sock = socket.socket(socket.AF_UNIX) + sock.bind(str(self.runtime / "bus")) + self.addCleanup(sock.close) + + def install(self, app, binary=None): + unit = self.units / f"{app}.service" + unit.write_text(f'[Service]\nExecStart="{binary or "/usr/bin/" + app}" --config "{self.home}/config.toml" daemon\n') + target = self.units / "graphical-session.target.wants" + target.mkdir(exist_ok=True) + link = target / unit.name + link.symlink_to(f"../{unit.name}") + return unit, link + + def run_remove(self, app): + self.log.unlink(missing_ok=True) # Every run is judged on its own calls. + return subprocess.run(["bash", str(ROOT / f"pkgbuilds/{app}-bin/package-remove"), + "--user", app, str(self.home), str(self.runtime)], + env=self.env, text=True, capture_output=True) + + def test_logged_out_users_and_data_preservation(self): + for app in ("omawake", "omaspeak"): + unit, link = self.install(app) + config = self.home / f"{app}.toml" + config.write_text("keep settings and models") + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + self.assertEqual(config.read_text(), "keep settings and models") + self.assertFalse(self.log.exists(), "offline cleanup contacted systemd") + + def test_active_unit_is_stopped_before_removing_it(self): + self.online() + for app in ("omawake", "omaspeak"): + unit, link = self.install(app) + self.env["EFFECTIVE"] = f"{{ path=/usr/bin/{app} ; argv[]=/usr/bin/{app} daemon ; }}" + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + calls = self.log.read_text().splitlines() + self.assertLess(calls.index(f"--user stop {app}.service"), calls.index(f"--user disable {app}.service")) + self.assertEqual(calls[-1], "--user daemon-reload") + + def test_failed_stop_prevents_unit_deletion_and_fails_hook(self): + self.online() + unit, link = self.install("omawake") + self.env.update(STOP_FAIL="1", EFFECTIVE="{ path=/usr/bin/omawake ; }") + result = self.run_remove("omawake") + self.assertNotEqual(result.returncode, 0) + self.assertTrue(unit.exists()) + self.assertTrue(link.is_symlink()) + self.assertNotIn("disable", self.log.read_text()) + + def test_reset_failed_is_requested_only_for_a_unit_that_failed(self): + self.online() + for app in ("omawake", "omaspeak"): + unit, link = self.install(app) + self.env["EFFECTIVE"] = f"{{ path=/usr/bin/{app} ; argv[]=/usr/bin/{app} daemon ; }}" + # A loaded unit that never failed is not failed, and asking systemd to + # reset it fails with "Unit is not loaded": that must not abort removal. + self.env["ACTIVE_STATE"] = "active" + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + calls = [call for call in self.log.read_text().splitlines() if call.startswith("--user")] + self.assertNotIn(f"--user reset-failed {app}.service", calls) + # Reading the manager's state is welcome; the only changes asked for + # are the stop, the disable and the reload that follow them. + self.assertEqual([call for call in calls + if "show-environment" not in call and "--property=" not in call], + [f"--user stop {app}.service", + f"--user disable {app}.service", "--user daemon-reload"]) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + + # A unit that failed does keep that state once stopped, and there the + # reset belongs between stopping the service and disabling the unit. + unit, link = self.install(app) + self.env["ACTIVE_STATE"] = "failed" + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + calls = self.log.read_text().splitlines() + reset = f"--user reset-failed {app}.service" + self.assertIn(reset, calls) + self.assertLess(calls.index(f"--user stop {app}.service"), calls.index(reset)) + self.assertLess(calls.index(reset), calls.index(f"--user disable {app}.service")) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + del self.env["ACTIVE_STATE"] + + def test_reset_refused_by_a_healthy_manager_still_fails_the_transaction(self): + self.online() + unit, link = self.install("omaspeak") + self.env.update(EFFECTIVE="{ path=/usr/bin/omaspeak ; }", + ACTIVE_STATE="failed", RESET_FAIL="1") + result = self.run_remove("omaspeak") + self.assertNotEqual(result.returncode, 0) + self.assertTrue(unit.exists()) + self.assertTrue(link.is_symlink()) + + def test_custom_build_and_mask_are_preserved(self): + unit, link = self.install("omawake", "/home/user/dev/omawake") + self.assertEqual(self.run_remove("omawake").returncode, 0) + self.assertTrue(unit.exists()) + self.assertTrue(link.is_symlink()) + unit.unlink() + unit.symlink_to("/dev/null") + self.assertEqual(self.run_remove("omawake").returncode, 0) + self.assertTrue(unit.is_symlink()) + self.assertFalse(self.log.exists()) + + def test_effective_override_and_missing_online_unit(self): + self.online() + unit, _ = self.install("omaspeak") + self.env["EFFECTIVE"] = "{ path=/home/user/development/omaspeak ; }" + self.assertEqual(self.run_remove("omaspeak").returncode, 0) + self.assertTrue(unit.exists()) + self.assertNotIn(" stop ", self.log.read_text()) + unit.unlink() + self.env.update(EFFECTIVE="", LOAD_STATE="not-found") + self.assertEqual(self.run_remove("omaspeak").returncode, 0) + self.assertNotIn(" stop ", self.log.read_text()) + + def test_manager_config_home_and_unavailable_manager(self): + self.online() + default = self.units + self.units = self.home / "custom-config/systemd/user" + self.units.mkdir(parents=True) + unit, link = self.install("omawake") + self.env.update(CONFIG_HOME=str(self.home / "custom-config"), MANAGER_FAIL="1") + self.assertNotEqual(self.run_remove("omawake").returncode, 0) + self.assertTrue(unit.exists()) + del self.env["MANAGER_FAIL"] + self.assertEqual(self.run_remove("omawake").returncode, 0) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + self.assertTrue(default.exists()) + + def test_root_dispatch_drops_privileges_and_propagates_failure(self): + passwd = f"fixture:x:12345:12345::{self.home}:/bin/bash" + self.executable("getent", f"#!/bin/sh\nprintf '%s\\n' '{passwd}'\n") + self.executable("runuser", '#!/bin/sh\nprintf "%s\\n" "$*" >> "$CALLS"\nexit 1\n') + result = subprocess.run(["bash", str(ROOT / "pkgbuilds/omawake-bin/package-remove"), "omawake"], env=self.env, capture_output=True) + self.assertNotEqual(result.returncode, 0) + self.assertIn("-u fixture -- env", self.log.read_text()) + self.assertIn("--user omawake", self.log.read_text()) + + def test_offline_executable_overrides_are_preserved(self): + for app in ("omawake", "omaspeak"): + for spacing in ("ExecStart=\nExecStart={command}", + # systemd's parser throws the whitespace around an + # assignment away, so both of these spellings still + # name a development build, exactly as the first does. + "ExecStart =\nExecStart = {command}", + "\tExecStart\t=\t{command}"): + unit, link = self.install(app) + dropins = Path(str(unit) + ".d") + dropins.mkdir(exist_ok=True) + (dropins / "override.conf").write_text("[Service]\n" + spacing.format( + command=f"/home/user/build/{app} daemon") + "\n") + try: + with self.subTest(app=app, spacing=spacing): + self.assertEqual(self.run_remove(app).returncode, 0) + self.assertTrue(unit.exists(), "removed a service with an override") + self.assertTrue(link.is_symlink(), "unlinked a service with an override") + finally: + unit.unlink(missing_ok=True) + link.unlink(missing_ok=True) + self.assertFalse(self.log.exists(), "offline cleanup contacted systemd") + + def test_shell_quoted_manager_config_home_is_resolved(self): + self.online() + default_units = self.units + for app in ("omawake", "omaspeak"): + config_home = self.home / f"{app} custom's \\ config" + self.units = config_home / "systemd/user" + self.units.mkdir(parents=True) + unit, link = self.install(app) + # A unit in the directory the manager reads nothing from is no unit of + # the manager's, and the helper has no business reaching for it. + stray = default_units / f"{app}.service" + stray.write_text(f'[Service]\nExecStart="/usr/bin/{app}" daemon\n') + printed = self.env["CONFIG_HOME_RAW"] = systemd_environment_value(str(config_home)) + with self.subTest(app=app, printed=printed): + self.assertTrue(printed.startswith("$'") and printed.endswith("'"), + "a path of spaces is not what a plain value looks like") + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + self.assertTrue(stray.is_file(), "guessed at a directory no manager reads") + del self.env["CONFIG_HOME_RAW"] + + def test_control_character_manager_config_home_is_resolved(self): + self.online() + for app in ("omawake", "omaspeak"): + for suffix in ("tab\tpath", "newline\npath\n", "\a\b\f\r\v", "\x01\x1b\x7f"): + config_home = self.home / (app + suffix) + self.units = config_home / "systemd/user" + self.units.mkdir(parents=True) + unit, link = self.install(app) + self.env["CONFIG_HOME_RAW"] = systemd_environment_value(str(config_home)) + with self.subTest(app=app, suffix=suffix): + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + + def test_unreadable_manager_config_home_aborts_the_cleanup(self): + self.online() + for app in ("omawake", "omaspeak"): + unit, link = self.install(app) + # Truncated output must not make cleanup guess at a directory. + self.env["CONFIG_HOME_RAW"] = "$'" + str(self.home / f"broken {app} config") + with self.subTest(app=app): + result = self.run_remove(app) + self.assertNotEqual(result.returncode, 0) + self.assertTrue(unit.exists()) + self.assertTrue(link.is_symlink()) + self.assertNotIn(" stop ", self.log.read_text()) + del self.env["CONFIG_HOME_RAW"] + + def test_relative_manager_config_home_keeps_the_default_directory(self): + self.online() + for app in ("omawake", "omaspeak"): + unit, link = self.install(app) + # An XDG_CONFIG_HOME that is not absolute is no setting at all: the + # manager itself reads the home's .config directory then. + self.env["CONFIG_HOME_RAW"] = systemd_environment_value(f"relative {app} config") + with self.subTest(app=app): + result = self.run_remove(app) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertFalse(unit.exists()) + self.assertFalse(link.is_symlink()) + del self.env["CONFIG_HOME_RAW"] + + def test_packaging_installs_hooks_and_helpers(self): + import shutil + for app, version in (("omawake", "0.0.3"), ("omaspeak", "0.0.3")): + source = self.root / app / "src" + package = self.root / app / "pkg" + release = source / f"{app}-{version}-linux-x86_64" + release.mkdir(parents=True) + for path in [app, "lib/libaudiocpp.so.0.1.0", f"packaging/systemd/{app}.service", + "README.md", "INSTALL.md", "ACCELERATOR_SETUP.md", "CHANGELOG.md", + "RELEASE_NOTES.md", "DEMO.md", "RUNTIME.md", "config.example.toml", + "licenses/LICENSE", "assets/fixture", "benchmarks/fixture"]: + target = release / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text("fixture") + directory = ROOT / f"pkgbuilds/{app}-bin" + for name in ("package-remove", "remove-user-services.hook"): + shutil.copyfile(directory / name, source / name) + env = dict(self.env, srcdir=str(source), pkgdir=str(package), CARCH="x86_64") + result = subprocess.run(["bash", "-c", 'source "$1"; package', "package-fixture", str(directory / "PKGBUILD")], env=env, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + helper = package / f"usr/lib/{app}/package-remove" + self.assertEqual(helper.read_bytes(), (directory / "package-remove").read_bytes()) + self.assertEqual(helper.stat().st_mode & 0o777, 0o755) + self.assertTrue((package / f"usr/share/libalpm/hooks/30-{app}-remove-user-services.hook").exists()) + + def test_hook_contract_and_package_release(self): + scripts = [] + for app in ("omawake", "omaspeak"): + directory = ROOT / f"pkgbuilds/{app}-bin" + hook = (directory / "remove-user-services.hook").read_text() + self.assertIn("Operation = Remove", hook) + self.assertNotIn("Operation = Upgrade", hook) + self.assertIn("When = PreTransaction", hook) + self.assertIn("AbortOnFail", hook) + self.assertIn(f"Exec = /usr/lib/{app}/package-remove {app}", hook) + self.assertIn("pkgrel=4", (directory / "PKGBUILD").read_text()) + scripts.append((directory / "package-remove").read_bytes()) + self.assertEqual(*scripts) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pr-workflow-approval.cjs b/tests/pr-workflow-approval.cjs new file mode 100644 index 0000000..e4fc661 --- /dev/null +++ b/tests/pr-workflow-approval.cjs @@ -0,0 +1,314 @@ +const assert = require('node:assert/strict'); +const { readFileSync, mkdtempSync, rmSync } = require('node:fs'); +const { join } = require('node:path'); +const { tmpdir } = require('node:os'); +const { test } = require('node:test'); +const { execFileSync, spawnSync } = require('node:child_process'); +const approve = require('../.github/scripts/approve-pr-workflows.cjs'); + +const BUILD = '.github/workflows/build-pr.yml'; +const TESTS = '.github/workflows/test.yml'; +const time = '2026-09-19T02:47:52Z'; +const earlier = '2026-09-19T02:36:04Z'; +const pr = { + number: 390, state: 'open', updated_at: time, + head: { sha: 'reviewed-sha', ref: 'ghost', repo: { id: 42 } }, + labels: [{ name: 'build-approved' }], +}; +const clone = value => structuredClone(value); +const run = (id, path, overrides = {}) => ({ + id, path, event: 'pull_request', head_sha: pr.head.sha, + head_repository: { id: 42 }, head_branch: 'ghost', pull_requests: [], + status: 'completed', conclusion: 'action_required', created_at: time, + ...overrides, +}); + +function fixture(initial = [run(1, TESTS, { created_at: earlier }), run(2, BUILD)], options = {}) { + const state = { pr: clone(pr), runs: clone(initial), approved: [], reads: 0, tick: 0, transitions: [] }; + const repo = { owner: 'omacom', repo: 'omarchy-pkgs' }; + const github = { + rest: { + pulls: { get: async args => { + assert.deepEqual(args, { ...repo, pull_number: 390 }); + state.reads++; + options.onRead?.(state); + return { data: clone(state.pr) }; + } }, + actions: { + listWorkflowRunsForRepo() {}, + getWorkflowRun: async ({ run_id }) => { + const current = state.runs.find(run => run.id === run_id); + state.transitions.push([run_id, current.status]); + return { data: clone(current) }; + }, + approveWorkflowRun: async args => { + assert.deepEqual(args, { ...repo, run_id: args.run_id }); + options.onApprove?.(state, args.run_id); + const current = state.runs.find(run => run.id === args.run_id); + assert.equal(current.conclusion, 'action_required'); + state.approved.push(current.id); + current.status = 'queued'; + current.conclusion = null; + }, + }, + }, + paginate: async (method, args) => { + assert.equal(method, github.rest.actions.listWorkflowRunsForRepo); + assert.deepEqual(args, { ...repo, event: 'pull_request', head_sha: pr.head.sha, per_page: 100 }); + return clone(state.runs).reverse(); // GitHub returns newest first. + }, + }; + const invoke = overrides => approve({ + github, context: { repo, payload: { action: 'labeled', pull_request: clone(pr) } }, + core: { info() {} }, vouchStatus: 'unknown', attempts: 6, + sleep: async () => { + state.tick++; + for (const current of state.runs) { + if (current.status === 'queued' && state.tick >= (options.queueUntil ?? 1)) current.status = 'in_progress'; + } + options.onSleep?.(state); + }, + ...overrides, + }); + return { state, invoke, github }; +} + +test('an unvouched, labeled fork PR releases both required workflows', async () => { + const { state, invoke } = fixture(); + await invoke(); + assert.deepEqual(state.approved, [1, 2]); +}); + +test('waits for the label-triggered build instead of stopping at the old build', async () => { + const { state, invoke } = fixture([ + run(1, BUILD, { created_at: earlier }), run(2, TESTS, { created_at: earlier }), + ], { + onSleep(state) { + if (state.tick === 2) { + assert.deepEqual(state.approved, []); + state.runs.push(run(3, BUILD)); + } + }, + queueUntil: 4, + onApprove(state, id) { + if (id === 3) assert.equal(state.runs.find(run => run.id === 1).status, 'in_progress'); + }, + }); + await invoke({ attempts: 10 }); + assert.deepEqual(state.approved, [1, 2, 3]); + assert.ok(state.transitions.some(([id, status]) => id === 1 && status === 'queued')); +}); + +test('approves only the two known workflows for this fork, branch, PR and SHA', async () => { + const unrelated = [ + { path: '.github/workflows/publish.yml' }, { event: 'push' }, + { head_sha: 'other-sha' }, { head_repository: { id: 99 } }, + { head_branch: 'other-branch' }, { pull_requests: [{ number: 391 }] }, + ].map((overrides, i) => run(10 + i, BUILD, overrides)); + const { state, invoke } = fixture([run(1, TESTS), run(2, BUILD), ...unrelated]); + await invoke(); + assert.deepEqual(state.approved, [1, 2]); +}); + +test('accepts a run explicitly associated with this PR', async () => { + const { state, invoke } = fixture([ + run(1, TESTS), run(2, BUILD, { pull_requests: [{ number: 390 }] }), + ]); + await invoke(); + assert.deepEqual(state.approved, [1, 2]); +}); + +test('does not restart running or completed workflows', async () => { + const { state, invoke } = fixture([ + run(1, TESTS, { conclusion: 'success' }), + run(2, BUILD, { status: 'in_progress', conclusion: null }), + ]); + await invoke(); + assert.deepEqual(state.approved, []); +}); + +test('an obsolete build hold cannot cancel a newer build that was already released', async () => { + for (const current of [ + { status: 'queued', conclusion: null }, { status: 'in_progress', conclusion: null }, + { status: 'completed', conclusion: 'success' }, { status: 'completed', conclusion: 'failure' }, + ]) { + const { state, invoke } = fixture([ + run(1, BUILD, { created_at: earlier }), run(2, TESTS), run(3, BUILD, current), + ]); + await invoke(); + assert.deepEqual(state.approved, [2]); + } +}); + +for (const status of ['denounced', '', undefined, 'unexpected']) { + test(`vouch status ${String(status)} fails closed`, async () => { + const { state, invoke } = fixture(); + await assert.rejects(invoke({ vouchStatus: status }), /Cannot approve workflows/); + assert.deepEqual(state.approved, []); + }); +} + +for (const status of ['bot', 'collaborator', 'vouched']) { + test(`a labeled ${status} can also clear GitHub's approval gate`, async () => { + const { state, invoke } = fixture(); + await invoke({ vouchStatus: status }); + assert.deepEqual(state.approved, [1, 2]); + }); +} + +for (const [name, change] of [ + ['removed label', pr => { pr.labels = []; }], + ['changed head', pr => { pr.head.sha = 'new-sha'; }], + ['closed PR', pr => { pr.state = 'closed'; }], +]) { + test(`${name} stops approval, including changes immediately before a write`, async () => { + for (const read of [1, 2]) { + const { state, invoke } = fixture(undefined, { onRead(state) { + if (state.reads === read) change(state.pr); + } }); + await invoke(); + assert.deepEqual(state.approved, []); + } + }); +} + +test('revocation between approvals prevents releasing further workflows', async () => { + const { state, invoke } = fixture(undefined, { onSleep(state) { state.pr.labels = []; } }); + await invoke(); + assert.deepEqual(state.approved, [1]); +}); + +test('a delayed tests workflow is also awaited', async () => { + const { state, invoke } = fixture([run(2, BUILD)], { + onSleep(state) { if (state.tick === 2) state.runs.push(run(1, TESTS)); }, + }); + await invoke(); + assert.deepEqual(state.approved, [1, 2]); +}); + +test('reopening a labeled PR waits for its new tests, even if old tests passed at the same SHA', async () => { + const { state, invoke } = fixture([ + run(1, TESTS, { created_at: earlier, conclusion: 'success' }), run(2, BUILD), + ], { onSleep(state) { if (state.tick === 2) state.runs.push(run(3, TESTS)); } }); + await invoke({ context: { repo: { owner: 'omacom', repo: 'omarchy-pkgs' }, + payload: { action: 'reopened', pull_request: clone(pr) } } }); + assert.deepEqual(state.approved, [2, 3]); +}); + +test('missing current runs time out without approving stale builds', async () => { + const { state, invoke } = fixture([run(1, TESTS), run(2, BUILD, { created_at: earlier })]); + await assert.rejects(invoke(), /Timed out/); + assert.deepEqual(state.approved, []); +}); + +test('API failure is reported rather than silently treated as approval', async () => { + const { state, invoke } = fixture(undefined, { onApprove() { throw new Error('Forbidden'); } }); + await assert.rejects(invoke(), /Forbidden/); + assert.deepEqual(state.approved, []); +}); + +// Execute the actual build workflow's approval script and shell gate. This +// covers the stale event payload that originally accompanied held PR runs. +const workflow = readFileSync(join(__dirname, '../.github/workflows/build-pr.yml'), 'utf8'); +const approvalScript = workflow.match(/- id: approval[\s\S]*?script: \|\n([\s\S]*?)(?= # One matrix)/)[1] + .split('\n').map(line => line.replace(/^ /, '')).join('\n'); +const gateScript = workflow.match(/ case "\$STATUS" in[\s\S]*? esac/)[0] + '\nprintf "%s" "$trusted"'; + +test('the build reads the live label rather than its pre-label event payload', async () => { + const execute = new (Object.getPrototypeOf(async function () {}).constructor)('github', 'context', 'core', approvalScript); + for (const [current, approved] of [ + [pr, true], [{ ...pr, labels: [] }, false], + [{ ...pr, head: { ...pr.head, sha: 'new-sha' } }, false], + [{ ...pr, state: 'closed' }, false], + ]) { + const outputs = {}; + await execute({ rest: { pulls: { get: async () => ({ data: current }) } } }, + { repo: {}, payload: { pull_request: { ...pr, labels: [] } } }, + { setOutput: (key, value) => { outputs[key] = value; } }); + assert.equal(outputs.approved, approved); + } +}); + +test('the build gate permits a missing vouch only with approval, never a denouncement or lookup failure', () => { + for (const [status, approved, expected] of [ + ['unknown', 'true', 'true'], ['unknown', 'false', 'false'], + ['denounced', 'true', 'false'], ['', 'true', 'false'], ['unexpected', 'true', 'false'], + ['vouched', 'false', 'true'], ['collaborator', 'false', 'true'], + ['bot', 'false', 'true'], ['dispatch', 'false', 'true'], + ]) { + assert.equal(execFileSync('bash', ['-c', gateScript], { + env: { ...process.env, STATUS: status, APPROVED: approved }, encoding: 'utf8', + }), expected); + } +}); + +// Exercise the actual reporting job, including its GitHub check name: a +// successful/skipped check called "result" would accidentally allow merging +// a PR whose build never ran. GitHub keeps a missing required check pending. +const resultJob = workflow.slice(workflow.indexOf('\n result:\n')); +const resultName = resultJob.match(/^ name: (.+)$/m)[1]; +const resultScript = resultJob.split(' - run: |\n')[1]; +function report({ trusted = 'false', vouch = 'unknown', empty = 'false', changes = 'success', build = 'skipped' } = {}) { + const needs = { + changes: { result: changes, outputs: { trusted, vouch_status: vouch, empty } }, + build: { result: build }, + }; + // The reporting expressions use &&, || and string equality, with the + // same semantics in JavaScript and Actions for these string-only fixtures. + const render = text => text.replace(/\$\{\{(.*?)\}\}/g, (_, expression) => + new Function('needs', `return (${expression})`)(needs)); + const directory = mkdtempSync(join(tmpdir(), 'build-approval-report-')); + const summaryPath = join(directory, 'summary'); + try { + const result = spawnSync('bash', ['-e', '-c', render(resultScript)], { + env: { ...process.env, GITHUB_STEP_SUMMARY: summaryPath }, encoding: 'utf8', + }); + return { name: render(resultName), ...result, + summary: result.stdout.includes('::notice::') ? readFileSync(summaryPath, 'utf8') : '' }; + } finally { + rmSync(directory, { recursive: true, force: true }); + } +} + +test('an unvouched PR waits without publishing a passing or failing required result', () => { + const result = report(); + assert.equal(result.name, 'Awaiting build approval'); + assert.equal(result.status, 0); + assert.match(result.stdout, /::notice::Awaiting maintainer build approval/); + assert.doesNotMatch(result.stdout, /::error::/); + assert.match(result.summary, /required \*\*result\*\* check remains pending/); +}); + +test('applying build-approved transitions the waiting PR to the required build result', () => { + assert.notEqual(report().name, 'result'); + const approved = report({ trusted: 'true', build: 'success' }); + assert.equal(approved.name, 'result'); + assert.equal(approved.status, 0); + const failed = report({ trusted: 'true', build: 'failure' }); + assert.equal(failed.name, 'result'); + assert.notEqual(failed.status, 0); +}); + +test('trusted tooling-only PRs still satisfy the required result without a package build', () => { + const result = report({ trusted: 'true', vouch: 'vouched' }); + assert.equal(result.name, 'result'); + assert.equal(result.status, 0); +}); + +for (const [name, overrides] of [ + ['denounced author', { vouch: 'denounced' }], + ['failed trust lookup', { vouch: '', changes: 'failure' }], + ['missing trust result', { vouch: '' }], + ['missing gate output', { trusted: '' }], + ['failed planning', { changes: 'failure' }], + ['cancelled planning', { changes: 'cancelled' }], + ['empty PR', { empty: 'true' }], + ['cancelled build', { trusted: 'true', build: 'cancelled' }], +]) { + test(`${name} fails the required result instead of masquerading as pending approval`, () => { + const result = report(overrides); + assert.equal(result.name, 'result'); + assert.notEqual(result.status, 0); + assert.doesNotMatch(result.stdout, /::notice::Awaiting maintainer build approval/); + }); +} diff --git a/tests/publish-artifact.sh b/tests/publish-artifact.sh new file mode 100755 index 0000000..2c5269c --- /dev/null +++ b/tests/publish-artifact.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Self-test for bin/publish-artifact against a local directory as the remote. +# Needs repo-add, gpg, rclone, bsdtar (run in the Arch builder/test container). +set -euo pipefail +ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +T=$(mktemp -d); chmod 755 "$T"; trap 'rm -rf "$T"' EXIT +REMOTE="$T/r2"; mkdir -p "$REMOTE" + +# throwaway signing key +export GNUPGHOME="$T/g"; mkdir -m700 "$GNUPGHOME" +gpg --batch --quiet --passphrase '' --quick-gen-key 'Test ' ed25519 sign 0 2>/dev/null +export GPG_PRIVATE_KEY=$(gpg --batch --armor --export-secret-keys 'Test ') GPG_PASSPHRASE='' +unset GNUPGHOME + +# minimal real packages via makepkg +mkpkg() { # mkpkg [payload] + local d="$T/src/$1-$2${4:+-$4}"; mkdir -p "$d"; cd "$d" + printf 'pkgname=%s\npkgver=1.0\npkgrel=%s\narch=(%s)\npackage(){ install -Dm644 /dev/null "$pkgdir/usr/share/%s-%s"; echo "%s" > "$pkgdir/usr/share/%s-%s"; }\n' "$1" "$2" "$3" "$1" "$2" "${4:-payload}" "$1" "$2" > PKGBUILD + # CARCH so the PKGINFO records the requested arch (--ignorearch would + # stamp the host's). + # makepkg refuses to run as root (the CI test container does); build the + # fixture as an unprivileged user in that case. + if (( EUID == 0 )); then + id -u fixture >/dev/null 2>&1 || useradd -m fixture + chmod 755 "$T/src"; chown -R fixture "$d" + runuser -u fixture -- env CARCH=$3 makepkg -f --nodeps --ignorearch >/dev/null 2>&1 + else + CARCH=$3 makepkg -f --nodeps --ignorearch >/dev/null 2>&1 + fi + ls "$d"/*.pkg.tar.zst +} +A1=$(mkpkg alpha 1 any); A2=$(mkpkg alpha 2 any); B1=$(mkpkg beta 1 x86_64); C1=$(mkpkg gamma 1 aarch64) + +pub() { "$ROOT/bin/publish-artifact" --remote "$REMOTE" --mirror edge --arch x86_64 "$@" >"$T/out" 2>&1; } +entries() { tar -tf "$REMOTE/edge/x86_64/omarchy.db.tar.zst" | grep '/$' | sort | tr '\n' ' '; } +pass() { echo "PASS: $1"; } +fail() { echo "FAIL: $1"; cat "$T/out"; exit 1; } + +pub "$A1" && [[ "$(entries)" == "alpha-1.0-1/ " ]] && [[ -f "$REMOTE/edge/x86_64/$(basename "$A1").sig" ]] \ + && pass "first publish creates db with one entry and a signature" || fail "first publish" + +sum_before=$(sha256sum "$REMOTE/edge/x86_64/$(basename "$A1")") +pub "$B1" && [[ "$(entries)" == "alpha-1.0-1/ beta-1.0-1/ " ]] && [[ "$(sha256sum "$REMOTE/edge/x86_64/$(basename "$A1")")" == "$sum_before" ]] \ + && pass "second package added incrementally; first file untouched" || fail "incremental add" + +pub "$A2" && [[ "$(entries)" == "alpha-1.0-2/ beta-1.0-1/ " ]] && [[ -f "$REMOTE/edge/x86_64/$(basename "$A1")" ]] \ + && pass "new pkgrel replaces the db entry, old file remains on remote" || fail "replace entry" + +# Same bytes again: allowed, idempotent (this is how a fast-ring artifact +# reaches rc and stable after edge, and how a re-run recovers). +pub "$A2" && grep -q 'identical bytes' "$T/out" && [[ "$(entries)" == "alpha-1.0-2/ beta-1.0-1/ " ]] \ + && pass "identical bytes under an existing name: accepted, db unchanged" || fail "identical republish" + +# Orphan repair: a file that reached the remote but whose db entry was lost +# (a concurrent publish overwrote the db) is fixed by publishing it again. +( cd "$REMOTE/edge/x86_64" && repo-remove --quiet omarchy.db.tar.zst alpha >/dev/null 2>&1 ) +[[ "$(entries)" == "beta-1.0-1/ " ]] || fail "fixture: could not drop alpha from the db" +pub "$A2" && [[ "$(entries)" == "alpha-1.0-2/ beta-1.0-1/ " ]] \ + && pass "orphaned file regains its db entry on republish" || fail "orphan repair" + +# Different bytes under an existing name: refused. Build alpha-2 again with +# a different payload (makepkg is reproducible, so the content must change). +A2b=$(mkpkg alpha 2 any different-payload) +[[ "$(md5sum < "$A2")" != "$(md5sum < "$A2b")" ]] || { echo "fixture: rebuilt package is byte-identical, cannot test"; exit 1; } +if pub "$A2b"; then fail "different bytes under same filename should refuse"; else grep -q 'DIFFERENT bytes' "$T/out" && pass "different bytes under an existing name refused" || fail "wrong refusal reason"; fi + +if pub "$C1"; then fail "aarch64 package into x86_64 should refuse"; else grep -q 'publishing to x86_64' "$T/out" && pass "wrong-arch package refused" || fail "wrong-arch reason"; fi + +cp "$B1" "$T/renamed-1.0-1-x86_64.pkg.tar.zst" +if pub "$T/renamed-1.0-1-x86_64.pkg.tar.zst"; then fail "filename/PKGINFO mismatch should refuse"; else grep -q 'does not match PKGINFO' "$T/out" && pass "filename must match PKGINFO" || fail "mismatch reason"; fi + +# db must verify: pacman can read it and each package's signature checks +gpg --batch --quiet --import <<<"$GPG_PRIVATE_KEY" 2>/dev/null || true +( cd "$REMOTE/edge/x86_64" && for f in *.pkg.tar.zst; do gpg --batch --quiet --verify "$f.sig" "$f" 2>/dev/null || { echo "FAIL: signature $f"; exit 1; }; done ) && pass "all signatures verify" diff --git a/tests/published-build-plan.sh b/tests/published-build-plan.sh new file mode 100755 index 0000000..538eff7 --- /dev/null +++ b/tests/published-build-plan.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# The timer and builder must agree when an older archive remains published. +set -euo pipefail +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +TEST_ROOT=$(mktemp -d) +trap 'rm -rf "$TEST_ROOT"' EXIT +mkdir -p "$TEST_ROOT/bin" "$TEST_ROOT/pkgbuilds" "$TEST_ROOT/state" +cp "$BUILD_ROOT/bin/check-versions" "$TEST_ROOT/bin/" +cp -r "$BUILD_ROOT/helpers" "$BUILD_ROOT/build" "$TEST_ROOT/" +export OMARCHY_STATE_DIR="$TEST_ROOT/state" +unset OMARCHY_REPO_ROOT OMARCHY_RC_PINS OMARCHY_DEFER_RUNTIME_DEPS + +fixture() { + local name=$1 version=$2 release=$3 + mkdir -p "$TEST_ROOT/pkgbuilds/$name/.omarchy" + printf '{"source":"local","channels":["edge"]}\n' > "$TEST_ROOT/pkgbuilds/$name/.omarchy/package.json" + cat > "$TEST_ROOT/pkgbuilds/$name/PKGBUILD" < "$TEST_ROOT/db/$name-$version/desc" +} +db_entry omarchy 4.0.4rc1-1 +db_entry omarchy-settings 4.0.4rc1-1 +for package in current update rebuild; do db_entry "$package" 1-1; done +printf '%s\n' new-package other-arch rebuild signature-only update | sort > "$TEST_ROOT/expected" + +for arch in x86_64 aarch64; do + repo="$TEST_ROOT/pkgs.omarchy.org/edge/$arch" + mkdir -p "$repo" + tar --zstd -cf "$repo/omarchy.db.tar.zst" -C "$TEST_ROOT/db" . + touch "$repo/omarchy-4.0.3-1-$arch.pkg.tar.zst" + touch "$repo/omarchy-settings-4.0.3-1-any.pkg.tar.xz" + touch "$repo/unindexed-1-1-$arch.pkg.tar.zst" + touch "$repo/rebuild-1-1-$arch.pkg.tar.zst" + touch "$repo/signature-only-1-1-$arch.pkg.tar.zst.sig" + other=x86_64 + [[ "$arch" == x86_64 ]] && other=aarch64 + touch "$repo/other-arch-1-1-$other.pkg.tar.zst" + + "$TEST_ROOT/bin/check-versions" --arch "$arch" > "$TEST_ROOT/check.log" 2>&1 + sort "$TEST_ROOT/state/.sync-needed-edge-$arch" > "$TEST_ROOT/queue" + diff -u "$TEST_ROOT/expected" "$TEST_ROOT/queue" + + for selection in '' 'omarchy omarchy-settings current update rebuild unindexed signature-only other-arch new-package'; do + ARCH="$arch" MIRROR=edge DRY_RUN=true PACKAGES="$selection" \ + PKGBUILDS_DIR="$TEST_ROOT/pkgbuilds" HELPERS_DIR="$TEST_ROOT/helpers" \ + FINAL_OUTPUT_DIR="$repo" BUILD_PLAN_DIR="$TEST_ROOT/plan" \ + "$TEST_ROOT/build/build.sh" > "$TEST_ROOT/plan.log" 2>&1 + sort "$TEST_ROOT/plan/packages" > "$TEST_ROOT/planned" + diff -u "$TEST_ROOT/queue" "$TEST_ROOT/planned" + grep -q 'omarchy 4.0.3-1 - archive already published' "$TEST_ROOT/plan.log" + done + printf 'PASS: %s scheduler and explicit/unscoped plans skip retained archives, allow new versions and releases\n' "$arch" +done diff --git a/tests/upstream-watch.py b/tests/upstream-watch.py index 856a928..bb9c8dc 100644 --- a/tests/upstream-watch.py +++ b/tests/upstream-watch.py @@ -330,5 +330,71 @@ os.execv(os.environ['REAL_GIT'], ['git', *args]) self.assertEqual(metadata_file.read_bytes(), original) +class T3CodeHookTest(unittest.TestCase): + """Keep both desktop architectures on the same complete upstream release.""" + + def setUp(self): + work = tempfile.TemporaryDirectory() + self.addCleanup(work.cleanup) + self.root = Path(work.name) + self.recipe = self.root / "PKGBUILD" + self.recipe.write_text("pkgver=0.0.41\n") + self.feed = self.root / "latest-linux.yml" + self.feed.write_text("version: 0.0.42\npath: T3-Code-0.0.42-x86_64.AppImage\n") + self.arm_feed = self.root / "latest-linux-arm64.yml" + self.arm_feed.write_text("version: 0.0.42\npath: T3-Code-0.0.42-arm64.AppImage\n") + for arch in ("x86_64", "arm64"): + (self.root / f"T3-Code-0.0.42-{arch}.AppImage").write_text(arch) + # Serve only fixture assets, and record the requested release URLs. + curl = self.root / "curl" + curl.write_text('#!/bin/bash\nurl="${@: -1}"\nprintf "%s\\n" "$url" >> requests\ncat "${url##*/}"\n') + curl.chmod(0o755) + self.env = dict(os.environ, PATH=f"{self.root}:{os.environ['PATH']}") + + def run_hook(self): + return subprocess.run( + ['bash', str(ROOT / 'pkgbuilds/t3code-bin/.omarchy/upstream.sh')], + cwd=self.root, env=self.env, text=True, capture_output=True, + ) + + def test_hashes_both_architectures_from_one_release(self): + result = self.run_hook() + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(json.loads(result.stdout), { + 'pkgver': '0.0.42', + 'sha256sums': { + arch: [w.hash_file(self.root / f'T3-Code-0.0.42-{asset_arch}.AppImage', 'sha256')] + for arch, asset_arch in [('x86_64', 'x86_64'), ('aarch64', 'arm64')] + }, + }) + self.assertIn('/download/v0.0.42/latest-linux-arm64.yml', (self.root / 'requests').read_text()) + + def test_current_version_does_not_download_assets(self): + self.recipe.write_text('pkgver=0.0.42\n') + result = self.run_hook() + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(json.loads(result.stdout), {}) + self.assertEqual(len((self.root / 'requests').read_text().splitlines()), 1) + + def test_incomplete_or_mismatched_arm_release_reports_no_update(self): + for bad_feed in ('', 'version: 0.0.43\npath: T3-Code-0.0.42-arm64.AppImage\n', + 'version: 0.0.42\npath: renamed.AppImage\n'): + with self.subTest(feed=bad_feed): + self.arm_feed.write_text(bad_feed) + result = self.run_hook() + self.assertNotEqual(result.returncode, 0) + self.assertEqual(result.stdout, '') + self.arm_feed.unlink() + result = self.run_hook() + self.assertNotEqual(result.returncode, 0) + self.assertEqual(result.stdout, '') + + def test_missing_arm_asset_reports_no_update(self): + (self.root / 'T3-Code-0.0.42-arm64.AppImage').unlink() + result = self.run_hook() + self.assertNotEqual(result.returncode, 0) + self.assertEqual(result.stdout, '') + + if __name__ == '__main__': unittest.main(verbosity=2)