Compare commits
27
Commits
master
..
2cce621d95
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cce621d95 | ||
|
|
cb3909f052 | ||
|
|
678d7400ed | ||
|
|
ebd2d6a766 | ||
|
|
93b1655ca8 | ||
|
|
5d9783b85e | ||
|
|
48d6217ff7 | ||
|
|
7d2c7c50af | ||
|
|
e285432795 | ||
|
|
5961a3ff5d | ||
|
|
259a3fa8b8 | ||
|
|
37288aada4 | ||
|
|
128c5647ba | ||
|
|
024943141d | ||
|
|
a1ad25bbae | ||
|
|
77d62dd156 | ||
|
|
7d70768c3f | ||
|
|
efc09808c6 | ||
|
|
f004f5fa20 | ||
|
|
963cbf1230 | ||
|
|
a57d0fdb7f | ||
|
|
d37fcd09d1 | ||
|
|
c19b48c445 | ||
|
|
0e90a7652b | ||
|
|
4b6cc8ba0f | ||
|
|
2d56129a55 | ||
|
|
fe4b80b0a6 |
No files matched your search
@@ -0,0 +1,84 @@
|
||||
// Whether a PR rides to master on its own once the required checks pass.
|
||||
//
|
||||
// The rule is the build gate's, from build-pr.yml: a PR trusted to build is
|
||||
// trusted to ship. Collaborators, vouched authors and bots are trusted; an
|
||||
// unknown author is trusted while the PR carries build-approved; a
|
||||
// denouncement is absolute. Two limits on top of it:
|
||||
//
|
||||
// - Only package changes. A PR's workflows, scripts and build tooling never
|
||||
// run in its own build (build-pr.yml overlays only its package directories
|
||||
// onto base tooling), so green checks say nothing about them, and after
|
||||
// merge they run with the publish secrets. Allowed: anything under
|
||||
// pkgbuilds/, plus the test a package PR brings with it, which is a new
|
||||
// file under tests/ and lines in test.yml that only run new tests/*.sh.
|
||||
// test.yml runs on PRs only; an existing test may also run on master
|
||||
// (builder-images.yml runs tests/build-isolation.sh with packages: write),
|
||||
// so editing one still needs a maintainer.
|
||||
// - Not the upstream sync. It labels its own PR build-approved to release
|
||||
// GitHub's hold on its pushes, which is no one's approval; it stays on the
|
||||
// reviewed lane.
|
||||
const PACKAGES = 'pkgbuilds/';
|
||||
const TESTS = 'tests/';
|
||||
const TEST_WORKFLOW = '.github/workflows/test.yml';
|
||||
const TEST_LINE = /^\+\s*\.\/tests\/[A-Za-z0-9._-]+\.sh\s*$/;
|
||||
|
||||
// Every changed line adds a ./tests/<name>.sh call; nothing removed. A diff
|
||||
// too large for the API has no patch and does not qualify.
|
||||
function onlyRunsTests(patch) {
|
||||
if (!patch) return false;
|
||||
const changed = patch.split('\n').filter(line =>
|
||||
/^[+-]/.test(line) && !line.startsWith('+++') && !line.startsWith('---'));
|
||||
return changed.length > 0 && changed.every(line => TEST_LINE.test(line));
|
||||
}
|
||||
|
||||
function packageChange(file) {
|
||||
if (file.previous_filename && !file.previous_filename.startsWith(PACKAGES)) return false;
|
||||
if (file.filename.startsWith(PACKAGES)) return true;
|
||||
if (file.filename.startsWith(TESTS)) return file.status === 'added';
|
||||
if (file.filename === TEST_WORKFLOW) return file.status === 'modified' && onlyRunsTests(file.patch);
|
||||
return false;
|
||||
}
|
||||
const REVIEWED_BRANCHES = /^auto\/sync-upstream(\/|$)/;
|
||||
|
||||
function decide({ pr, files, vouchStatus, repository }) {
|
||||
if (pr.state !== 'open') return { enable: false, reason: 'PR is not open' };
|
||||
if (pr.draft) return { enable: false, reason: 'PR is a draft' };
|
||||
|
||||
const labelled = pr.labels.some(label => label.name === 'build-approved');
|
||||
let trusted;
|
||||
switch (vouchStatus) {
|
||||
case 'bot': case 'collaborator': case 'vouched': trusted = true; break;
|
||||
case 'unknown': trusted = labelled; break;
|
||||
default: trusted = false; // denounced, or a failed lookup
|
||||
}
|
||||
if (!trusted) {
|
||||
return { enable: false, reason: `author not trusted to build (${vouchStatus || 'missing'}${labelled ? ', labelled' : ''})` };
|
||||
}
|
||||
|
||||
if (pr.head.repo?.full_name === repository && REVIEWED_BRANCHES.test(pr.head.ref)) {
|
||||
return { enable: false, reason: `${pr.head.ref} stays on the reviewed lane` };
|
||||
}
|
||||
|
||||
if (!files.length) return { enable: false, reason: 'PR changes no files' };
|
||||
const outside = files.filter(file => !packageChange(file));
|
||||
if (outside.length) {
|
||||
const names = outside.slice(0, 3).map(file => file.filename).join(', ');
|
||||
return { enable: false, reason: `changes more than packages and their new tests: ${names}${outside.length > 3 ? ', ...' : ''}` };
|
||||
}
|
||||
return { enable: true, reason: `${vouchStatus === 'unknown' ? 'build-approved' : vouchStatus} author, package changes only` };
|
||||
}
|
||||
|
||||
module.exports = async function autoMerge({ github, context, core, number, vouchStatus }) {
|
||||
const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: number });
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
...context.repo, pull_number: number, per_page: 100,
|
||||
});
|
||||
const decision = decide({
|
||||
pr, files, vouchStatus, repository: `${context.repo.owner}/${context.repo.repo}`,
|
||||
});
|
||||
core.info(`#${number}: ${decision.enable ? 'auto-merge' : 'leave for a maintainer'} (${decision.reason})`);
|
||||
core.setOutput('enable', String(decision.enable));
|
||||
core.setOutput('head_sha', pr.head.sha);
|
||||
return decision;
|
||||
};
|
||||
module.exports.decide = decide;
|
||||
@@ -0,0 +1,108 @@
|
||||
name: Auto-merge approved package PRs
|
||||
|
||||
# A package PR trusted to build is trusted to ship: once its builds are
|
||||
# green it should merge and publish without a maintainer pressing the
|
||||
# button. This enables GitHub's auto-merge on such PRs; branch protection
|
||||
# still holds the merge until `result`, `self-tests` and `build-isolation`
|
||||
# pass, and a red build stays an open PR. .github/scripts/auto-merge-pr.cjs
|
||||
# has the rule.
|
||||
#
|
||||
# Auto-merge is enabled with the PAT in PKGS_BOT_TOKEN: a merge made with the
|
||||
# built-in GITHUB_TOKEN does not start publish.yml.
|
||||
#
|
||||
# pull_request_target runs this default-branch code with secrets; the PR's
|
||||
# code is never checked out here.
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr:
|
||||
description: 'PR number to evaluate'
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
concurrency:
|
||||
group: auto-merge-pr-${{ github.event.pull_request.number || github.event.inputs.pr }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
auto-merge:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Find the PR's author
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr }}
|
||||
with:
|
||||
script: |
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
...context.repo, pull_number: Number(process.env.NUMBER),
|
||||
});
|
||||
core.setOutput('number', String(pr.number));
|
||||
core.setOutput('author', pr.user.login);
|
||||
|
||||
- id: vouch
|
||||
uses: mitchellh/vouch/action/check-user@f23dbb5e745334f97414ec70463ce7301071a661 # v1
|
||||
with:
|
||||
user: ${{ steps.pr.outputs.author }}
|
||||
allow-fail: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Decide
|
||||
id: decide
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
NUMBER: ${{ steps.pr.outputs.number }}
|
||||
VOUCH_STATUS: ${{ steps.vouch.outputs.status }}
|
||||
with:
|
||||
script: |
|
||||
const autoMerge = require('./.github/scripts/auto-merge-pr.cjs');
|
||||
await autoMerge({ github, context, core,
|
||||
number: Number(process.env.NUMBER), vouchStatus: process.env.VOUCH_STATUS });
|
||||
|
||||
- name: Enable auto-merge
|
||||
if: steps.decide.outputs.enable == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
|
||||
PR: ${{ steps.pr.outputs.number }}
|
||||
HEAD_SHA: ${{ steps.decide.outputs.head_sha }}
|
||||
run: |
|
||||
if [[ -z "$GH_TOKEN" ]]; then
|
||||
echo "::error::Set PKGS_BOT_TOKEN; a GITHUB_TOKEN merge would not publish."
|
||||
exit 1
|
||||
fi
|
||||
# Idempotent: enabling twice errors. Bot lanes enable their own.
|
||||
if [[ "$(gh pr view "$PR" -R "$GITHUB_REPOSITORY" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
|
||||
echo "auto-merge already enabled on #$PR"
|
||||
exit 0
|
||||
fi
|
||||
# --match-head-commit: never arm a merge for a commit newer than
|
||||
# the one just judged.
|
||||
gh pr merge --auto --squash --match-head-commit "$HEAD_SHA" "$PR" -R "$GITHUB_REPOSITORY"
|
||||
|
||||
# Removing build-approved withdraws the approval, so withdraw the
|
||||
# auto-merge it armed too. Only on that event: auto-merge a maintainer
|
||||
# enabled by hand on any other PR is theirs to keep.
|
||||
- name: Withdraw auto-merge
|
||||
if: >-
|
||||
steps.decide.outputs.enable == 'false' &&
|
||||
github.event.action == 'unlabeled' && github.event.label.name == 'build-approved'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
|
||||
PR: ${{ steps.pr.outputs.number }}
|
||||
run: |
|
||||
if [[ "$(gh pr view "$PR" -R "$GITHUB_REPOSITORY" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
|
||||
gh pr merge --disable-auto "$PR" -R "$GITHUB_REPOSITORY"
|
||||
fi
|
||||
@@ -24,11 +24,10 @@ on:
|
||||
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
|
||||
# Only the publish job serializes (see its concurrency group): two publishes
|
||||
# into one channel at once would race on the database. Builds run outside the
|
||||
# lock, so a merge waits behind another merge's signing and upload, seconds,
|
||||
# never behind its kernel build.
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -58,11 +57,10 @@ jobs:
|
||||
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
|
||||
echo "count=$(jq '.include | length' <<<"$matrix")" >> "$GITHUB_OUTPUT"
|
||||
jq -r '.include[] | "\(.package) \(.arch) -> \(.channels)"' <<<"$matrix"
|
||||
# Reuse or rebuild, decided per entry and said out loud. An aarch64
|
||||
# tree with no build artifact (PR artifacts last 7 days; a dispatch
|
||||
# may name any package) goes to the rebuild job, which builds it
|
||||
# natively on GitHub's arm64 runner. x86_64 builds inside the
|
||||
# publish job on the droplet, as before.
|
||||
# Reuse or rebuild, decided per entry and said out loud. A tree with
|
||||
# no build artifact (PR artifacts last 7 days; a dispatch may name
|
||||
# any package) goes to the rebuild job: aarch64 natively on GitHub's
|
||||
# arm64 runner, x86_64 on a builder droplet, one runner per entry.
|
||||
rebuild=()
|
||||
echo "| package | arch | tree | decision |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| --- | --- | --- | --- |" >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -73,13 +71,23 @@ jobs:
|
||||
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 | select(. != null) | "run \(.workflow_run.id), expires \(.expires_at)"')
|
||||
# The plan the publish job makes, made here before a runner is
|
||||
# asked for: a tree the channel already holds at master's version
|
||||
# (a re-run, or a dispatch naming a package that is fine) needs
|
||||
# no build, and an x86 rebuild would wait minutes for a droplet
|
||||
# to find that out in seconds.
|
||||
plan=""
|
||||
[[ -n "$found" ]] || plan=$(bin/build --dry-run --mirror edge --arch "$arch" --package "$package" 2>&1 | grep -E '^==> Plan complete' || true)
|
||||
if [[ -n "$found" ]]; then
|
||||
decision="reuse the build artifact ($found)"
|
||||
elif [[ -n "$plan" && -z "$(sed -E 's/.*would build: *//' <<<"$plan" | tr -d '[:space:]')" ]]; then
|
||||
decision="already published at master's version, nothing to build"
|
||||
elif [[ $arch == aarch64 ]]; then
|
||||
decision="no build artifact: rebuild natively on ubuntu-24.04-arm"
|
||||
rebuild+=("$entry")
|
||||
rebuild+=("$(jq -c '. + {runner: "[\"ubuntu-24.04-arm\"]"}' <<<"$entry")")
|
||||
else
|
||||
decision="no build artifact: build in the publish job on the self-hosted builder"
|
||||
decision="no build artifact: rebuild on a builder droplet"
|
||||
rebuild+=("$(jq -c '. + {runner: "[\"self-hosted\",\"omarchy-builder\"]"}' <<<"$entry")")
|
||||
fi
|
||||
echo "==> $label: $decision"
|
||||
echo "| $package | $arch | ${hash:0:12} | $decision |" >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -87,16 +95,19 @@ jobs:
|
||||
echo "rebuild=$(printf '%s\n' "${rebuild[@]}" | jq -sc '{include: .}')" >> "$GITHUB_OUTPUT"
|
||||
echo "rebuild_count=${#rebuild[@]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# The aarch64 half of "build it now when there is none". It builds exactly
|
||||
# as build-pr.yml's aarch64 path does (same runner, same builder image,
|
||||
# same bin/build call) and uploads under the same label, so the publish
|
||||
# job collects this run's artifact the way it collects a PR's. No secret
|
||||
# reaches this runner; signing and upload stay on the self-hosted builder.
|
||||
# "Build it now when there is none". Each entry builds exactly as
|
||||
# build-pr.yml builds it (same runner kind, same builder image, same
|
||||
# bin/build call) and uploads under the same label, so the publish job
|
||||
# collects this run's artifact the way it collects a PR's. No secret
|
||||
# reaches these runners, and they hold no lock: entries build in parallel,
|
||||
# and other merges publish while they do.
|
||||
rebuild:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rebuild_count != '0'
|
||||
runs-on: ubuntu-24.04-arm
|
||||
timeout-minutes: 180
|
||||
runs-on: ${{ fromJSON(matrix.runner) }}
|
||||
# The droplets are x86, so aarch64 never builds there. omarchy-mac-boot
|
||||
# under QEMU took 2h47m; native arm64 and x86 kernels fit easily.
|
||||
timeout-minutes: 240
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
@@ -109,7 +120,7 @@ jobs:
|
||||
# The same check the publish job makes before building: a re-run for a
|
||||
# package the channel already holds at master's version builds
|
||||
# nothing, and uploads nothing that could shadow the published file.
|
||||
- name: Build ${{ matrix.package }} (${{ matrix.arch }}, native)
|
||||
- name: Build ${{ matrix.package }} (${{ matrix.arch }})
|
||||
id: build
|
||||
env:
|
||||
CONTAINER_ENGINE: docker
|
||||
@@ -140,23 +151,26 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
# One job for the whole merge. It collects every PR artifact for the
|
||||
# merged tree (building only what has none; aarch64 comes from the
|
||||
# rebuild job above), then walks each channel and
|
||||
# One job for the whole merge. It collects every artifact for the merged
|
||||
# tree (PR builds, or the rebuild job above), 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.
|
||||
# is no race between packages; the concurrency group keeps one merge's
|
||||
# publish from overlapping the next. It builds nothing, so it runs on a
|
||||
# hosted runner in about a minute instead of waiting for a droplet.
|
||||
# It waits for the rebuild job and runs whatever that job's result: a
|
||||
# failed rebuild leaves its package without an artifact, and the collect
|
||||
# step below records that and stops before any publish.
|
||||
publish:
|
||||
needs: [changes, rebuild]
|
||||
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.count != '0' }}
|
||||
runs-on: [self-hosted, omarchy-builder]
|
||||
runs-on: ubuntu-latest
|
||||
environment: publish
|
||||
timeout-minutes: 240
|
||||
timeout-minutes: 30
|
||||
concurrency:
|
||||
group: publish
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -172,8 +186,8 @@ jobs:
|
||||
EOF_MATRIX
|
||||
cat plan.txt
|
||||
|
||||
# Fetch each package's PR artifact into build-output/edge/<arch>/, or
|
||||
# build it when no artifact exists for exactly this tree. An artifact
|
||||
# Fetch each package's artifact into build-output/edge/<arch>/: the PR's,
|
||||
# or the rebuild job's when the PR's had expired. 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).
|
||||
@@ -199,8 +213,8 @@ jobs:
|
||||
mkdir -p "build-output/edge/$arch"
|
||||
if [[ -n "$found" ]]; then
|
||||
if [[ $from_run == "${{ github.run_id }}" ]]; then
|
||||
kind=native-rebuild
|
||||
echo "==> $label: artifact from this run's native $arch rebuild"
|
||||
kind=rebuild
|
||||
echo "==> $label: artifact from this run's $arch rebuild"
|
||||
else
|
||||
kind=pr-artifact
|
||||
echo "==> $label: reusing the build artifact from run $from_run"
|
||||
@@ -225,20 +239,11 @@ jobs:
|
||||
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"already-published"}' >> sources.jsonl
|
||||
continue
|
||||
fi
|
||||
# aarch64 never builds here: this droplet is x86 and would
|
||||
# emulate it. No artifact means the native rebuild failed (see
|
||||
# the rebuild job), or an artifact expired between planning
|
||||
# and now (re-run all jobs).
|
||||
if [[ $arch == aarch64 ]]; then
|
||||
echo "::error::$label: no artifact from the native rebuild; not building aarch64 under emulation"
|
||||
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"native-build-failed"}' >> sources.jsonl; failed=1; break
|
||||
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
|
||||
# Nothing builds here. No artifact means the rebuild failed (see
|
||||
# the rebuild job), or an artifact expired between planning and
|
||||
# now (re-run all jobs).
|
||||
echo "::error::$label: no artifact from the rebuild job"
|
||||
jq -nc --arg p "$package" --arg a "$arch" '{package:$p, arch:$a, source:"build-failed"}' >> sources.jsonl; failed=1; break
|
||||
fi
|
||||
done < plan.txt
|
||||
ls -1 build-output/edge/*/*.pkg.tar.zst 2>/dev/null || true
|
||||
@@ -280,8 +285,26 @@ jobs:
|
||||
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
|
||||
# A fresh runner has no images, and building this one here cost
|
||||
# every publish about 100 s (and 20 s more to start a container
|
||||
# from it) for the 14 s of signing and upload it is needed for.
|
||||
# builder-images.yml already publishes the tested image for exactly
|
||||
# these build inputs under their key; pull that. Build only when no
|
||||
# image carries the key: a merge that changed build/ publishes
|
||||
# before the refresh it triggered has finished.
|
||||
builder=omarchy-pkg-builder:latest-x86_64-edge
|
||||
if ! docker image inspect "$builder" >/dev/null 2>&1; then
|
||||
key=$(bin/builder-image key --arch x86_64 --mirror edge)
|
||||
published="ghcr.io/omacom/omarchy-pkg-builder:$key"
|
||||
if docker pull --quiet "$published" &&
|
||||
[[ $(docker image inspect "$published" --format '{{index .Config.Labels "org.omarchy.builder.key"}}') == "$key" ]]; then
|
||||
docker tag "$published" "$builder"
|
||||
echo "==> Builder image: pulled $published"
|
||||
else
|
||||
echo "==> Builder image: none published for $key, building it"
|
||||
docker buildx build --load -t "$builder" --build-arg MIRROR=edge build
|
||||
fi
|
||||
fi
|
||||
|
||||
# Group the merge's files by the (channel, architecture) slot each
|
||||
# belongs to. A package's files live under build-output/edge/<built
|
||||
@@ -374,7 +397,7 @@ jobs:
|
||||
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=="native-rebuild" then "rebuilt natively on arm64" elif .source=="built" then "built here" elif .source=="already-published" then "already published, skipped" else "**" + .source + "**" end;
|
||||
def src: if .source=="pr-artifact" then "PR artifact" elif .source=="rebuild" or .source=="native-rebuild" then "rebuilt at merge" 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),
|
||||
"",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
name: Sync Rebuild Triggers
|
||||
|
||||
# Rebuilds ride the unattended lane, like track-branches.yml: the pkgrel bump
|
||||
# PR builds on the droplets, auto-merge lands it once `result`, `self-tests`
|
||||
# and `build-isolation` are green, and the merge publishes. A rebuild that
|
||||
# fails stays an unmerged red PR for a maintainer.
|
||||
#
|
||||
# The PR and auto-merge use the PAT in PKGS_BOT_TOKEN, because a merge made
|
||||
# with the built-in GITHUB_TOKEN does not start publish.yml, and its pushes
|
||||
# are held for approval instead of building.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every 6 hours, off the hour to dodge the scheduling backlog at :00
|
||||
@@ -17,14 +26,17 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
branch: ${{ steps.branch.outputs.branch }}
|
||||
pushed_at: ${{ steps.pushed.outputs.at }}
|
||||
number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
operation: ${{ steps.cpr.outputs.pull-request-operation }}
|
||||
head_sha: ${{ steps.cpr.outputs.pull-request-head-sha }}
|
||||
|
||||
steps:
|
||||
- name: Require the bot token
|
||||
env:
|
||||
PKGS_BOT_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
|
||||
run: |
|
||||
if [[ -z "$PKGS_BOT_TOKEN" ]]; then
|
||||
echo "::error::Set PKGS_BOT_TOKEN to a PAT with Contents: write and Pull requests: write on this repository, owned by an account trusted to trigger builds."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -85,19 +97,12 @@ jobs:
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Runs created by this push are newer than this; the approve job
|
||||
# waits for them. A minute's slack absorbs runner clock skew.
|
||||
- name: Record push time
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
id: pushed
|
||||
run: echo "at=$(date -u -d '-1 minute' +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.PKGS_BOT_TOKEN }}
|
||||
commit-message: 'chore: rebuild against updated dependencies'
|
||||
title: "chore: rebuild against updated dependencies${{ steps.branch.outputs.scope && format(' ({0})', steps.branch.outputs.scope) || '' }}"
|
||||
body: |
|
||||
@@ -109,14 +114,27 @@ jobs:
|
||||
bump is what makes the rebuilt package an upgrade pacman will offer;
|
||||
without it the build produces the version already published and no
|
||||
one receives it.
|
||||
|
||||
This PR auto-merges once the build checks pass. A failing rebuild
|
||||
leaves it open for a maintainer.
|
||||
branch: ${{ steps.branch.outputs.branch }}
|
||||
delete-branch: true
|
||||
# The bot is trusted; build-approved lets the approve job below
|
||||
# release GitHub's hold on its pushes without a maintainer.
|
||||
labels: |
|
||||
automated
|
||||
build-approved
|
||||
reviewers: ryanrhughes
|
||||
labels: automated
|
||||
|
||||
# Auto-merge, not a direct merge: branch protection still has to see
|
||||
# the build checks green before the rebuild lands.
|
||||
- name: Enable auto-merge
|
||||
if: steps.cpr.outputs.pull-request-number != ''
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PKGS_BOT_TOKEN }}
|
||||
PR: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
run: |
|
||||
# Idempotent across re-runs of an updated PR: enabling twice errors.
|
||||
if [[ "$(gh pr view "$PR" -R "${{ github.repository }}" --json autoMergeRequest --jq '.autoMergeRequest != null')" == true ]]; then
|
||||
echo "auto-merge already enabled on #$PR"
|
||||
exit 0
|
||||
fi
|
||||
gh pr merge --auto --merge "$PR" -R "${{ github.repository }}"
|
||||
|
||||
- name: Notify Basecamp on failure
|
||||
if: failure() && env.BASECAMP_CHATBOT_URL != ''
|
||||
@@ -129,36 +147,3 @@ jobs:
|
||||
"🔴 <strong>Rebuild trigger sync failed</strong><br><a href=\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\">View run</a>" \
|
||||
'{content: $content}')" \
|
||||
"$BASECAMP_CHATBOT_URL"
|
||||
|
||||
# GitHub holds pull_request runs from a GITHUB_TOKEN push for approval and
|
||||
# creates no pull_request_target run for it, so approve-pr.yml never sees
|
||||
# the sync's own pushes. The sync labels its PR build-approved, so release
|
||||
# the held runs for the commit just pushed, whether it opened the PR or
|
||||
# updated it. A separate job, so the sync container's token never holds
|
||||
# actions: write.
|
||||
approve:
|
||||
needs: sync
|
||||
if: ${{ !cancelled() && (needs.sync.outputs.operation == 'created' || needs.sync.outputs.operation == 'updated') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
actions: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Release held build and test runs
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
NUMBER: ${{ needs.sync.outputs.number }}
|
||||
BRANCH: ${{ needs.sync.outputs.branch }}
|
||||
HEAD_SHA: ${{ needs.sync.outputs.head_sha }}
|
||||
SINCE: ${{ needs.sync.outputs.pushed_at }}
|
||||
with:
|
||||
script: |
|
||||
const approve = require('./.github/scripts/approve-sync-push.cjs');
|
||||
const { NUMBER, BRANCH, HEAD_SHA, SINCE } = process.env;
|
||||
await approve({ github, context, core, number: Number(NUMBER),
|
||||
branch: BRANCH, headSha: HEAD_SHA, since: SINCE });
|
||||
@@ -67,6 +67,7 @@ jobs:
|
||||
./tests/artifact-helpers.sh
|
||||
./tests/limine-mkinitcpio-hook.sh
|
||||
./tests/voxtype-bin-install.sh
|
||||
./tests/superwhisper-bin-install.sh
|
||||
pacman -S --noconfirm --quiet rclone >/dev/null
|
||||
./tests/publish-artifact.sh
|
||||
'
|
||||
@@ -0,0 +1,79 @@
|
||||
name: Unpublish retired packages
|
||||
|
||||
# Takes packages out of the channel databases after their recipes are gone
|
||||
# from master. Deleting a recipe stops it building; this stops pacman
|
||||
# offering it. Files stay in the bucket (see bin/unpublish-packages).
|
||||
#
|
||||
# Only packages with no recipe on master: one that still has a recipe would
|
||||
# come back on its next publish, and refusing it keeps a typo from pulling a
|
||||
# live package out of every channel.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
packages:
|
||||
description: "Space-separated pkgbases whose recipes were removed from master"
|
||||
required: true
|
||||
channels:
|
||||
description: "Channels to remove them from"
|
||||
required: true
|
||||
default: "edge rc stable"
|
||||
|
||||
jobs:
|
||||
unpublish:
|
||||
runs-on: ubuntu-latest
|
||||
environment: publish
|
||||
timeout-minutes: 15
|
||||
# The publish job's group: one writer per channel database at a time.
|
||||
concurrency:
|
||||
group: publish
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Refuse packages that still have a recipe
|
||||
env:
|
||||
PACKAGES: ${{ github.event.inputs.packages }}
|
||||
CHANNELS: ${{ github.event.inputs.channels }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for c in $CHANNELS; do
|
||||
[[ $c == edge || $c == rc || $c == stable ]] || { echo "::error::Unknown channel: $c"; exit 1; }
|
||||
done
|
||||
for p in $PACKAGES; do
|
||||
[[ $p =~ ^[a-z0-9@._+-]+$ ]] || { echo "::error::Not a package name: $p"; exit 1; }
|
||||
if [[ -e pkgbuilds/$p ]]; then
|
||||
echo "::error::pkgbuilds/$p still exists on master; remove the recipe first"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Unpublish
|
||||
env:
|
||||
PACKAGES: ${{ github.event.inputs.packages }}
|
||||
CHANNELS: ${{ github.event.inputs.channels }}
|
||||
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 }}
|
||||
OMARCHY_PUBLISH_PREFIX: ${{ vars.OMARCHY_PUBLISH_PREFIX }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# repo-remove and bsdtar are Arch tools: run in the tested builder
|
||||
# image, as the publish job does.
|
||||
builder=ghcr.io/omacom/omarchy-pkg-builder:$(bin/builder-image key --arch x86_64 --mirror edge)
|
||||
docker pull --quiet "$builder"
|
||||
for mirror in $CHANNELS; do
|
||||
for arch in x86_64 aarch64; do
|
||||
docker run --rm \
|
||||
-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 "$builder" \
|
||||
bin/unpublish-packages --remote R2:omarchy-pkgs --mirror "$mirror" --arch "$arch" $PACKAGES
|
||||
done
|
||||
done
|
||||
@@ -565,12 +565,26 @@ bin/repo deploy # Build locally, then publish from the host
|
||||
bin/repo push # Upload local builds to the host and publish
|
||||
bin/add-package <package> # Add an Omarchy-owned package with metadata
|
||||
bin/package-worktree <package> # Inspect historical AUR provenance in a scratch workspace
|
||||
bin/repo remove <package> # Remove package
|
||||
bin/repo remove <package> # Remove package (host workflow; CI uses unpublish.yml)
|
||||
bin/sync-upstream # Update packages that track a vendor release feed
|
||||
bin/sync-rebuilds # Bump pkgrel for packages whose dependencies moved
|
||||
bin/clean-docker # Clear Docker images/cache (forces fresh rebuild)
|
||||
```
|
||||
|
||||
### Retiring a package
|
||||
|
||||
Delete its recipe directory in a PR. Once that merges, take it out of the
|
||||
channel databases with the **Unpublish retired packages** workflow:
|
||||
|
||||
```
|
||||
gh workflow run unpublish.yml -f packages="<pkgbase> ..." -f channels="edge rc stable"
|
||||
```
|
||||
|
||||
It removes every entry built from those pkgbases (split outputs and `-debug`
|
||||
included) from both architectures' databases, and refuses any package that
|
||||
still has a recipe on master. Package files stay in the bucket: published
|
||||
filenames are immutable, and nothing references them once the entries are gone.
|
||||
|
||||
### Package Metadata Tools
|
||||
|
||||
```bash
|
||||
@@ -893,15 +907,15 @@ The repository includes GitHub workflows and systemd services for automated rele
|
||||
#### GitHub Workflows
|
||||
|
||||
1. **sync-upstream.yml** (Every 6 hours): Watches direct upstream feeds and updates owned recipes on the reviewed lane. 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.
|
||||
2. **sync-rebuilds.yml** (Every 6 hours): Bumps pkgrel for packages whose `rebuild_on` dependencies have moved in the official repositories, opens a PR, and enables auto-merge. The PR lands once its build checks pass; a rebuild that fails stays an open red PR for a maintainer.
|
||||
3. **track-branches.yml** (Every 2 hours): The unattended lane. Pins every `"auto_merge": true` package to the tip of its watched branch once its commit timestamp clears `min_release_age`, opens one PR for all of them, and enables auto-merge. Packages pinned from the same branch move together or not at all, including targeted syncs. The PR builds like any other; a tip that fails to build stays an open red PR until the next tick supersedes it.
|
||||
|
||||
The tracking PR and auto-merge use the PAT stored in `PKGS_BOT_TOKEN`, with
|
||||
The tracking and rebuild PRs and their auto-merge use the PAT stored in `PKGS_BOT_TOKEN`, with
|
||||
Contents and Pull requests write access to this repository and an owner trusted
|
||||
to trigger builds. The existing controller PAT can be reused. No GitHub App is
|
||||
required. The built-in Actions `GITHUB_TOKEN` cannot drive the unattended
|
||||
build-and-publish chain, so the tracker requires this secret before it runs.
|
||||
The reviewed sync workflows continue to use `GITHUB_TOKEN` and require
|
||||
build-and-publish chain, so both workflows require this secret before they run.
|
||||
The reviewed upstream sync continues to use `GITHUB_TOKEN` and requires
|
||||
maintainer approval as before. See [setup instructions](docs/upstream-sources.md#enable-unattended-branch-updates).
|
||||
|
||||
Scheduled runs regenerate one shared PR (`auto/sync-upstream`, `auto/sync-rebuilds`)
|
||||
@@ -912,14 +926,27 @@ pending updates. The next scheduled run still picks the same update up in the
|
||||
shared PR if it has not merged by then; identical package trees reuse the same
|
||||
build artifacts.
|
||||
|
||||
Sync PRs are pushed with `GITHUB_TOKEN`, so GitHub holds their build and test
|
||||
Upstream sync PRs are pushed with `GITHUB_TOKEN`, so GitHub holds their build and test
|
||||
runs for approval on every push and starts no `pull_request_target` workflow
|
||||
for them. The sync workflows label their own PRs **`build-approved`**, and
|
||||
their `approve` job releases the held runs for each commit they push, including
|
||||
for them. The upstream sync labels its own PRs **`build-approved`**, and
|
||||
its `approve` job releases the held runs for each commit they push, including
|
||||
the push that opens the PR. A push to an `auto/sync-*` branch does not cancel
|
||||
the PR's in-flight build: the new build waits for it and then reuses its
|
||||
artifacts, so a long aarch64 build is not restarted by every sync.
|
||||
|
||||
Package PRs that are trusted to build also merge themselves.
|
||||
`auto-merge-pr.yml` enables auto-merge (with `PKGS_BOT_TOKEN`, so the merge
|
||||
publishes) on any open, non-draft PR whose author is a collaborator, vouched,
|
||||
or a bot, or that carries **`build-approved`**, and that changes only
|
||||
packages: anything under `pkgbuilds/`, plus the test a package PR brings with
|
||||
it (a new file under `tests/`, and `test.yml` lines that only run new
|
||||
`./tests/*.sh`). The PR lands once `result`, `self-tests` and
|
||||
`build-isolation` pass; a red build stays open. PRs that also touch
|
||||
workflows, scripts, build tooling or existing tests still need a maintainer, as does
|
||||
the upstream sync (`auto/sync-upstream`), which labels itself. Removing
|
||||
`build-approved` withdraws the auto-merge it armed. For a PR opened before
|
||||
the workflow existed, run it by hand: `gh workflow run auto-merge-pr.yml -f pr=<number>`.
|
||||
|
||||
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
|
||||
|
||||
@@ -86,7 +86,7 @@ while [[ $# -gt 0 ]]; do
|
||||
echo " $0 --arch aarch64"
|
||||
echo " $0 --mirror stable"
|
||||
echo " $0 --package yay"
|
||||
echo " $0 --package yay elephant cursor-bin"
|
||||
echo " $0 --package yay walker cursor-bin"
|
||||
echo ""
|
||||
echo "Environment (for CI and resumed builds; defaults keep today's behaviour):"
|
||||
echo " OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output and reuse packages already there"
|
||||
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# Retire packages from one channel of the remote repository.
|
||||
#
|
||||
# unpublish-packages --mirror <edge|rc|stable> --arch <arch> <pkgbase...>
|
||||
#
|
||||
# The counterpart of publish-artifact, with the same steps:
|
||||
# 1. pull the channel's current database from the remote
|
||||
# 2. find every entry built from the named pkgbases (so a package's split
|
||||
# outputs and -debug go with it)
|
||||
# 3. repo-remove them
|
||||
# 4. upload the database
|
||||
#
|
||||
# Package files stay in the bucket. Published filenames are immutable and the
|
||||
# R2 cache cannot recover from a rewrite; an unreferenced file costs nothing
|
||||
# and pacman never sees it. Naming a package the channel does not hold is not
|
||||
# an error, so a re-run is harmless.
|
||||
#
|
||||
# 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:-}
|
||||
BASES=()
|
||||
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,19p' "$0"; exit 0 ;;
|
||||
-*) print_error "Unknown option: $1"; exit 1 ;;
|
||||
*) BASES+=("$1"); shift ;;
|
||||
esac
|
||||
done
|
||||
(( ${#BASES[@]} )) || { print_error "No packages given"; exit 1; }
|
||||
|
||||
DEST="$REMOTE/${PREFIX:+$PREFIX/}$MIRROR/$ARCH"
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
print_header "Unpublish from $DEST"
|
||||
|
||||
# --- 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
|
||||
print_info "No database at $DEST; nothing to remove"
|
||||
exit 0
|
||||
fi
|
||||
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
|
||||
before=$(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$')
|
||||
print_info "Pulled current database ($before entries)"
|
||||
|
||||
# --- 2. entries built from the named pkgbases -----------------------------
|
||||
names=$(bsdtar -xOf "$WORK/repo/omarchy.db.tar.zst" --include '*/desc' |
|
||||
awk -v bases=" ${BASES[*]} " '
|
||||
/^%NAME%$/ { getline name }
|
||||
/^%BASE%$/ { getline base; if (index(bases, " " base " ")) print name }')
|
||||
if [[ -z "$names" ]]; then
|
||||
print_info "None of ${BASES[*]} is in $MIRROR/$ARCH; nothing to remove"
|
||||
exit 0
|
||||
fi
|
||||
mapfile -t NAMES <<<"$names"
|
||||
for n in "${NAMES[@]}"; do print_step "removing $n"; done
|
||||
|
||||
# --- 3. repo-remove ---------------------------------------------------------
|
||||
( cd "$WORK/repo" && repo-remove --quiet omarchy.db.tar.zst "${NAMES[@]}" )
|
||||
ln -sf omarchy.db.tar.zst "$WORK/repo/omarchy.db"
|
||||
ln -sf omarchy.files.tar.zst "$WORK/repo/omarchy.files"
|
||||
after=$(tar -tf "$WORK/repo/omarchy.db.tar.zst" | grep -c '/$')
|
||||
(( before - after == ${#NAMES[@]} )) || {
|
||||
print_error "Expected to remove ${#NAMES[@]} entries, removed $((before - after))"; exit 1; }
|
||||
print_info "Database now has $after entries"
|
||||
|
||||
# --- 4. upload the database -------------------------------------------------
|
||||
rclone copy "$WORK/repo" "$DEST" --s3-no-head --checksum --copy-links --filter '+ omarchy.db*' --filter '+ omarchy.files*' --filter '- *'
|
||||
print_success "Removed ${#NAMES[@]} package(s) from $DEST"
|
||||
+17
-4
@@ -12,9 +12,12 @@ signing on merge exactly as before.
|
||||
- `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
|
||||
Polls for queued jobs with our label, creates one g5-32vcpu-64gb-50gb droplet 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
|
||||
`MAX_AGE_MINUTES`, or still provisioning after `MAX_BOOT_MINUTES`. Builders go in any region DigitalOcean lists the size in
|
||||
stock in (`REGIONS` only sets which to try first); a refused create, logged
|
||||
with DigitalOcean's message, falls back to the next region, then the next
|
||||
of `SIZES`. 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.
|
||||
@@ -31,6 +34,13 @@ 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.
|
||||
|
||||
Each tick pulls the box's checkout first, so a merged `controller.sh` is live
|
||||
within a minute. The unit and timer are copies made at creation; after
|
||||
changing them, on the box:
|
||||
|
||||
cp /opt/omarchy-pkgs/ci/controller-box/omarchy-controller.{service,timer} /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
|
||||
## What the spike proved (2026-09-17, fork ryanrhughes/omarchy-pkgs)
|
||||
|
||||
- `bin/build` works from a bare clone: with no local published tree it
|
||||
@@ -70,8 +80,11 @@ Watch it with `journalctl -u omarchy-controller -f` on the box.
|
||||
packages then signatures then the db.
|
||||
- aarch64 under QEMU with credential-preserving binfmt. PR builds now run
|
||||
aarch64 natively on `ubuntu-24.04-arm` (QEMU was up to ~15x slower). When a
|
||||
merged aarch64 tree has no artifact, publish.yml rebuilds it there too, in its
|
||||
own job, and signs and uploads it on the droplet like a PR artifact.
|
||||
merged tree has no artifact, publish.yml rebuilds it in its own job, aarch64
|
||||
there and x86_64 on a droplet, outside the publish lock.
|
||||
- Publish itself builds nothing: it signs and uploads on `ubuntu-latest` in the
|
||||
tested builder image pulled from GHCR, and only that job holds the `publish`
|
||||
concurrency group, so a merge waits for seconds of signing, not for builds.
|
||||
- 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
|
||||
|
||||
@@ -5,10 +5,14 @@ GITHUB_TOKEN=github_pat_...
|
||||
REPO=omacom/omarchy-pkgs
|
||||
LABEL=omarchy-builder
|
||||
TAG=omarchy-builder
|
||||
REGION=ric1
|
||||
SIZE=g5-32vcpu-64gb-50gb
|
||||
# Builder sizes, tried in order in any region that has them in stock.
|
||||
SIZES="g5-32vcpu-64gb-50gb g5-32vcpu-128gb-50gb"
|
||||
# Optional: regions to try first, e.g. "ric1". Empty means any.
|
||||
REGIONS=
|
||||
MAX_DROPLETS=6
|
||||
MAX_AGE_MINUTES=200
|
||||
# Delete a droplet DigitalOcean still reports as provisioning after this long.
|
||||
MAX_BOOT_MINUTES=10
|
||||
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.
|
||||
|
||||
@@ -7,6 +7,9 @@ Wants=network-online.target
|
||||
Type=oneshot
|
||||
User=controller
|
||||
EnvironmentFile=/etc/omarchy-controller.env
|
||||
# Run the branch's current controller, not the one cloned when the box was
|
||||
# built. As root (the checkout's owner); a failed pull keeps the last one.
|
||||
ExecStartPre=-+/usr/bin/git -C /opt/omarchy-pkgs pull --ff-only --quiet
|
||||
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
|
||||
+88
-18
@@ -4,7 +4,8 @@
|
||||
# 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
|
||||
# that have powered off, exceeded MAX_AGE_MINUTES, or are still provisioning
|
||||
# after MAX_BOOT_MINUTES. The reaper does not
|
||||
# trust its own bookkeeping: it lists by tag and acts on what DigitalOcean
|
||||
# reports.
|
||||
#
|
||||
@@ -21,11 +22,19 @@ 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}
|
||||
# Sizes to try, in order, in any region DigitalOcean lists them in stock. A
|
||||
# size can sell out in a region for hours; the create is then refused with
|
||||
# 422 and the next region, then the next size, is tried. REGIONS only orders
|
||||
# the regions tried first. SIZE and REGION, if set, are one-item lists.
|
||||
SIZES=${SIZES:-${SIZE:-g5-32vcpu-64gb-50gb g5-32vcpu-128gb-50gb}}
|
||||
REGIONS=${REGIONS:-${REGION:-}}
|
||||
IMAGE=${IMAGE:-ubuntu-24-04-x64}
|
||||
MAX_DROPLETS=${MAX_DROPLETS:-4}
|
||||
MAX_AGE_MINUTES=${MAX_AGE_MINUTES:-200}
|
||||
# A droplet DigitalOcean still reports as "new" this long after creation is
|
||||
# stuck provisioning. Left alone it counts as a runner booting, and holds a
|
||||
# queued job until MAX_AGE_MINUTES.
|
||||
MAX_BOOT_MINUTES=${MAX_BOOT_MINUTES:-10}
|
||||
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).
|
||||
@@ -39,7 +48,8 @@ log() { echo "$(date '+%F %T') $*"; }
|
||||
# both, so every decision below is exercised against canned responses.
|
||||
do_api() { # do_api <path> [curl args...]
|
||||
local path=$1; shift
|
||||
curl -fsS -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
# --fail-with-body: a refused create still prints why.
|
||||
curl -sS --fail-with-body -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
-H "Content-Type: application/json" "https://api.digitalocean.com/v2/$path" "$@"
|
||||
}
|
||||
gh_api() { # gh_api <path> [curl args...]
|
||||
@@ -55,7 +65,8 @@ reap() {
|
||||
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
|
||||
if [[ $status == off ]] || (( age > MAX_AGE_MINUTES )) ||
|
||||
{ [[ $status == new ]] && (( age > MAX_BOOT_MINUTES )); }; then
|
||||
log "deleting droplet $id (status=$status age=${age}m)"
|
||||
do_api "droplets/$id" -X DELETE
|
||||
fi
|
||||
@@ -64,18 +75,40 @@ reap() {
|
||||
}
|
||||
|
||||
# --- demand ----------------------------------------------------------------
|
||||
# Emit every item, including later pages of large build matrices. Keep API
|
||||
# failures fatal so a failed query cannot look like an empty queue.
|
||||
gh_items() {
|
||||
local path=$1 key=$2 page=1 response count separator="?"
|
||||
[[ $path != *"?"* ]] || separator="&"
|
||||
while :; do
|
||||
response=$(gh_api "${path}${separator}per_page=100&page=$page") || return 1
|
||||
count=$(jq -er --arg key "$key" '.[$key] | arrays | length' <<< "$response") || return 1
|
||||
jq -c --arg key "$key" '.[$key][]' <<< "$response" || return 1
|
||||
(( count == 100 )) || break
|
||||
((page += 1))
|
||||
done
|
||||
}
|
||||
|
||||
queued_jobs() {
|
||||
local run
|
||||
gh_api "repos/$REPO/actions/runs?status=queued&per_page=50" --get \
|
||||
| jq -r '.workflow_runs[].id' |
|
||||
local status runs run
|
||||
# A workflow can be in progress while most of its matrix is still queued.
|
||||
runs=$(
|
||||
for status in queued in_progress; do
|
||||
gh_items "repos/$REPO/actions/runs?status=$status" workflow_runs || exit 1
|
||||
done
|
||||
) || return 1
|
||||
jq -r '.id' <<< "$runs" | sort -u |
|
||||
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
|
||||
gh_items "repos/$REPO/actions/runs/$run/jobs" jobs |
|
||||
jq -r --arg l "$LABEL" 'select(.status=="queued") | select(.labels | index($l)) | .id' || return 1
|
||||
done | sort -u | wc -l
|
||||
}
|
||||
|
||||
live_droplets() {
|
||||
do_api "droplets?tag_name=$TAG&per_page=200" | jq '[.droplets[] | select(.status != "off")] | length'
|
||||
# Not the ones reap() just deleted: DigitalOcean can list them for a while.
|
||||
do_api "droplets?tag_name=$TAG&per_page=200" | jq --argjson boot "$MAX_BOOT_MINUTES" '
|
||||
[.droplets[] | select(.status != "off")
|
||||
| select(.status != "new" or (now - (.created_at | fromdateiso8601)) / 60 <= $boot)] | length'
|
||||
}
|
||||
|
||||
busy_runners() {
|
||||
@@ -83,22 +116,59 @@ busy_runners() {
|
||||
| jq --arg l "$LABEL" '[.runners[] | select(.busy) | select(any(.labels[]; .name == $l))] | length'
|
||||
}
|
||||
|
||||
# --- capacity --------------------------------------------------------------
|
||||
# "size region" lines to try, best first: SIZES order, then REGIONS order,
|
||||
# then every other region where DigitalOcean lists the size in stock.
|
||||
candidates() {
|
||||
local page=1 response count catalog=""
|
||||
while :; do
|
||||
response=$(do_api "sizes?per_page=200&page=$page") || return 1
|
||||
count=$(jq -er '.sizes | arrays | length' <<< "$response") || return 1
|
||||
catalog+=$(jq -c '.sizes[]' <<< "$response")$'\n'
|
||||
(( count == 200 )) || break
|
||||
((page += 1))
|
||||
done
|
||||
jq -rs --arg sizes "$SIZES" --arg regions "$REGIONS" '
|
||||
($regions | split(" ") | map(select(length > 0))) as $pref
|
||||
| INDEX(.slug) as $by
|
||||
| $sizes | split(" ") | map(select(length > 0)) | .[]
|
||||
| . as $size | $by[$size] // {} | select(.available == true)
|
||||
| .regions as $in
|
||||
| (($pref | map(select(. as $r | $in | index($r)))) + ($in - $pref))[]
|
||||
| "\($size) \(.)"' <<< "$catalog"
|
||||
}
|
||||
|
||||
# --- create ----------------------------------------------------------------
|
||||
# Built once per tick by the first create; a refused pair is dropped from it.
|
||||
CANDIDATES=""
|
||||
create_droplet() {
|
||||
local token userdata name body
|
||||
local token userdata name size region body response
|
||||
[[ -n $CANDIDATES ]] || CANDIDATES=$(candidates) || return 1
|
||||
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)"'
|
||||
while read -r size region; do
|
||||
[[ -n $size ]] || continue
|
||||
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 in $region)"
|
||||
if response=$(do_api droplets -X POST -d "$body"); then
|
||||
jq -r '"created droplet \(.droplet.id)"' <<< "$response"
|
||||
return 0
|
||||
fi
|
||||
log "$size in $region refused: $(jq -r .message <<< "$response" 2>/dev/null || echo "$response")"
|
||||
CANDIDATES=$(grep -Fvx "$size $region" <<< "$CANDIDATES" || true)
|
||||
done <<< "$CANDIDATES"
|
||||
# Every size refused everywhere: the rest of this tick's creates would be too.
|
||||
log "no size in '$SIZES' can be created in any region"
|
||||
return 1
|
||||
}
|
||||
|
||||
controller_tick() {
|
||||
CANDIDATES=""
|
||||
reap
|
||||
local queued live busy available need room
|
||||
queued=$(queued_jobs)
|
||||
|
||||
@@ -45,6 +45,10 @@ write_files:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
# Power off after the one job, and also when the download or the
|
||||
# registration fails: the controller deletes powered-off droplets, but
|
||||
# counts a running one as a runner still booting until MAX_AGE_MINUTES.
|
||||
trap 'sudo poweroff' EXIT
|
||||
cd /home/runner
|
||||
mkdir -p actions-runner && cd actions-runner
|
||||
arch=$(uname -m); [[ $arch == x86_64 ]] && arch=x64
|
||||
@@ -58,8 +62,6 @@ write_files:
|
||||
--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
|
||||
|
||||
@@ -151,7 +151,6 @@ in `origin` and has no effect on release selection.
|
||||
| `1password-beta` | debian | [https://downloads.1password.com/linux/debian/amd64/dists/beta/main/binary-amd64/Packages](https://downloads.1password.com/linux/debian/amd64/dists/beta/main/binary-amd64/Packages) |
|
||||
| `1password-cli` | json | [https://app-updates.agilebits.com/check/1/0/CLI2/en/0](https://app-updates.agilebits.com/check/1/0/CLI2/en/0) |
|
||||
| `aether` | github | [omacom/aether](https://github.com/omacom/aether) |
|
||||
| `asusctl` | git_tags | [https://github.com/OpenGamingCollective/asusctl.git](https://github.com/OpenGamingCollective/asusctl.git) |
|
||||
| `basecamp-cli` | github | [basecamp/basecamp-cli](https://github.com/basecamp/basecamp-cli) |
|
||||
| `bun-bin` | github | [oven-sh/bun](https://github.com/oven-sh/bun) |
|
||||
| `claude-code` | regex | [https://downloads.claude.ai/claude-code-releases/latest](https://downloads.claude.ai/claude-code-releases/latest) |
|
||||
@@ -162,21 +161,6 @@ in `origin` and has no effect on release selection.
|
||||
| `dbxcli-bin` | github | [dropbox/dbxcli](https://github.com/dropbox/dbxcli) |
|
||||
| `dropbox` | redirect | [https://www.dropbox.com/download?plat=lnx.x86_64](https://www.dropbox.com/download?plat=lnx.x86_64) |
|
||||
| `dropbox-cli` | regex | [https://linux.dropbox.com/packages/](https://linux.dropbox.com/packages/) |
|
||||
| `elephant` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-all` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-archlinuxpkgs` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-bluetooth` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-calc` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-clipboard` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-desktopapplications` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-files` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-menus` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-providerlist` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-runner` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-symbols` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-todo` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-unicode` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `elephant-websearch` | github | [abenz1267/elephant](https://github.com/abenz1267/elephant) |
|
||||
| `heroic-games-launcher-bin` | github | [Heroic-Games-Launcher/HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) |
|
||||
| `hyprshade` | pypi | [hyprshade](https://pypi.org/project/hyprshade/) |
|
||||
| `lib32-nvidia-580xx-utils` | regex | [https://download.nvidia.com/XFree86/Linux-x86_64/](https://download.nvidia.com/XFree86/Linux-x86_64/) |
|
||||
@@ -213,7 +197,6 @@ in `origin` and has no effect on release selection.
|
||||
| `vi` | regex | [https://sources.archlinux.org/other/vi/](https://sources.archlinux.org/other/vi/) |
|
||||
| `visual-studio-code-bin` | json | [https://update.code.visualstudio.com/api/update/linux-deb-x64/stable/latest](https://update.code.visualstudio.com/api/update/linux-deb-x64/stable/latest) |
|
||||
| `walker` | github | [abenz1267/walker](https://github.com/abenz1267/walker) |
|
||||
| `xdg-terminal-exec` | git_tags | [https://gitlab.freedesktop.org/Vladimir-csp/xdg-terminal-exec.git](https://gitlab.freedesktop.org/Vladimir-csp/xdg-terminal-exec.git) |
|
||||
| `xpadneo-dkms` | github | [atar-axis/xpadneo](https://github.com/atar-axis/xpadneo) |
|
||||
| `yaru-icon-theme` | git_tags | [https://github.com/ubuntu/yaru.git](https://github.com/ubuntu/yaru.git) |
|
||||
| `yay` | github | [Jguer/yay](https://github.com/Jguer/yay) |
|
||||
@@ -221,12 +204,10 @@ in `origin` and has no effect on release selection.
|
||||
|
||||
## Existing manual holds
|
||||
|
||||
`grok-bot`, `libfprint-git`, `libretro-cap32-git`, `libretro-database-git`, `libretro-fbneo-git`, `libretro-uae-git`, `libretro-vice-git`, `quickshell-git`, `supergfxctl`.
|
||||
`libfprint-git`, `libretro-cap32-git`, `libretro-database-git`, `libretro-fbneo-git`, `libretro-uae-git`, `libretro-vice-git`, `quickshell-git`, `supergfxctl`.
|
||||
|
||||
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.
|
||||
|
||||
`m1n1-aurora` and `uboot-asahi` are deliberate holds: Apple Silicon boot code, pinned by hand like `linux-aurora`, and bumped only after a cold boot on the qualification Macs. `m1n1-aurora` pins an aurora-silicon/m1n1 commit plus a local patch. `uboot-asahi` follows asahi-alarm's recipe and patch set (asahi-alarm/PKGBUILDs), which a tag watch on AsahiLinux/u-boot cannot carry.
|
||||
|
||||
`cua-driver-bin` is a deliberate hold: Omarchy bumps it by hand, so a Cua release ships only when a maintainer has verified it. It keeps its `.omarchy/upstream.sh` hook and `min_release_age`, so lifting the hold means removing `"sync": false`. `cua-hyprland-plugin` declares no upstream source, so no automation updates it either.
|
||||
|
||||
@@ -10,7 +10,7 @@ case "${CARCH}" in
|
||||
;;
|
||||
esac
|
||||
pkgver=8.12.40_27.BETA
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
conflicts=('1password' '1password-beta-bin')
|
||||
pkgdesc="Password manager and secure wallet"
|
||||
arch=('x86_64' 'aarch64')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
pkgname=1password-cli
|
||||
pkgver=2.40.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="1Password command line tool"
|
||||
arch=('x86_64' 'i686' 'arm' 'armv6h' 'aarch64')
|
||||
url="https://app-updates.agilebits.com/product_history/CLI2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pkgname=1password
|
||||
pkgver=8.12.40
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
conflicts=('1password-beta' '1password-beta-bin')
|
||||
pkgdesc="Password manager and secure wallet"
|
||||
arch=('x86_64' 'aarch64')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: Bjarne Øverli <bjarne@oever.li>
|
||||
pkgname=aether
|
||||
pkgver=4.32.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='Desktop theming application - extract colors from wallpapers and apply cohesive themes'
|
||||
arch=('x86_64' 'aarch64')
|
||||
url='https://github.com/omacom/aether'
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[asusctl]
|
||||
source = "git"
|
||||
git = "https://github.com/OpenGamingCollective/asusctl.git"
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"git_tags": "https://github.com/OpenGamingCollective/asusctl.git",
|
||||
"pattern": "(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "asusctl",
|
||||
"commit": "b0ec6ca495eb331a684db91b0fe12085a868b632"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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.
|
||||
@@ -1 +0,0 @@
|
||||
../LICENSE
|
||||
@@ -1,59 +0,0 @@
|
||||
# Maintainer: Mahdi Sarikhani <mahdisarikhani@outlook.com>
|
||||
# Contributor: Fabian Bornschein <fabiscafe@archlinux.org>
|
||||
# Contributor: Static_Rocket
|
||||
|
||||
pkgbase=asusctl
|
||||
pkgname=(asusctl rog-control-center)
|
||||
pkgver=6.5.0
|
||||
pkgrel=1
|
||||
pkgdesc="Daemon and tools to control your ASUS ROG laptop"
|
||||
arch=('x86_64')
|
||||
url="https://asus-linux.org"
|
||||
license=('MPL-2.0')
|
||||
makedepends=('cargo' 'fontconfig')
|
||||
source=("${pkgbase}-${pkgver}.tar.gz::https://github.com/OpenGamingCollective/asusctl/archive/${pkgver}.tar.gz")
|
||||
b2sums=('4179e08a60f9480b62e41d84faade1f46407140a213ca4d60c8699837a2486bda8e66b4ca43fa06149667d02e3d060c3efd792348f9a93a675f762d6ea2d05f8')
|
||||
|
||||
prepare() {
|
||||
cd "${pkgbase}-${pkgver}"
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
cargo fetch --locked --target host-tuple
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${pkgbase}-${pkgver}"
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
export CARGO_TARGET_DIR=target
|
||||
make build
|
||||
}
|
||||
|
||||
package_asusctl() {
|
||||
pkgdesc="An utility for Linux to control many aspects of various ASUS laptops"
|
||||
depends=('glibc' 'libgcc' 'libusb' 'systemd-libs')
|
||||
optdepends=(
|
||||
'acpi_call: fan control'
|
||||
'asusctltray: tray profile switcher'
|
||||
'rog-control-center: graphical user interface for asusctl'
|
||||
'supergfxctl: hybrid GPU control'
|
||||
)
|
||||
install=asusctl.install
|
||||
|
||||
cd "${pkgbase}-${pkgver}"
|
||||
make DESTDIR="${pkgdir}" \
|
||||
install-asusctl \
|
||||
install-asusd \
|
||||
install-asusd_user \
|
||||
install-asus-shutdown \
|
||||
install-data-asusd \
|
||||
install-data-asusd_user
|
||||
}
|
||||
|
||||
package_rog-control-center() {
|
||||
pkgdesc="Graphical user interface for asusctl"
|
||||
depends=('asusctl' 'fontconfig' 'glibc' 'hicolor-icon-theme' 'libgcc' 'systemd-libs')
|
||||
|
||||
cd "${pkgbase}-${pkgver}"
|
||||
make DESTDIR="${pkgdir}" \
|
||||
install-rog_gui \
|
||||
install-data-rog_gui
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
version = 1
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
"PKGBUILD",
|
||||
"README.md",
|
||||
"keys/**",
|
||||
".SRCINFO",
|
||||
".nvchecker.toml",
|
||||
"*.install",
|
||||
"*.sysusers",
|
||||
"*.tmpfiles",
|
||||
"*.logrotate",
|
||||
"*.pam",
|
||||
"*.service",
|
||||
"*.socket",
|
||||
"*.timer",
|
||||
"*.desktop",
|
||||
"*.hook",
|
||||
]
|
||||
SPDX-FileCopyrightText = "Arch Linux contributors"
|
||||
SPDX-License-Identifier = "0BSD"
|
||||
@@ -1,18 +0,0 @@
|
||||
post_install() {
|
||||
printf ":: asusd provides a service that is activated by an udev rule on\n"
|
||||
printf ":: startup. Please reboot the system or run\n"
|
||||
printf ":: # systemctl start asusd.service\n"
|
||||
printf ":: to make it work.\n"
|
||||
printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n"
|
||||
printf ":: for latest required kernel patches/versions\n"
|
||||
}
|
||||
|
||||
post_upgrade() {
|
||||
if systemctl is-active asusd.service --quiet; then
|
||||
printf ":: asusd service will be restarted…\n"
|
||||
systemctl daemon-reload
|
||||
systemctl restart asusd.service
|
||||
fi
|
||||
printf ":: See https://github.com/OpenGamingCollective/asusctl#kernel-requirements\n"
|
||||
printf ":: for latest required kernel patches/versions\n"
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
pkgname=brave-bin
|
||||
pkgver=1.96.61
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
epoch=1
|
||||
pkgdesc='Web browser that blocks ads and trackers by default (binary release)'
|
||||
arch=(x86_64 aarch64)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=brave-origin-bin
|
||||
pkgver=1.96.61
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
epoch=1
|
||||
pkgdesc='The minimalist browser from the makers of Brave (binary release).'
|
||||
arch=(x86_64 aarch64)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
pkgname=claude-code
|
||||
pkgver=2.1.291
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="An agentic coding tool that lives in your terminal"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://github.com/anthropics/claude-code"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
pkgname=claude-desktop
|
||||
pkgver=2.19675.1
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Official Claude desktop app with Claude Code"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://claude.ai"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: bjarneo <https://github.com/bjarneo>
|
||||
pkgname=cliamp
|
||||
pkgver=2.3.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='A retro terminal music player inspired by Winamp 2.x'
|
||||
arch=('x86_64' 'aarch64')
|
||||
url='https://github.com/bjarneo/cliamp'
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
pkgname='crush-bin'
|
||||
pkgver=0.97.1
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='A powerful terminal-based AI assistant for developers, providing intelligent coding assistance directly in your terminal.'
|
||||
url='https://charm.sh/crush'
|
||||
arch=('aarch64' 'armv7h' 'i686' 'x86_64')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=cursor-bin
|
||||
pkgver=3.23.23
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='AI-first coding environment'
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://www.cursor.com"
|
||||
|
||||
@@ -8,7 +8,7 @@ pkgver=2026.10.01.1.e373342
|
||||
# Derive the upstream version (YYYY.MM.DD-<hash>) from that pkgver.
|
||||
_upstream_ver="${pkgver%.*}"
|
||||
_upstream_ver="${_upstream_ver%.*}-${pkgver##*.}"
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
# epoch=1: bumped when switching from the original `20250808.0.<sha>` scheme
|
||||
# to the current `YYYY.MM.DD.<n>.<hash>` scheme (2025-08-09). Never decrease.
|
||||
epoch=1
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
pkgname=disktree-bin
|
||||
_name=disktree
|
||||
pkgver=0.11.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Disk space treemap for Omarchy: see what fills a disk by kind, mark what should go, and remove it"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://github.com/tobi/disktree"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
pkgname=dropbox-cli
|
||||
pkgver=2026.09.28
|
||||
pkgrel=2
|
||||
pkgrel=3
|
||||
pkgdesc="Command line interface for Dropbox"
|
||||
arch=("any")
|
||||
url="https://www.dropbox.com"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-all",
|
||||
"commit": "5614a0a61616643e448fb7c68d58237715ce2739"
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-all
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='elephant + all official elephant providers'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
depends=('libqalculate' 'wl-clipboard' 'imagemagick' 'fd' 'wtype' 'jq' 'sqlite3')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant' 'elephant-playerctl' 'elephant-wireplumber' 'elephant-bitwarden' 'elephant-dnfpackages' 'elephant-1password' 'elephant-bookmarks' 'elephant-nirisessions' 'elephant-niriactions' 'elephant-archlinuxpkgs' 'elephant-bluetooth' 'elephant-calc' 'elephant-clipboard' 'elephant-desktopapplications' 'elephant-files' 'elephant-menus' 'elephant-providerlist' 'elephant-runner' 'elephant-snippets' 'elephant-symbols' 'elephant-todo' 'elephant-unicode' 'elephant-websearch' 'elephant-windows')
|
||||
provides=('elephant' 'elephant-playerctl' 'elephant-wireplumber' 'elephant-nirisessions' 'elephant-niriactions' 'elephant-archlinuxpkgs' 'elephant-bluetooth' 'elephant-calc' 'elephant-clipboard' 'elephant-desktopapplications' 'elephant-files' 'elephant-menus' 'elephant-providerlist' 'elephant-runner' 'elephant-snippets' 'elephant-symbols' 'elephant-todo' 'elephant-unicode' 'elephant-websearch' 'elephant-windows')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
# Build main elephant binary
|
||||
cd elephant-${pkgver}/cmd/elephant
|
||||
go build -ldflags="-s -w" -buildvcs=false -x -o elephant -trimpath
|
||||
|
||||
# Build all provider plugins
|
||||
cd ../../internal/providers
|
||||
|
||||
# Build each provider
|
||||
for provider in playerctl wireplumber archlinuxpkgs bitwarden dnfpackages 1password bookmarks bluetooth nirisessions niriactions calc clipboard desktopapplications files menus providerlist runner snippets symbols todo unicode websearch windows; do
|
||||
cd $provider
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
cd ..
|
||||
done
|
||||
}
|
||||
|
||||
package() {
|
||||
# Install main elephant binary
|
||||
cd elephant-${pkgver}/cmd/elephant
|
||||
install -Dm 755 elephant -t "${pkgdir}/usr/bin"
|
||||
|
||||
# Install all provider plugins
|
||||
cd ../../internal/providers
|
||||
for provider in playerctl wireplumber archlinuxpkgs bitwarden dnfpackages bookmarks 1password nirisessions niriactions bluetooth calc clipboard desktopapplications files menus providerlist runner snippets symbols todo unicode websearch windows; do
|
||||
install -Dm 755 $provider/$provider.so -t "${pkgdir}/usr/lib/elephant"
|
||||
done
|
||||
|
||||
# Install license
|
||||
cd ../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-archlinuxpkgs",
|
||||
"commit": "659b81f1aa74a13fd2ebec222e19da2046d8e977"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-archlinuxpkgs
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='archlinuxpkgs provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-archlinuxpkgs')
|
||||
provides=('elephant-archlinuxpkgs')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/archlinuxpkgs
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/archlinuxpkgs
|
||||
install -Dm 755 archlinuxpkgs.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-bluetooth",
|
||||
"commit": "42d9dd5424884c51b8fe6bf7692caf0a31007f05"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-bluetooth
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='bluetooth provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-bluetooth')
|
||||
provides=('elephant-bluetooth')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/bluetooth
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/bluetooth
|
||||
install -Dm 755 bluetooth.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-calc",
|
||||
"commit": "84aba9e90bbd65af45f83168cd6c7bce6ec4322e"
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-calc
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='calc provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
depends=('libqalculate')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-calc')
|
||||
provides=('elephant-calc')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/calc
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/calc
|
||||
install -Dm 755 calc.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-clipboard",
|
||||
"commit": "3176f9de1445e7115009383f9cdac116729fc7be"
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-clipboard
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='clipboard provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
depends=('wl-clipboard' 'imagemagick')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-clipboard')
|
||||
provides=('elephant-clipboard')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/clipboard
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/clipboard
|
||||
install -Dm 755 clipboard.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-desktopapplications",
|
||||
"commit": "c30e33db5fef912dec9aa157773b10ffab1e0307"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-desktopapplications
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='desktopapplications provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-desktopapplications')
|
||||
provides=('elephant-desktopapplications')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/desktopapplications
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/desktopapplications
|
||||
install -Dm 755 desktopapplications.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-files",
|
||||
"commit": "2d16502b7a905e7d7a03e0026c5519c3b3c4abf2"
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-files
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='files provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
depends=('fd')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-files')
|
||||
provides=('elephant-files')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/files
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/files
|
||||
install -Dm 755 files.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-menus",
|
||||
"commit": "5ab583fee6ba3e387d49ffe4e409bb84bc60ea24"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-menus
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='menus provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-menus')
|
||||
provides=('elephant-menus')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/menus
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/menus
|
||||
install -Dm 755 menus.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-providerlist",
|
||||
"commit": "1d756a138e926a81b9d33265f0197b8661168845"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-providerlist
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='providerlist provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-providerlist')
|
||||
provides=('elephant-providerlist')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/providerlist
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/providerlist
|
||||
install -Dm 755 providerlist.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-runner",
|
||||
"commit": "e47641530d912199372204cf8780ef37f99f0b37"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-runner
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='runner provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-runner')
|
||||
provides=('elephant-runner')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/runner
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/runner
|
||||
install -Dm 755 runner.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-symbols",
|
||||
"commit": "32263e8b71390ab38b8aa1fd82fc2595b41a5157"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-symbols
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='symbols provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-symbols')
|
||||
provides=('elephant-symbols')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/symbols
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/symbols
|
||||
install -Dm 755 symbols.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-todo",
|
||||
"commit": "447978df5ea3afd1e10959d7973c0b35367724c3"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-todo
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='todo provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-todo')
|
||||
provides=('elephant-todo')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/todo
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/todo
|
||||
install -Dm 755 todo.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-unicode",
|
||||
"commit": "23222b0d304a234c74f630c5b9176d58c6c6e0b3"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-unicode
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='unicode provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-unicode')
|
||||
provides=('elephant-unicode')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/unicode
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/unicode
|
||||
install -Dm 755 unicode.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant-websearch",
|
||||
"commit": "6b5da831da33e3533593823826a8ffb1a008a299"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant-websearch
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='websearch provider for elephant'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant-websearch')
|
||||
provides=('elephant-websearch')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd elephant-${pkgver}/internal/providers/websearch
|
||||
go build -ldflags="-s -w" -buildvcs=false -buildmode=plugin -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd elephant-${pkgver}/internal/providers/websearch
|
||||
install -Dm 755 websearch.so -t "${pkgdir}/usr/lib/elephant"
|
||||
|
||||
cd ../../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "abenz1267/elephant",
|
||||
"pattern": "v(?P<version>[0-9]+(?:\\.[0-9]+)*)"
|
||||
}
|
||||
},
|
||||
"origin": {
|
||||
"aur": "elephant",
|
||||
"commit": "6dd02e6987a7a91be6a33e9600147523efa7fa5a"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Maintainer: Andrej Benz <hello[at]benz[dot]dev>
|
||||
|
||||
pkgname=elephant
|
||||
pkgver=2.22.1
|
||||
pkgrel=1
|
||||
pkgdesc='general purpose datasource and executor'
|
||||
url='https://github.com/abenz1267/elephant'
|
||||
arch=('x86_64' 'aarch64')
|
||||
license=('GPL')
|
||||
makedepends=('go')
|
||||
conflicts=('elephant')
|
||||
provides=('elephant')
|
||||
source=("${url}/archive/refs/tags/v${pkgver}.tar.gz")
|
||||
sha256sums=('3d1d0d4c55ae531fa3f06406b96504b5165a0d7b53523d1f8351d9d93e457f44')
|
||||
|
||||
build() {
|
||||
cd ${pkgname}-${pkgver}/cmd/elephant
|
||||
go build -ldflags="-s -w" -buildvcs=false -x -o elephant -trimpath
|
||||
}
|
||||
|
||||
package() {
|
||||
cd ${pkgname}-${pkgver}/cmd/elephant
|
||||
install -Dm 755 elephant -t "${pkgdir}/usr/bin"
|
||||
|
||||
cd ../../
|
||||
install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=flea
|
||||
pkgver=0.3.7
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='Fast, keyboard-first file manager for Omarchy'
|
||||
arch=('x86_64' 'aarch64')
|
||||
url='https://github.com/thisisgm/flea'
|
||||
|
||||
@@ -7,7 +7,7 @@ pkgname=github-copilot-cli
|
||||
_pkgexec=copilot
|
||||
|
||||
pkgver=1.0.92
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
|
||||
pkgdesc="GitHub Copilot CLI brings the power of Copilot coding agent directly to your terminal."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
pkgname=google-chrome
|
||||
pkgver=155.0.8059.39
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="The popular web browser by Google (Stable Channel)"
|
||||
arch=(
|
||||
'x86_64'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"source": "local",
|
||||
"sync": false,
|
||||
"release_ring": "fast",
|
||||
"origin": {
|
||||
"aur": "grok-bot",
|
||||
"commit": "05eb78fca06b482affda28b26223cbf249d4bbcd"
|
||||
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# Cursor publishes Grok Bot from its own Debian repository, one index per
|
||||
# architecture. Each index carries the version and the SHA256, so an update
|
||||
# is two small HTTP requests instead of downloading the debs, and the pool
|
||||
# URL is keyed by version. bin/sync-upstream can rewrite pkgver and the
|
||||
# checksums; it cannot rewrite a commit id embedded in the old
|
||||
# downloads.cursor.com/grokbot/stable/<commit>/ URL.
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="https://downloads.cursor.com/aptrepo"
|
||||
declare -A DEB_ARCHES=([x86_64]=amd64 [aarch64]=arm64)
|
||||
|
||||
# Print "<version> <sha256>" for the newest grok-bot stanza. Newest is
|
||||
# vercmp's opinion, which is the one bin/sync-upstream and pacman both use;
|
||||
# sort -V disagrees with it over versions like 1.0a. The winner's Filename
|
||||
# must be the versioned pool path the PKGBUILD downloads from.
|
||||
newest_release() {
|
||||
local index="$1" debarch="$2"
|
||||
local version sha256 filename best_version="" best_sha256="" best_filename=""
|
||||
|
||||
while read -r version sha256 filename; do
|
||||
[[ -n "$version" && -n "$sha256" ]] || continue
|
||||
if [[ -z "$best_version" ]] || [[ "$(vercmp "$version" "$best_version")" -gt 0 ]]; then
|
||||
best_version="$version"
|
||||
best_sha256="$sha256"
|
||||
best_filename="$filename"
|
||||
fi
|
||||
done < <(awk '
|
||||
{ sub(/\r$/, "") }
|
||||
/^Package:/ { package = $2 }
|
||||
/^Version:/ { version = $2 }
|
||||
/^SHA256:/ { sha256 = $2 }
|
||||
/^Filename:/ { filename = $2 }
|
||||
/^$/ {
|
||||
if (package == "grok-bot" && version && sha256) print version, sha256, filename
|
||||
package = version = sha256 = filename = ""
|
||||
}
|
||||
END {
|
||||
if (package == "grok-bot" && version && sha256) print version, sha256, filename
|
||||
}
|
||||
' <<<"$index")
|
||||
|
||||
[[ -n "$best_version" ]] || return 1
|
||||
|
||||
local expected="pool/grok-bot/g/gr/grok-bot_${best_version}_${debarch}.deb"
|
||||
if [[ "$best_filename" != "$expected" ]]; then
|
||||
echo "Unexpected Grok Bot $best_version $debarch Filename: '${best_filename}' (expected $expected)" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$best_version $best_sha256"
|
||||
}
|
||||
|
||||
versions=()
|
||||
declare -A checksums=()
|
||||
|
||||
for arch in "${!DEB_ARCHES[@]}"; do
|
||||
index=$(curl -fsSL "$BASE_URL/dists/grok-bot/main/binary-${DEB_ARCHES[$arch]}/Packages")
|
||||
|
||||
read -r version sha256 <<<"$(newest_release "$index" "${DEB_ARCHES[$arch]}")"
|
||||
if [[ -z "${version:-}" || -z "${sha256:-}" ]]; then
|
||||
echo "No usable Grok Bot release found for $arch" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
versions+=("$version")
|
||||
checksums[$arch]="$sha256"
|
||||
done
|
||||
|
||||
# A release can land one architecture at a time. Wait until both agree so one
|
||||
# pkgver always describes both artifacts.
|
||||
for version in "${versions[@]}"; do
|
||||
if [[ "$version" != "${versions[0]}" ]]; then
|
||||
echo "Upstream architectures are mid-release (${versions[*]}); skipping" >&2
|
||||
echo '{}'
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
jq -n \
|
||||
--arg pkgver "${versions[0]}" \
|
||||
--arg x86_64 "${checksums[x86_64]}" \
|
||||
--arg aarch64 "${checksums[aarch64]}" \
|
||||
'{pkgver: $pkgver, sha256sums: {x86_64: [$x86_64], aarch64: [$aarch64]}}'
|
||||
@@ -2,9 +2,8 @@
|
||||
# Contributor: Omarchy
|
||||
|
||||
pkgname=grok-bot
|
||||
pkgver=0.47.0
|
||||
pkgver=0.68.1
|
||||
pkgrel=1
|
||||
_commit=c1e7d7a46549956d25f53e9c0b9f59666e03aa3a
|
||||
pkgdesc='Grok Bot desktop agent'
|
||||
arch=('x86_64' 'aarch64')
|
||||
url='https://x.ai/bot'
|
||||
@@ -30,20 +29,23 @@ install=grok-bot.install
|
||||
|
||||
_deb_x86_64="grok-bot_${pkgver}_amd64.deb"
|
||||
_deb_aarch64="grok-bot_${pkgver}_arm64.deb"
|
||||
# Versioned pool paths — Packages indexes carry SHA256 so upstream.sh
|
||||
# can bump pkgver without embedding a Cursor commit id.
|
||||
_pool="https://downloads.cursor.com/aptrepo/pool/grok-bot/g/gr"
|
||||
source=(
|
||||
'grok-bot.sh'
|
||||
'grok-bot.desktop'
|
||||
)
|
||||
source_x86_64=(
|
||||
"${_deb_x86_64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/x64/${_deb_x86_64}"
|
||||
"${_deb_x86_64}::${_pool}/${_deb_x86_64}"
|
||||
)
|
||||
source_aarch64=(
|
||||
"${_deb_aarch64}::https://downloads.cursor.com/grokbot/stable/${_commit}/linux/arm64/${_deb_aarch64}"
|
||||
"${_deb_aarch64}::${_pool}/${_deb_aarch64}"
|
||||
)
|
||||
sha256sums=('6dfa6c305941afa6cbaefbeaae06d05ab5a88f31630005d25a819a160c20c7a3'
|
||||
'3e2a2461ea58d17ac1777616be9ba660f7cb9ceefa9292016e36c55758bf78dd')
|
||||
sha256sums_x86_64=('11ca0f51a535b97af51a352adf9c0f9ecd2e1b0430a69ae9451b688a7a065808')
|
||||
sha256sums_aarch64=('836f8d19d3826c6573c31ac45c7a9b797abc73381ae0d2b1e7a8dae5410e7e46')
|
||||
sha256sums_x86_64=('b2be8106d2b3eae07d983d5f1ca77b657accde666dc440db2a409421ecff3359')
|
||||
sha256sums_aarch64=('3f85fbe2ba3c1d122aa16f8be672076bc19146e07e9d431f37a93b85fa75a93f')
|
||||
noextract=("${_deb_x86_64}" "${_deb_aarch64}")
|
||||
|
||||
package() {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Resolve current Grok Bot stable from Cursor's update feed and pin PKGBUILD.
|
||||
# Linux has no latest alias (linux-x64 feed returns 204). The darwin-arm64
|
||||
# sand feed publishes version + commit; the Linux .deb lives at the same commit.
|
||||
# Darwin can ship first — HEAD-check the Linux URL and fail loudly if 404.
|
||||
set -euo pipefail
|
||||
|
||||
PKGBUILD_PATH="${1:-PKGBUILD}"
|
||||
[[ -f "${PKGBUILD_PATH}" ]] || { echo "Error: PKGBUILD not found at '${PKGBUILD_PATH}'" >&2; exit 1; }
|
||||
|
||||
FEED='https://api2.cursor.sh/updates/api/update/darwin-arm64/sand/0.0.0/00000000-0000-0000-0000-000000000000/stable'
|
||||
json="$(curl -fsSL -H 'cache-control: no-cache' "${FEED}")"
|
||||
|
||||
ver="$(jq -er '.name // .version' <<<"${json}")"
|
||||
feed_url="$(jq -er '.url' <<<"${json}")"
|
||||
commit="$(sed -nE 's@.*/(grokbot|sand)/stable/([0-9a-f]{40})/.*@\2@p' <<<"${feed_url}")"
|
||||
|
||||
[[ -n "${ver}" && -n "${commit}" ]] || {
|
||||
echo "Error: could not parse version/commit from feed: ${json}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
deb_url="https://downloads.cursor.com/grokbot/stable/${commit}/linux/x64/Grok_Bot_${ver}.deb"
|
||||
code="$(curl -fsSIL -o /dev/null -w '%{http_code}' "${deb_url}")"
|
||||
[[ "${code}" == "200" ]] || {
|
||||
echo "Error: Linux deb not fetchable (${code}): ${deb_url}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
tmp="$(mktemp)"
|
||||
trap 'rm -f "${tmp}"' EXIT
|
||||
curl -fL --retry 3 -o "${tmp}" "${deb_url}"
|
||||
sum="$(sha256sum "${tmp}" | awk '{print $1}')"
|
||||
|
||||
current_ver="$(sed -nE 's/^pkgver=([^[:space:]#]+).*/\1/p' "${PKGBUILD_PATH}" | head -n1)"
|
||||
|
||||
sed -i -E \
|
||||
-e "s/^_commit=.*/_commit=${commit}/" \
|
||||
-e "s/^pkgver=.*/pkgver=${ver}/" \
|
||||
-e "0,/^[[:space:]]*'[0-9a-f]{64}'/s// '${sum}'/" \
|
||||
"${PKGBUILD_PATH}"
|
||||
|
||||
if [[ "${ver}" != "${current_ver}" ]]; then
|
||||
sed -i -E 's/^pkgrel=.*/pkgrel=1/' "${PKGBUILD_PATH}"
|
||||
fi
|
||||
|
||||
echo "${ver} ${commit}"
|
||||
echo "${deb_url}"
|
||||
echo "${sum}"
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
pkgname=hermes-desktop
|
||||
pkgver=2026.9.7
|
||||
pkgrel=3
|
||||
pkgrel=4
|
||||
pkgdesc='Native desktop shell for Hermes Agent'
|
||||
arch=('x86_64')
|
||||
url='https://github.com/NousResearch/hermes-agent'
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"release_ring": "fast",
|
||||
"upstream": {
|
||||
"git_tags": "https://github.com/hyprwm/hyprland-guiutils.git",
|
||||
"tag_pattern": "v{pkgver}",
|
||||
"sources": {
|
||||
"any": [
|
||||
"https://github.com/hyprwm/hyprland-guiutils/archive/v{pkgver}/hyprland-guiutils-{pkgver}.tar.gz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"rebuild_on": [
|
||||
"aquamarine"
|
||||
],
|
||||
"rebuilt_against": {
|
||||
"aarch64": {
|
||||
"aquamarine": "0.15.0-2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
# Maintainer: Caleb Maclennan <caleb@alerque.com>
|
||||
|
||||
pkgname=hyprland-guiutils
|
||||
pkgver=0.2.2
|
||||
pkgrel=3
|
||||
pkgdesc='Hyprland GUI utilities'
|
||||
arch=(aarch64)
|
||||
url="https://github.com/hyprwm/$pkgname"
|
||||
license=(BSD-3-Clause)
|
||||
depends=(
|
||||
libgcc
|
||||
libstdc++
|
||||
glibc # libc.so libm.so
|
||||
hyprlang
|
||||
hyprtoolkit libhyprtoolkit.so
|
||||
hyprutils libhyprutils.so
|
||||
libdrm
|
||||
pixman
|
||||
)
|
||||
makedepends=(cmake)
|
||||
replaces=(hyprland-qtutils)
|
||||
_archive="$pkgname-$pkgver"
|
||||
source=("$url/archive/v$pkgver/$_archive.tar.gz")
|
||||
sha256sums=('16f92a6c5a22ac58e1fc313f6b202c188da45e804e1f21ff57dfd0da5c1a01b7')
|
||||
|
||||
build() {
|
||||
cd "$_archive"
|
||||
local cmake_flags=(
|
||||
-D CMAKE_BUILD_TYPE=Release
|
||||
-D CMAKE_INSTALL_PREFIX=/usr
|
||||
)
|
||||
cmake -B build ${cmake_flags[@]}
|
||||
cmake --build build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$_archive"
|
||||
DESTDIR="$pkgdir" cmake --install build
|
||||
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname" LICENSE
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname="hyprland-preview-share-picker"
|
||||
pkgver=0.2.1
|
||||
pkgrel=2
|
||||
pkgrel=3
|
||||
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"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"release_ring": "fast",
|
||||
"upstream": {
|
||||
"git_tags": "https://github.com/hyprwm/hyprtoolkit.git",
|
||||
"tag_pattern": "v{pkgver}",
|
||||
"sources": {
|
||||
"any": [
|
||||
"https://github.com/hyprwm/hyprtoolkit/archive/v{pkgver}/hyprtoolkit-{pkgver}.tar.gz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"rebuild_on": [
|
||||
"aquamarine"
|
||||
],
|
||||
"rebuilt_against": {
|
||||
"aarch64": {
|
||||
"aquamarine": "0.15.0-2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
# Maintainer: Caleb Maclennan <caleb@alerque.com>
|
||||
|
||||
pkgname=hyprtoolkit
|
||||
pkgver=0.6.0
|
||||
pkgrel=1
|
||||
pkgdesc='A modern C++ Wayland-native GUI toolkit'
|
||||
arch=(aarch64)
|
||||
url="https://github.com/hyprwm/$pkgname"
|
||||
license=(BSD-3-Clause)
|
||||
depends=(
|
||||
libgcc
|
||||
libstdc++
|
||||
aquamarine libaquamarine.so
|
||||
cairo libcairo.so
|
||||
glib2 libglib-2.0.so
|
||||
glibc # libc.so libm.so
|
||||
hyprgraphics libhyprgraphics.so
|
||||
hyprlang libhyprlang.so
|
||||
hyprutils libhyprutils.so
|
||||
iniparser libiniparser.so
|
||||
libdrm # libdrm.so
|
||||
libglvnd libEGL.so libOpenGL.so
|
||||
libxkbcommon libxkbcommon.so
|
||||
mesa # libgbm.so
|
||||
pango libpango-1.0.so # libpango.so
|
||||
pixman libpixman-1.so
|
||||
wayland libwayland-client.so
|
||||
)
|
||||
makedepends=(cmake
|
||||
hyprwayland-scanner)
|
||||
provides=(libhyprtoolkit.so)
|
||||
_archive="$pkgname-$pkgver"
|
||||
source=("$url/archive/v$pkgver/$_archive.tar.gz")
|
||||
sha256sums=('53c41be72af97d9ede274a63c9c1034c58726d862905763ed6e5a564ae42ba6b')
|
||||
|
||||
build() {
|
||||
cd "$_archive"
|
||||
local cmake_options=(
|
||||
-D CMAKE_BUILD_TYPE=None
|
||||
-D CMAKE_INSTALL_PREFIX=/usr
|
||||
)
|
||||
cmake -B build -W no-dev ${cmake_options[@]}
|
||||
cmake --build build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$_archive"
|
||||
DESTDIR="$pkgdir" cmake --install build
|
||||
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=learn-omarchy
|
||||
pkgver=0.2.5
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Interactive, theme-aware courses for learning Omarchy"
|
||||
arch=('any')
|
||||
url="https://github.com/DanWahlin/learn-omarchy"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
pkgname=libfprint-git
|
||||
pkgver=1.94.100.r10.g6f9479c
|
||||
pkgrel=2
|
||||
pkgrel=3
|
||||
# Retain the epoch used to supersede the previously published untested build.
|
||||
epoch=1
|
||||
pkgdesc="Library for fingerprint readers (pinned upstream snapshot with FocalTech and Synaptics 06cb:010b support, and Apple Touch ID on aarch64)"
|
||||
|
||||
@@ -3,7 +3,7 @@ _pkgname="limine-entry-tool"
|
||||
pkgname="limine-mkinitcpio-hook"
|
||||
_gradle_version=9.7.1
|
||||
pkgver=1.40.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Install kernels for the Limine bootloader."
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://gitlab.com/Zesko/limine-entry-tool"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: Zesko
|
||||
pkgname="limine-snapper-sync"
|
||||
pkgver=1.32.1
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
_gradle_version=9.7.1
|
||||
pkgdesc="Integrates Limine boot entries with Snapper snapshots."
|
||||
arch=('x86_64' 'aarch64')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pkgname=linux-firmware-cirrus-dx13260
|
||||
pkgver=20260810
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Cirrus speaker firmware aliases for Dell XPS 13 DX13260 Panther Lake"
|
||||
arch=('x86_64')
|
||||
url="https://gitlab.com/kernel-firmware/linux-firmware"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"source": "local",
|
||||
"release_ring": "fast",
|
||||
"sync": false
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
# Maintainer: Spencer Bull <spencerbull2554@gmail.com>
|
||||
#
|
||||
# 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) <heftig@archlinux.org>, 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
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
-----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-----
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"source": "local",
|
||||
"skip_build": true
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
diff --git a/kernel/fork.c b/kernel/fork.c
|
||||
--- a/kernel/fork.c
|
||||
+++ b/kernel/fork.c
|
||||
@@ -127,6 +127,12 @@
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
+#ifdef CONFIG_USER_NS
|
||||
+static int unprivileged_userns_clone = 1;
|
||||
+#else
|
||||
+#define unprivileged_userns_clone 1
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Minimum number of threads to boot the kernel
|
||||
*/
|
||||
@@ -2092,6 +2098,11 @@ __latent_entropy struct task_struct *copy_process(
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
||||
+ if ((clone_flags & CLONE_NEWUSER) && !unprivileged_userns_clone) {
|
||||
+ if (!capable(CAP_SYS_ADMIN))
|
||||
+ return ERR_PTR(-EPERM);
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Force any signals received before this point to be delivered
|
||||
* before the fork happens. Collect up signals sent to multiple
|
||||
@@ -3165,6 +3176,10 @@ static int check_unshare_flags(unsigned long unshare_flags)
|
||||
if (!current_is_single_threaded())
|
||||
return -EINVAL;
|
||||
}
|
||||
+ if ((unshare_flags & CLONE_NEWUSER) && !unprivileged_userns_clone) {
|
||||
+ if (!capable(CAP_SYS_ADMIN))
|
||||
+ return -EPERM;
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3400,6 +3415,15 @@ static const struct ctl_table fork_sysctl_table[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = sysctl_max_threads,
|
||||
},
|
||||
+#ifdef CONFIG_USER_NS
|
||||
+ {
|
||||
+ .procname = "unprivileged_userns_clone",
|
||||
+ .data = &unprivileged_userns_clone,
|
||||
+ .maxlen = sizeof(int),
|
||||
+ .mode = 0644,
|
||||
+ .proc_handler = proc_dointvec,
|
||||
+ },
|
||||
+#endif
|
||||
};
|
||||
|
||||
static int __init init_fork_sysctl(void)
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -0,0 +1,582 @@
|
||||
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
|
||||
--- a/arch/x86/include/asm/pgtable.h
|
||||
+++ b/arch/x86/include/asm/pgtable.h
|
||||
@@ -50,7 +50,7 @@ void ptdump_walk_user_pgd_level_checkwx(void);
|
||||
extern spinlock_t pgd_lock;
|
||||
extern struct list_head pgd_list;
|
||||
|
||||
-extern struct mm_struct *pgd_page_get_mm(struct page *page);
|
||||
+struct mm_struct *pgd_page_get_mm(struct ptdesc *pt);
|
||||
|
||||
extern pmdval_t early_pmd_flags;
|
||||
|
||||
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
|
||||
--- a/arch/x86/include/asm/pgtable_types.h
|
||||
+++ b/arch/x86/include/asm/pgtable_types.h
|
||||
@@ -512,7 +512,7 @@ static inline pgprot_t pgprot_large_2_4k(pgprot_t pgprot)
|
||||
return __pgprot(protval_large_2_4k(pgprot_val(pgprot)));
|
||||
}
|
||||
|
||||
-
|
||||
+struct ptdesc;
|
||||
typedef struct page *pgtable_t;
|
||||
|
||||
extern pteval_t __supported_pte_mask;
|
||||
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
|
||||
--- a/arch/x86/include/asm/tlbflush.h
|
||||
+++ b/arch/x86/include/asm/tlbflush.h
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
+#include <linux/minmax.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/barrier.h>
|
||||
@@ -211,6 +212,12 @@ extern u16 invlpgb_count_max;
|
||||
|
||||
extern void initialize_tlbstate_and_flush(void);
|
||||
|
||||
+/*
|
||||
+ * Keep stack-allocated flush_tlb_info cacheline aligned, but cap the
|
||||
+ * alignment to avoid excessive stack usage on large-cacheline systems.
|
||||
+ */
|
||||
+#define FLUSH_TLB_INFO_ALIGN MIN(SMP_CACHE_BYTES, 64)
|
||||
+
|
||||
/*
|
||||
* TLB flushing:
|
||||
*
|
||||
@@ -249,7 +256,7 @@ struct flush_tlb_info {
|
||||
u8 stride_shift;
|
||||
u8 freed_tables;
|
||||
u8 trim_cpumask;
|
||||
-};
|
||||
+} __aligned(FLUSH_TLB_INFO_ALIGN);
|
||||
|
||||
void flush_tlb_local(void);
|
||||
void flush_tlb_one_user(unsigned long addr);
|
||||
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
|
||||
--- a/arch/x86/kernel/kvm.c
|
||||
+++ b/arch/x86/kernel/kvm.c
|
||||
@@ -663,8 +663,10 @@ static void kvm_flush_tlb_multi(const struct cpumask *cpumask,
|
||||
u8 state;
|
||||
int cpu;
|
||||
struct kvm_steal_time *src;
|
||||
- struct cpumask *flushmask = this_cpu_cpumask_var_ptr(__pv_cpu_mask);
|
||||
+ struct cpumask *flushmask;
|
||||
|
||||
+ guard(preempt)();
|
||||
+ flushmask = this_cpu_cpumask_var_ptr(__pv_cpu_mask);
|
||||
cpumask_copy(flushmask, cpumask);
|
||||
/*
|
||||
* We have to call flush only on online vCPUs. And
|
||||
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
|
||||
--- a/arch/x86/mm/fault.c
|
||||
+++ b/arch/x86/mm/fault.c
|
||||
@@ -275,17 +275,17 @@ void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
|
||||
for (addr = start & PMD_MASK;
|
||||
addr >= TASK_SIZE_MAX && addr < VMALLOC_END;
|
||||
addr += PMD_SIZE) {
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
spin_lock(&pgd_lock);
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
spinlock_t *pgt_lock;
|
||||
|
||||
/* the pgt_lock only for Xen */
|
||||
- pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
|
||||
+ pgt_lock = &pgd_page_get_mm(ptdesc)->page_table_lock;
|
||||
|
||||
spin_lock(pgt_lock);
|
||||
- vmalloc_sync_one(page_address(page), addr);
|
||||
+ vmalloc_sync_one(ptdesc_address(ptdesc), addr);
|
||||
spin_unlock(pgt_lock);
|
||||
}
|
||||
spin_unlock(&pgd_lock);
|
||||
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
|
||||
--- a/arch/x86/mm/init_64.c
|
||||
+++ b/arch/x86/mm/init_64.c
|
||||
@@ -136,7 +136,7 @@ static void sync_global_pgds_l5(unsigned long start, unsigned long end)
|
||||
|
||||
for (addr = start; addr <= end; addr = ALIGN(addr + 1, PGDIR_SIZE)) {
|
||||
const pgd_t *pgd_ref = pgd_offset_k(addr);
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
/* Check for overflow */
|
||||
if (addr < start)
|
||||
@@ -146,13 +146,13 @@ static void sync_global_pgds_l5(unsigned long start, unsigned long end)
|
||||
continue;
|
||||
|
||||
spin_lock(&pgd_lock);
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
pgd_t *pgd;
|
||||
spinlock_t *pgt_lock;
|
||||
|
||||
- pgd = (pgd_t *)page_address(page) + pgd_index(addr);
|
||||
+ pgd = (pgd_t *)ptdesc_address(ptdesc) + pgd_index(addr);
|
||||
/* the pgt_lock only for Xen */
|
||||
- pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
|
||||
+ pgt_lock = &pgd_page_get_mm(ptdesc)->page_table_lock;
|
||||
spin_lock(pgt_lock);
|
||||
|
||||
if (!pgd_none(*pgd_ref) && !pgd_none(*pgd))
|
||||
@@ -174,7 +174,7 @@ static void sync_global_pgds_l4(unsigned long start, unsigned long end)
|
||||
for (addr = start; addr <= end; addr = ALIGN(addr + 1, PGDIR_SIZE)) {
|
||||
pgd_t *pgd_ref = pgd_offset_k(addr);
|
||||
const p4d_t *p4d_ref;
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
/*
|
||||
* With folded p4d, pgd_none() is always false, we need to
|
||||
@@ -187,15 +187,15 @@ static void sync_global_pgds_l4(unsigned long start, unsigned long end)
|
||||
continue;
|
||||
|
||||
spin_lock(&pgd_lock);
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
spinlock_t *pgt_lock;
|
||||
|
||||
- pgd = (pgd_t *)page_address(page) + pgd_index(addr);
|
||||
+ pgd = (pgd_t *)ptdesc_address(ptdesc) + pgd_index(addr);
|
||||
p4d = p4d_offset(pgd, addr);
|
||||
/* the pgt_lock only for Xen */
|
||||
- pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
|
||||
+ pgt_lock = &pgd_page_get_mm(ptdesc)->page_table_lock;
|
||||
spin_lock(pgt_lock);
|
||||
|
||||
if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
|
||||
diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
|
||||
--- a/arch/x86/mm/pat/set_memory.c
|
||||
+++ b/arch/x86/mm/pat/set_memory.c
|
||||
@@ -88,9 +88,8 @@ static unsigned long direct_pages_count[PG_LEVEL_NUM];
|
||||
void update_page_count(int level, unsigned long pages)
|
||||
{
|
||||
/* Protect against CPA */
|
||||
- spin_lock(&pgd_lock);
|
||||
+ guard(spinlock)(&pgd_lock);
|
||||
direct_pages_count[level] += pages;
|
||||
- spin_unlock(&pgd_lock);
|
||||
}
|
||||
|
||||
static void split_page_count(int level)
|
||||
@@ -420,7 +419,7 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa)
|
||||
int collapsed = 0;
|
||||
int i;
|
||||
|
||||
- spin_lock(&cpa_lock);
|
||||
+ guard(spinlock)(&cpa_lock);
|
||||
|
||||
if (cpa->flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) {
|
||||
for (i = 0; i < cpa->numpages; i++)
|
||||
@@ -435,10 +434,8 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa)
|
||||
collapsed += collapse_large_pages(addr, &pgtables);
|
||||
}
|
||||
|
||||
- if (!collapsed) {
|
||||
- spin_unlock(&cpa_lock);
|
||||
+ if (!collapsed)
|
||||
return;
|
||||
- }
|
||||
|
||||
flush_tlb_all();
|
||||
|
||||
@@ -454,8 +451,6 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa)
|
||||
else
|
||||
pagetable_free(ptdesc);
|
||||
}
|
||||
-
|
||||
- spin_unlock(&cpa_lock);
|
||||
}
|
||||
|
||||
static void cpa_collapse_large_pages(struct cpa_data *cpa)
|
||||
@@ -916,24 +911,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
|
||||
{
|
||||
/* change init_mm */
|
||||
set_pte_atomic(kpte, pte);
|
||||
-#ifdef CONFIG_X86_32
|
||||
- {
|
||||
- struct page *page;
|
||||
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
+ if (IS_ENABLED(CONFIG_X86_32)) {
|
||||
+ struct ptdesc *ptdesc;
|
||||
+
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
|
||||
- pgd = (pgd_t *)page_address(page) + pgd_index(address);
|
||||
+ pgd = (pgd_t *)ptdesc_address(ptdesc) + pgd_index(address);
|
||||
p4d = p4d_offset(pgd, address);
|
||||
pud = pud_offset(p4d, address);
|
||||
pmd = pmd_offset(pud, address);
|
||||
set_pte_atomic((pte_t *)pmd, pte);
|
||||
}
|
||||
}
|
||||
-#endif
|
||||
}
|
||||
|
||||
static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot)
|
||||
@@ -1103,16 +1097,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
|
||||
static int should_split_large_page(pte_t *kpte, unsigned long address,
|
||||
struct cpa_data *cpa)
|
||||
{
|
||||
- int do_split;
|
||||
-
|
||||
if (cpa->force_split)
|
||||
return 1;
|
||||
|
||||
- spin_lock(&pgd_lock);
|
||||
- do_split = __should_split_large_page(kpte, address, cpa);
|
||||
- spin_unlock(&pgd_lock);
|
||||
-
|
||||
- return do_split;
|
||||
+ guard(spinlock)(&pgd_lock);
|
||||
+ return __should_split_large_page(kpte, address, cpa);
|
||||
}
|
||||
|
||||
static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn,
|
||||
@@ -1162,16 +1151,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
|
||||
bool nx, rw;
|
||||
pte_t *tmp;
|
||||
|
||||
- spin_lock(&pgd_lock);
|
||||
+ guard(spinlock)(&pgd_lock);
|
||||
/*
|
||||
* Check for races, another CPU might have split this page
|
||||
* up for us already:
|
||||
*/
|
||||
tmp = _lookup_address_cpa(cpa, address, &level, &nx, &rw);
|
||||
- if (tmp != kpte) {
|
||||
- spin_unlock(&pgd_lock);
|
||||
+ if (tmp != kpte)
|
||||
return 1;
|
||||
- }
|
||||
|
||||
paravirt_alloc_pte(&init_mm, page_to_pfn(base));
|
||||
|
||||
@@ -1204,7 +1191,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
|
||||
break;
|
||||
|
||||
default:
|
||||
- spin_unlock(&pgd_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1252,7 +1238,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
|
||||
* just split large page entry.
|
||||
*/
|
||||
flush_tlb_all();
|
||||
- spin_unlock(&pgd_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1327,11 +1312,11 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr,
|
||||
list_add(&page_ptdesc(pmd_page(old_pmd))->pt_list, pgtables);
|
||||
|
||||
if (IS_ENABLED(CONFIG_X86_32)) {
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
/* Update all PGD tables to use the same large page */
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
- pgd_t *pgd = (pgd_t *)page_address(page) + pgd_index(addr);
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
+ pgd_t *pgd = (pgd_t *)ptdesc_address(ptdesc) + pgd_index(addr);
|
||||
p4d_t *p4d = p4d_offset(pgd, addr);
|
||||
pud_t *pud = pud_offset(p4d, addr);
|
||||
pmd_t *pmd = pmd_offset(pud, addr);
|
||||
@@ -1405,7 +1390,7 @@ static int collapse_pud_page(pud_t *pud, unsigned long addr,
|
||||
*/
|
||||
static int collapse_large_pages(unsigned long addr, struct list_head *pgtables)
|
||||
{
|
||||
- int collapsed = 0;
|
||||
+ int collapsed;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
@@ -1413,26 +1398,24 @@ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables)
|
||||
|
||||
addr &= PMD_MASK;
|
||||
|
||||
- spin_lock(&pgd_lock);
|
||||
+ guard(spinlock)(&pgd_lock);
|
||||
pgd = pgd_offset_k(addr);
|
||||
if (pgd_none(*pgd))
|
||||
- goto out;
|
||||
+ return 0;
|
||||
p4d = p4d_offset(pgd, addr);
|
||||
if (p4d_none(*p4d))
|
||||
- goto out;
|
||||
+ return 0;
|
||||
pud = pud_offset(p4d, addr);
|
||||
if (!pud_present(*pud) || pud_leaf(*pud))
|
||||
- goto out;
|
||||
+ return 0;
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if (!pmd_present(*pmd) || pmd_leaf(*pmd))
|
||||
- goto out;
|
||||
+ return 0;
|
||||
|
||||
collapsed = collapse_pmd_page(pmd, addr, pgtables);
|
||||
if (collapsed)
|
||||
collapsed += collapse_pud_page(pud, addr, pgtables);
|
||||
|
||||
-out:
|
||||
- spin_unlock(&pgd_lock);
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
|
||||
--- a/arch/x86/mm/pgtable.c
|
||||
+++ b/arch/x86/mm/pgtable.c
|
||||
@@ -74,9 +74,9 @@ static void pgd_set_mm(pgd_t *pgd, struct mm_struct *mm)
|
||||
virt_to_ptdesc(pgd)->pt_mm = mm;
|
||||
}
|
||||
|
||||
-struct mm_struct *pgd_page_get_mm(struct page *page)
|
||||
+struct mm_struct *pgd_page_get_mm(struct ptdesc *pt)
|
||||
{
|
||||
- return page_ptdesc(page)->pt_mm;
|
||||
+ return pt->pt_mm;
|
||||
}
|
||||
|
||||
static void pgd_ctor(struct mm_struct *mm, pgd_t *pgd)
|
||||
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
|
||||
--- a/arch/x86/mm/tlb.c
|
||||
+++ b/arch/x86/mm/tlb.c
|
||||
@@ -1373,35 +1373,19 @@ void flush_tlb_multi(const struct cpumask *cpumask,
|
||||
*/
|
||||
unsigned long tlb_single_page_flush_ceiling __read_mostly = 33;
|
||||
|
||||
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct flush_tlb_info, flush_tlb_info);
|
||||
-
|
||||
-#ifdef CONFIG_DEBUG_VM
|
||||
-static DEFINE_PER_CPU(unsigned int, flush_tlb_info_idx);
|
||||
-#endif
|
||||
-
|
||||
-static struct flush_tlb_info *get_flush_tlb_info(struct mm_struct *mm,
|
||||
- unsigned long start, unsigned long end,
|
||||
- unsigned int stride_shift, bool freed_tables,
|
||||
- u64 new_tlb_gen)
|
||||
+static void init_flush_tlb_info(struct flush_tlb_info *info,
|
||||
+ struct mm_struct *mm,
|
||||
+ unsigned long start, unsigned long end,
|
||||
+ unsigned int stride_shift, bool freed_tables,
|
||||
+ u64 new_tlb_gen)
|
||||
{
|
||||
- struct flush_tlb_info *info = this_cpu_ptr(&flush_tlb_info);
|
||||
-
|
||||
-#ifdef CONFIG_DEBUG_VM
|
||||
- /*
|
||||
- * Ensure that the following code is non-reentrant and flush_tlb_info
|
||||
- * is not overwritten. This means no TLB flushing is initiated by
|
||||
- * interrupt handlers and machine-check exception handlers.
|
||||
- */
|
||||
- BUG_ON(this_cpu_inc_return(flush_tlb_info_idx) != 1);
|
||||
-#endif
|
||||
-
|
||||
/*
|
||||
* If the number of flushes is so large that a full flush
|
||||
* would be faster, do a full flush.
|
||||
*/
|
||||
if ((end - start) >> stride_shift > tlb_single_page_flush_ceiling) {
|
||||
- start = 0;
|
||||
- end = TLB_FLUSH_ALL;
|
||||
+ start = 0;
|
||||
+ end = TLB_FLUSH_ALL;
|
||||
}
|
||||
|
||||
info->start = start;
|
||||
@@ -1412,32 +1396,21 @@ static struct flush_tlb_info *get_flush_tlb_info(struct mm_struct *mm,
|
||||
info->new_tlb_gen = new_tlb_gen;
|
||||
info->initiating_cpu = smp_processor_id();
|
||||
info->trim_cpumask = 0;
|
||||
-
|
||||
- return info;
|
||||
-}
|
||||
-
|
||||
-static void put_flush_tlb_info(void)
|
||||
-{
|
||||
-#ifdef CONFIG_DEBUG_VM
|
||||
- /* Complete reentrancy prevention checks */
|
||||
- barrier();
|
||||
- this_cpu_dec(flush_tlb_info_idx);
|
||||
-#endif
|
||||
}
|
||||
|
||||
void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end, unsigned int stride_shift,
|
||||
bool freed_tables)
|
||||
{
|
||||
- struct flush_tlb_info *info;
|
||||
+ struct flush_tlb_info info;
|
||||
+ bool remote_flush = false;
|
||||
int cpu = get_cpu();
|
||||
u64 new_tlb_gen;
|
||||
|
||||
/* This is also a barrier that synchronizes with switch_mm(). */
|
||||
new_tlb_gen = inc_mm_tlb_gen(mm);
|
||||
|
||||
- info = get_flush_tlb_info(mm, start, end, stride_shift, freed_tables,
|
||||
- new_tlb_gen);
|
||||
+ init_flush_tlb_info(&info, mm, start, end, stride_shift, freed_tables, new_tlb_gen);
|
||||
|
||||
/*
|
||||
* flush_tlb_multi() is not optimized for the common case in which only
|
||||
@@ -1445,20 +1418,24 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
|
||||
* flush_tlb_func_local() directly in this case.
|
||||
*/
|
||||
if (mm_global_asid(mm)) {
|
||||
- broadcast_tlb_flush(info);
|
||||
+ broadcast_tlb_flush(&info);
|
||||
} else if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {
|
||||
- info->trim_cpumask = should_trim_cpumask(mm);
|
||||
- flush_tlb_multi(mm_cpumask(mm), info);
|
||||
- consider_global_asid(mm);
|
||||
+ remote_flush = true;
|
||||
} else if (mm == this_cpu_read(cpu_tlbstate.loaded_mm)) {
|
||||
lockdep_assert_irqs_enabled();
|
||||
local_irq_disable();
|
||||
- flush_tlb_func(info);
|
||||
+ flush_tlb_func(&info);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
- put_flush_tlb_info();
|
||||
put_cpu();
|
||||
+
|
||||
+ if (remote_flush) {
|
||||
+ info.trim_cpumask = should_trim_cpumask(mm);
|
||||
+ flush_tlb_multi(mm_cpumask(mm), &info);
|
||||
+ consider_global_asid(mm);
|
||||
+ }
|
||||
+
|
||||
mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end);
|
||||
}
|
||||
|
||||
@@ -1527,19 +1504,16 @@ static void kernel_tlb_flush_range(struct flush_tlb_info *info)
|
||||
|
||||
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
- struct flush_tlb_info *info;
|
||||
+ struct flush_tlb_info info;
|
||||
|
||||
guard(preempt)();
|
||||
+ init_flush_tlb_info(&info, NULL, start, end, PAGE_SHIFT, false,
|
||||
+ TLB_GENERATION_INVALID);
|
||||
|
||||
- info = get_flush_tlb_info(NULL, start, end, PAGE_SHIFT, false,
|
||||
- TLB_GENERATION_INVALID);
|
||||
-
|
||||
- if (info->end == TLB_FLUSH_ALL)
|
||||
- kernel_tlb_flush_all(info);
|
||||
+ if (info.end == TLB_FLUSH_ALL)
|
||||
+ kernel_tlb_flush_all(&info);
|
||||
else
|
||||
- kernel_tlb_flush_range(info);
|
||||
-
|
||||
- put_flush_tlb_info();
|
||||
+ kernel_tlb_flush_range(&info);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1707,12 +1681,12 @@ EXPORT_SYMBOL_FOR_KVM(__flush_tlb_all);
|
||||
|
||||
void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
|
||||
{
|
||||
- struct flush_tlb_info *info;
|
||||
-
|
||||
+ struct flush_tlb_info info;
|
||||
+ bool remote_flush = false;
|
||||
int cpu = get_cpu();
|
||||
|
||||
- info = get_flush_tlb_info(NULL, 0, TLB_FLUSH_ALL, 0, false,
|
||||
- TLB_GENERATION_INVALID);
|
||||
+ init_flush_tlb_info(&info, NULL, 0, TLB_FLUSH_ALL, 0, false,
|
||||
+ TLB_GENERATION_INVALID);
|
||||
/*
|
||||
* flush_tlb_multi() is not optimized for the common case in which only
|
||||
* a local TLB flush is needed. Optimize this use-case by calling
|
||||
@@ -1722,18 +1696,20 @@ void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
|
||||
invlpgb_flush_all_nonglobals();
|
||||
batch->unmapped_pages = false;
|
||||
} else if (cpumask_any_but(&batch->cpumask, cpu) < nr_cpu_ids) {
|
||||
- flush_tlb_multi(&batch->cpumask, info);
|
||||
+ remote_flush = true;
|
||||
} else if (cpumask_test_cpu(cpu, &batch->cpumask)) {
|
||||
lockdep_assert_irqs_enabled();
|
||||
local_irq_disable();
|
||||
- flush_tlb_func(info);
|
||||
+ flush_tlb_func(&info);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
- cpumask_clear(&batch->cpumask);
|
||||
-
|
||||
- put_flush_tlb_info();
|
||||
put_cpu();
|
||||
+
|
||||
+ if (remote_flush)
|
||||
+ flush_tlb_multi(&batch->cpumask, &info);
|
||||
+
|
||||
+ cpumask_clear(&batch->cpumask);
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c
|
||||
--- a/arch/x86/xen/mmu_pv.c
|
||||
+++ b/arch/x86/xen/mmu_pv.c
|
||||
@@ -836,15 +836,15 @@ static void xen_pgd_pin(struct mm_struct *mm)
|
||||
*/
|
||||
void xen_mm_pin_all(void)
|
||||
{
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
spin_lock(&init_mm.page_table_lock);
|
||||
spin_lock(&pgd_lock);
|
||||
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
- if (!PagePinned(page)) {
|
||||
- __xen_pgd_pin(&init_mm, (pgd_t *)page_address(page));
|
||||
- SetPageSavePinned(page);
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
+ if (!PagePinned(ptdesc_page(ptdesc))) {
|
||||
+ __xen_pgd_pin(&init_mm, (pgd_t *)ptdesc_address(ptdesc));
|
||||
+ SetPageSavePinned(ptdesc_page(ptdesc));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,16 +947,16 @@ static void xen_pgd_unpin(struct mm_struct *mm)
|
||||
*/
|
||||
void xen_mm_unpin_all(void)
|
||||
{
|
||||
- struct page *page;
|
||||
+ struct ptdesc *ptdesc;
|
||||
|
||||
spin_lock(&init_mm.page_table_lock);
|
||||
spin_lock(&pgd_lock);
|
||||
|
||||
- list_for_each_entry(page, &pgd_list, lru) {
|
||||
- if (PageSavePinned(page)) {
|
||||
- BUG_ON(!PagePinned(page));
|
||||
- __xen_pgd_unpin(&init_mm, (pgd_t *)page_address(page));
|
||||
- ClearPageSavePinned(page);
|
||||
+ list_for_each_entry(ptdesc, &pgd_list, pt_list) {
|
||||
+ if (PageSavePinned(ptdesc_page(ptdesc))) {
|
||||
+ BUG_ON(!PagePinned(ptdesc_page(ptdesc)));
|
||||
+ __xen_pgd_unpin(&init_mm, (pgd_t *)ptdesc_address(ptdesc));
|
||||
+ ClearPageSavePinned(ptdesc_page(ptdesc));
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,514 @@
|
||||
diff --git a/include/linux/sched.h b/include/linux/sched.h
|
||||
--- a/include/linux/sched.h
|
||||
+++ b/include/linux/sched.h
|
||||
@@ -832,6 +832,17 @@ struct kmap_ctrl {
|
||||
#endif
|
||||
};
|
||||
|
||||
+#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPTION)
|
||||
+struct task_ipi_mask {
|
||||
+ union {
|
||||
+ cpumask_t *ipi_mask_ptr;
|
||||
+ unsigned long ipi_mask_val;
|
||||
+ };
|
||||
+};
|
||||
+#else
|
||||
+struct task_ipi_mask { };
|
||||
+#endif
|
||||
+
|
||||
struct task_struct {
|
||||
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
||||
/*
|
||||
@@ -1442,6 +1453,7 @@ struct task_struct {
|
||||
struct list_head perf_event_list;
|
||||
struct perf_ctx_data __rcu *perf_ctx_data;
|
||||
#endif
|
||||
+ struct task_ipi_mask __private ipi_mask;
|
||||
#ifdef CONFIG_DEBUG_PREEMPT
|
||||
unsigned long preempt_disable_ip;
|
||||
#endif
|
||||
diff --git a/include/linux/smp.h b/include/linux/smp.h
|
||||
--- a/include/linux/smp.h
|
||||
+++ b/include/linux/smp.h
|
||||
@@ -47,8 +47,7 @@ extern void __smp_call_single_queue(int cpu, struct llist_node *node);
|
||||
/* total number of cpus in this system (may exceed NR_CPUS) */
|
||||
extern unsigned int total_cpus;
|
||||
|
||||
-int smp_call_function_single(int cpuid, smp_call_func_t func, void *info,
|
||||
- int wait);
|
||||
+int smp_call_function_single(int cpuid, smp_call_func_t func, void *info, bool wait);
|
||||
|
||||
void on_each_cpu_cond_mask(smp_cond_func_t cond_func, smp_call_func_t func,
|
||||
void *info, bool wait, const struct cpumask *mask);
|
||||
@@ -239,6 +238,18 @@ static inline int get_boot_cpu_id(void)
|
||||
|
||||
#endif /* !SMP */
|
||||
|
||||
+#if defined(CONFIG_PREEMPTION) && defined(CONFIG_SMP)
|
||||
+int smp_task_ipi_mask_alloc(struct task_struct *task);
|
||||
+void smp_task_ipi_mask_free(struct task_struct *task);
|
||||
+#else
|
||||
+static inline int smp_task_ipi_mask_alloc(struct task_struct *task)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline void smp_task_ipi_mask_free(struct task_struct *task) { }
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* raw_smp_processor_id() - get the current (unstable) CPU id
|
||||
*
|
||||
diff --git a/kernel/fork.c b/kernel/fork.c
|
||||
--- a/kernel/fork.c
|
||||
+++ b/kernel/fork.c
|
||||
@@ -543,6 +543,7 @@ void free_task(struct task_struct *tsk)
|
||||
#endif
|
||||
release_user_cpus_ptr(tsk);
|
||||
scs_release(tsk);
|
||||
+ smp_task_ipi_mask_free(tsk);
|
||||
|
||||
#ifndef CONFIG_THREAD_INFO_IN_TASK
|
||||
/*
|
||||
@@ -941,10 +942,14 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
|
||||
#endif
|
||||
account_kernel_stack(tsk, 1);
|
||||
|
||||
- err = scs_prepare(tsk, node);
|
||||
+ err = smp_task_ipi_mask_alloc(tsk);
|
||||
if (err)
|
||||
goto free_stack;
|
||||
|
||||
+ err = scs_prepare(tsk, node);
|
||||
+ if (err)
|
||||
+ goto free_ipi_mask;
|
||||
+
|
||||
#ifdef CONFIG_SECCOMP
|
||||
/*
|
||||
* We must handle setting up seccomp filters once we're under
|
||||
@@ -1022,6 +1027,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
|
||||
#endif
|
||||
return tsk;
|
||||
|
||||
+free_ipi_mask:
|
||||
+ smp_task_ipi_mask_free(tsk);
|
||||
free_stack:
|
||||
exit_task_stack_account(tsk);
|
||||
free_thread_stack(tsk);
|
||||
diff --git a/kernel/scftorture.c b/kernel/scftorture.c
|
||||
--- a/kernel/scftorture.c
|
||||
+++ b/kernel/scftorture.c
|
||||
@@ -348,6 +348,8 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
int ret = 0;
|
||||
struct scf_check *scfcp = NULL;
|
||||
struct scf_selector *scfsp = scf_sel_rand(trsp);
|
||||
+ bool is_single = (scfsp->scfs_prim == SCF_PRIM_SINGLE ||
|
||||
+ scfsp->scfs_prim == SCF_PRIM_SINGLE_RPC);
|
||||
|
||||
if (scfsp->scfs_prim == SCF_PRIM_SINGLE || scfsp->scfs_wait) {
|
||||
scfcp = kmalloc_obj(*scfcp, GFP_ATOMIC);
|
||||
@@ -364,8 +366,6 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
}
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_lock();
|
||||
- else
|
||||
- preempt_disable();
|
||||
switch (scfsp->scfs_prim) {
|
||||
case SCF_PRIM_RESCHED:
|
||||
if (IS_BUILTIN(CONFIG_SCF_TORTURE_TEST)) {
|
||||
@@ -411,13 +411,10 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
if (!ret) {
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_unlock();
|
||||
- else
|
||||
- preempt_enable();
|
||||
+
|
||||
wait_for_completion(&scfcp->scfc_completion);
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_lock();
|
||||
- else
|
||||
- preempt_disable();
|
||||
} else {
|
||||
scfp->n_single_rpc_ofl++;
|
||||
scf_add_to_free_list(scfcp);
|
||||
@@ -452,7 +449,7 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
scfcp->scfc_out = true;
|
||||
}
|
||||
if (scfcp && scfsp->scfs_wait) {
|
||||
- if (WARN_ON_ONCE((num_online_cpus() > 1 || scfsp->scfs_prim == SCF_PRIM_SINGLE) &&
|
||||
+ if (WARN_ON_ONCE(((use_cpus_read_lock && num_online_cpus() > 1) || is_single) &&
|
||||
!scfcp->scfc_out)) {
|
||||
pr_warn("%s: Memory-ordering failure, scfs_prim: %d.\n", __func__, scfsp->scfs_prim);
|
||||
atomic_inc(&n_mb_out_errs); // Leak rather than trash!
|
||||
@@ -463,8 +460,6 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
}
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_unlock();
|
||||
- else
|
||||
- preempt_enable();
|
||||
if (allocfail)
|
||||
schedule_timeout_idle((1 + longwait) * HZ); // Let no-wait handlers complete.
|
||||
else if (!(torture_random(trsp) & 0xfff))
|
||||
diff --git a/kernel/smp.c b/kernel/smp.c
|
||||
--- a/kernel/smp.c
|
||||
+++ b/kernel/smp.c
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gfp.h>
|
||||
+#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/sched.h>
|
||||
@@ -63,7 +64,14 @@ int smpcfd_prepare_cpu(unsigned int cpu)
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
return -ENOMEM;
|
||||
}
|
||||
- cfd->csd = alloc_percpu(call_single_data_t);
|
||||
+
|
||||
+ /*
|
||||
+ * Allocate the per-CPU CSD the first time a CPU comes up. It is
|
||||
+ * not freed when the CPU is offlined, so csd_lock_wait() can access
|
||||
+ * it even when the CPU was offlined after preemption was re-enabled.
|
||||
+ */
|
||||
+ if (!cfd->csd)
|
||||
+ cfd->csd = alloc_percpu(call_single_data_t);
|
||||
if (!cfd->csd) {
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
free_cpumask_var(cfd->cpumask_ipi);
|
||||
@@ -79,7 +87,6 @@ int smpcfd_dead_cpu(unsigned int cpu)
|
||||
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
free_cpumask_var(cfd->cpumask_ipi);
|
||||
- free_percpu(cfd->csd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -323,6 +330,8 @@ static void __csd_lock_wait(call_single_data_t *csd)
|
||||
int bug_id = 0;
|
||||
u64 ts0, ts1;
|
||||
|
||||
+ guard(preempt)();
|
||||
+
|
||||
ts1 = ts0 = ktime_get_mono_fast_ns();
|
||||
for (;;) {
|
||||
if (csd_lock_wait_toolong(csd, ts0, &ts1, &bug_id, &nmessages))
|
||||
@@ -659,17 +668,9 @@ void flush_smp_call_function_queue(void)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
-/**
|
||||
- * smp_call_function_single - Run a function on a specific CPU
|
||||
- * @cpu: Specific target CPU for this function.
|
||||
- * @func: The function to run. This must be fast and non-blocking.
|
||||
- * @info: An arbitrary pointer to pass to the function.
|
||||
- * @wait: If true, wait until function has completed on other CPUs.
|
||||
- *
|
||||
- * Returns: %0 on success, else a negative status code.
|
||||
- */
|
||||
-int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
|
||||
- int wait)
|
||||
+static int __smp_call_function_single(int cpu, smp_call_func_t func,
|
||||
+ void *info, const struct cpumask *mask,
|
||||
+ bool wait)
|
||||
{
|
||||
call_single_data_t *csd;
|
||||
call_single_data_t csd_stack = {
|
||||
@@ -686,6 +687,14 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
|
||||
*/
|
||||
this_cpu = get_cpu();
|
||||
|
||||
+ if (mask) {
|
||||
+ /* Try for same CPU (cheapest) */
|
||||
+ if (!cpumask_test_cpu(this_cpu, mask))
|
||||
+ cpu = sched_numa_find_nth_cpu(mask, 0, cpu_to_node(this_cpu));
|
||||
+ else
|
||||
+ cpu = this_cpu;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Can deadlock when called with interrupts disabled.
|
||||
* We allow cpu's that are not yet online though, as no one else can
|
||||
@@ -718,13 +727,32 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
|
||||
|
||||
err = generic_exec_single(cpu, csd);
|
||||
|
||||
+ /*
|
||||
+ * @csd is stack-allocated when @wait is true. No concurrent access
|
||||
+ * except from the IPI completion path, so we can re-enable preemption
|
||||
+ * early to reduce latency.
|
||||
+ */
|
||||
+ put_cpu();
|
||||
+
|
||||
if (wait)
|
||||
csd_lock_wait(csd);
|
||||
|
||||
- put_cpu();
|
||||
-
|
||||
return err;
|
||||
}
|
||||
+
|
||||
+/**
|
||||
+ * smp_call_function_single - Run a function on a specific CPU
|
||||
+ * @cpu: Specific target CPU for this function.
|
||||
+ * @func: The function to run. This must be fast and non-blocking.
|
||||
+ * @info: An arbitrary pointer to pass to the function.
|
||||
+ * @wait: If true, wait until function has completed on other CPUs.
|
||||
+ *
|
||||
+ * Returns: %0 on success, else a negative status code.
|
||||
+ */
|
||||
+int smp_call_function_single(int cpu, smp_call_func_t func, void *info, bool wait)
|
||||
+{
|
||||
+ return __smp_call_function_single(cpu, func, info, NULL, wait);
|
||||
+}
|
||||
EXPORT_SYMBOL(smp_call_function_single);
|
||||
|
||||
/**
|
||||
@@ -775,10 +803,10 @@ EXPORT_SYMBOL_GPL(smp_call_function_single_async);
|
||||
|
||||
/**
|
||||
* smp_call_function_any - Run a function on any of the given cpus
|
||||
- * @mask: The mask of cpus it can run on.
|
||||
- * @func: The function to run. This must be fast and non-blocking.
|
||||
- * @info: An arbitrary pointer to pass to the function.
|
||||
- * @wait: If true, wait until function has completed.
|
||||
+ * @mask: The mask of cpus it can run on.
|
||||
+ * @func: The function to run. This must be fast and non-blocking.
|
||||
+ * @info: An arbitrary pointer to pass to the function.
|
||||
+ * @wait: If true, wait until function has completed.
|
||||
*
|
||||
* Selection preference:
|
||||
* 1) current cpu if in @mask
|
||||
@@ -789,20 +817,54 @@ EXPORT_SYMBOL_GPL(smp_call_function_single_async);
|
||||
int smp_call_function_any(const struct cpumask *mask,
|
||||
smp_call_func_t func, void *info, int wait)
|
||||
{
|
||||
- unsigned int cpu;
|
||||
- int ret;
|
||||
-
|
||||
- /* Try for same CPU (cheapest) */
|
||||
- cpu = get_cpu();
|
||||
- if (!cpumask_test_cpu(cpu, mask))
|
||||
- cpu = sched_numa_find_nth_cpu(mask, 0, cpu_to_node(cpu));
|
||||
-
|
||||
- ret = smp_call_function_single(cpu, func, info, wait);
|
||||
- put_cpu();
|
||||
- return ret;
|
||||
+ return __smp_call_function_single(-1, func, info, mask, wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(smp_call_function_any);
|
||||
|
||||
+static DEFINE_STATIC_KEY_FALSE(ipi_mask_inlined);
|
||||
+
|
||||
+#ifdef CONFIG_PREEMPTION
|
||||
+
|
||||
+int smp_task_ipi_mask_alloc(struct task_struct *task)
|
||||
+{
|
||||
+ if (static_branch_unlikely(&ipi_mask_inlined))
|
||||
+ return 0;
|
||||
+
|
||||
+ ACCESS_PRIVATE(task, ipi_mask).ipi_mask_ptr =
|
||||
+ kmalloc(cpumask_size(), GFP_KERNEL);
|
||||
+ if (!ACCESS_PRIVATE(task, ipi_mask).ipi_mask_ptr)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void smp_task_ipi_mask_free(struct task_struct *task)
|
||||
+{
|
||||
+ if (static_branch_unlikely(&ipi_mask_inlined))
|
||||
+ return;
|
||||
+
|
||||
+ kfree(ACCESS_PRIVATE(task, ipi_mask).ipi_mask_ptr);
|
||||
+}
|
||||
+
|
||||
+static cpumask_t *smp_task_ipi_mask(struct task_struct *cur)
|
||||
+{
|
||||
+ /*
|
||||
+ * If cpumask_size() is smaller than or equal to the pointer
|
||||
+ * size, it stashes the cpumask in the pointer itself to
|
||||
+ * avoid extra memory allocations.
|
||||
+ */
|
||||
+ if (static_branch_unlikely(&ipi_mask_inlined))
|
||||
+ return (cpumask_t *)&ACCESS_PRIVATE(cur, ipi_mask).ipi_mask_val;
|
||||
+
|
||||
+ return ACCESS_PRIVATE(cur, ipi_mask).ipi_mask_ptr;
|
||||
+}
|
||||
+#else
|
||||
+static cpumask_t *smp_task_ipi_mask(struct task_struct *cur)
|
||||
+{
|
||||
+ return NULL;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Flags to be used as scf_flags argument of smp_call_function_many_cond().
|
||||
*
|
||||
@@ -817,13 +879,20 @@ static void smp_call_function_many_cond(const struct cpumask *mask,
|
||||
unsigned int scf_flags,
|
||||
smp_cond_func_t cond_func)
|
||||
{
|
||||
- int cpu, last_cpu, this_cpu = smp_processor_id();
|
||||
- struct call_function_data *cfd;
|
||||
+ struct cpumask *cpumask, *task_mask;
|
||||
bool wait = scf_flags & SCF_WAIT;
|
||||
- int nr_cpus = 0;
|
||||
+ struct call_function_data *cfd;
|
||||
+ int cpu, last_cpu, this_cpu;
|
||||
bool run_remote = false;
|
||||
+ int nr_cpus = 0;
|
||||
|
||||
- lockdep_assert_preemption_disabled();
|
||||
+ this_cpu = get_cpu();
|
||||
+ cfd = this_cpu_ptr(&cfd_data);
|
||||
+ task_mask = smp_task_ipi_mask(current);
|
||||
+ if (task_mask)
|
||||
+ cpumask = task_mask;
|
||||
+ else
|
||||
+ cpumask = cfd->cpumask;
|
||||
|
||||
/*
|
||||
* Can deadlock when called with interrupts disabled.
|
||||
@@ -845,16 +914,15 @@ static void smp_call_function_many_cond(const struct cpumask *mask,
|
||||
|
||||
/* Check if we need remote execution, i.e., any CPU excluding this one. */
|
||||
if (cpumask_any_and_but(mask, cpu_online_mask, this_cpu) < nr_cpu_ids) {
|
||||
- cfd = this_cpu_ptr(&cfd_data);
|
||||
- cpumask_and(cfd->cpumask, mask, cpu_online_mask);
|
||||
- __cpumask_clear_cpu(this_cpu, cfd->cpumask);
|
||||
+ cpumask_and(cpumask, mask, cpu_online_mask);
|
||||
+ __cpumask_clear_cpu(this_cpu, cpumask);
|
||||
|
||||
cpumask_clear(cfd->cpumask_ipi);
|
||||
- for_each_cpu(cpu, cfd->cpumask) {
|
||||
+ for_each_cpu(cpu, cpumask) {
|
||||
call_single_data_t *csd = per_cpu_ptr(cfd->csd, cpu);
|
||||
|
||||
if (cond_func && !cond_func(cpu, info)) {
|
||||
- __cpumask_clear_cpu(cpu, cfd->cpumask);
|
||||
+ __cpumask_clear_cpu(cpu, cpumask);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -904,8 +972,18 @@ static void smp_call_function_many_cond(const struct cpumask *mask,
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * The IPI work has been queued and dispatched. On PREEMPT kernels,
|
||||
+ * tasks created through dup_task_struct() have task-local wait masks.
|
||||
+ * The boot init_task can fall back to cfd->cpumask when the mask is
|
||||
+ * not inlined, but other tasks still use task-local masks and cannot
|
||||
+ * overwrite it. On !PREEMPT kernels, preempt_enable() cannot schedule
|
||||
+ * another task, so the per-CPU mask remains protected.
|
||||
+ */
|
||||
+ put_cpu();
|
||||
+
|
||||
if (run_remote && wait) {
|
||||
- for_each_cpu(cpu, cfd->cpumask) {
|
||||
+ for_each_cpu(cpu, cpumask) {
|
||||
call_single_data_t *csd;
|
||||
|
||||
csd = per_cpu_ptr(cfd->csd, cpu);
|
||||
@@ -916,15 +994,14 @@ static void smp_call_function_many_cond(const struct cpumask *mask,
|
||||
|
||||
/**
|
||||
* smp_call_function_many() - Run a function on a set of CPUs.
|
||||
- * @mask: The set of cpus to run on (only runs on online subset).
|
||||
- * @func: The function to run. This must be fast and non-blocking.
|
||||
- * @info: An arbitrary pointer to pass to the function.
|
||||
- * @wait: If true, wait (atomically) until function has completed
|
||||
- * on other CPUs.
|
||||
+ * @mask: The set of cpus to run on (only runs on online subset).
|
||||
+ * @func: The function to run. This must be fast and non-blocking.
|
||||
+ * @info: An arbitrary pointer to pass to the function.
|
||||
+ * @wait: If true, wait (atomically) until function has completed
|
||||
+ * on other CPUs.
|
||||
*
|
||||
* You must not call this function with disabled interrupts or from a
|
||||
- * hardware interrupt handler or from a bottom half handler. Preemption
|
||||
- * must be disabled when calling this function.
|
||||
+ * hardware interrupt handler or from a bottom half handler.
|
||||
*
|
||||
* @func is not called on the local CPU even if @mask contains it. Consider
|
||||
* using on_each_cpu_cond_mask() instead if this is not desirable.
|
||||
@@ -938,10 +1015,10 @@ EXPORT_SYMBOL(smp_call_function_many);
|
||||
|
||||
/**
|
||||
* smp_call_function() - Run a function on all other CPUs.
|
||||
- * @func: The function to run. This must be fast and non-blocking.
|
||||
- * @info: An arbitrary pointer to pass to the function.
|
||||
- * @wait: If true, wait (atomically) until function has completed
|
||||
- * on other CPUs.
|
||||
+ * @func: The function to run. This must be fast and non-blocking.
|
||||
+ * @info: An arbitrary pointer to pass to the function.
|
||||
+ * @wait: If true, wait (atomically) until function has completed
|
||||
+ * on other CPUs.
|
||||
*
|
||||
* If @wait is true, then returns once @func has returned; otherwise
|
||||
* it returns just before the target cpu calls @func.
|
||||
@@ -951,9 +1028,8 @@ EXPORT_SYMBOL(smp_call_function_many);
|
||||
*/
|
||||
void smp_call_function(smp_call_func_t func, void *info, int wait)
|
||||
{
|
||||
- preempt_disable();
|
||||
- smp_call_function_many(cpu_online_mask, func, info, wait);
|
||||
- preempt_enable();
|
||||
+ smp_call_function_many_cond(cpu_online_mask, func, info,
|
||||
+ wait ? SCF_WAIT : 0, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function);
|
||||
|
||||
@@ -1019,6 +1095,9 @@ EXPORT_SYMBOL(nr_cpu_ids);
|
||||
void __init setup_nr_cpu_ids(void)
|
||||
{
|
||||
set_nr_cpu_ids(find_last_bit(cpumask_bits(cpu_possible_mask), NR_CPUS) + 1);
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PREEMPTION) && cpumask_size() <= sizeof(unsigned long))
|
||||
+ static_branch_enable(&ipi_mask_inlined);
|
||||
}
|
||||
|
||||
/* Called by boot processor to activate the rest. */
|
||||
@@ -1055,12 +1134,14 @@ void __init smp_init(void)
|
||||
* @func: The function to run on all applicable CPUs.
|
||||
* This must be fast and non-blocking.
|
||||
* @info: An arbitrary pointer to pass to both functions.
|
||||
- * @wait: If true, wait (atomically) until function has
|
||||
- * completed on other CPUs.
|
||||
+ * @wait: If true, wait until function has completed on other CPUs.
|
||||
* @mask: The set of cpus to run on (only runs on online subset).
|
||||
*
|
||||
- * Preemption is disabled to protect against CPUs going offline but not online.
|
||||
- * CPUs going online during the call will not be seen or sent an IPI.
|
||||
+ * Target CPU selection and work queueing are done with preemption
|
||||
+ * disabled. This protects against CPUs going offline, but not against
|
||||
+ * CPUs coming online concurrently; newly online CPUs are not guaranteed
|
||||
+ * to be seen or sent an IPI. If @wait is true, the final wait for remote
|
||||
+ * completion happens after that preemption-disabled section.
|
||||
*
|
||||
* You must not call this function with disabled interrupts or
|
||||
* from a hardware interrupt handler or from a bottom half handler.
|
||||
@@ -1073,9 +1154,7 @@ void on_each_cpu_cond_mask(smp_cond_func_t cond_func, smp_call_func_t func,
|
||||
if (wait)
|
||||
scf_flags |= SCF_WAIT;
|
||||
|
||||
- preempt_disable();
|
||||
smp_call_function_many_cond(mask, func, info, scf_flags, cond_func);
|
||||
- preempt_enable();
|
||||
}
|
||||
EXPORT_SYMBOL(on_each_cpu_cond_mask);
|
||||
|
||||
diff --git a/kernel/up.c b/kernel/up.c
|
||||
--- a/kernel/up.c
|
||||
+++ b/kernel/up.c
|
||||
@@ -9,8 +9,7 @@
|
||||
#include <linux/smp.h>
|
||||
#include <linux/hypervisor.h>
|
||||
|
||||
-int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
|
||||
- int wait)
|
||||
+int smp_call_function_single(int cpu, void (*func)(void *info), void *info, bool wait)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
|
||||
--- a/kernel/sched/fair.c
|
||||
+++ b/kernel/sched/fair.c
|
||||
@@ -13350,12 +13350,15 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq,
|
||||
* still unbalanced. ld_moved simply stays zero, so it is
|
||||
* correctly treated as an imbalance.
|
||||
*/
|
||||
- env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running);
|
||||
-
|
||||
more_balance:
|
||||
rq_lock_irqsave(busiest, &rf);
|
||||
update_rq_clock(busiest);
|
||||
|
||||
+ if (!env.loop_max)
|
||||
+ env.loop_max = min(sysctl_sched_nr_migrate, busiest->cfs.h_nr_queued);
|
||||
+ else
|
||||
+ env.loop_max = min(env.loop_max, busiest->cfs.h_nr_queued);
|
||||
+
|
||||
/*
|
||||
* cur_ld_moved - load moved in current iteration
|
||||
* ld_moved - cumulative load moved across iterations
|
||||
Binary file not shown.
Loaded 100 of 466 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user