diff --git a/.github/workflows/module-update.yml b/.github/workflows/module-update.yml new file mode 100644 index 0000000..4ed3680 --- /dev/null +++ b/.github/workflows/module-update.yml @@ -0,0 +1,217 @@ +name: Module Update + +# Shared updater for module repos: detects an upstream pin change, opens/refreshes +# one PR per module (never a direct push to main), and dispatches cache-server's +# nix-build in external mode. cache-server builds every config against the PR head +# and rebase-merges the PR natively on success; on failure the PR stays open with +# one status comment. An open PR is re-dispatched on every cycle even without a +# new pin: a green rebuild is cheap (cache-hit skip) and completes a merge that a +# moved main had deferred. + +on: + workflow_call: + inputs: + module: + description: "Module name: branch update/, title update(): ..." + type: string + required: true + paths: + description: "Space-separated paths this module owns (diff detection + commit)" + type: string + required: true + command: + description: "Update command (sources core/lib/update-lib.sh)" + type: string + required: true + version-file: + description: "Optional json file whose version field feeds the old -> new title" + type: string + default: '' + version-jq: + description: "jq filter for the version field" + type: string + default: '.version' + title-command: + description: "Optional shell snippet printing a custom title suffix" + type: string + default: '' + +permissions: {} + +jobs: + update: + runs-on: ubuntu-latest + concurrency: + group: module-update-${{ inputs.module }} + cancel-in-progress: false + steps: + # main is ruleset-protected, and github-actions[bot] holds no repository role, + # so the App is the bypass actor: checkout credentials, branch push, PR upsert + # and the cache-server dispatch all run on its token. + - name: Mint app token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.DISPATCH_APP_ID }} + private-key: ${{ secrets.DISPATCH_APP_PRIVATE_KEY }} + owner: IceDOS + repositories: | + ${{ github.event.repository.name }} + cache-server + permission-contents: write + permission-pull-requests: write + permission-actions: write + + - uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + + # update.sh sources core/lib/update-lib.sh. CI only checks out this repo, so core + # is fetched alongside it; the script falls back to a sibling checkout when run + # from a local IceDOS tree, so .icedos-core never exists outside CI. + - uses: actions/checkout@v4 + with: + repository: IceDOS/core + path: .icedos-core + + - uses: nixbuild/nix-quick-install-action@v30 + with: + nix_conf: | + experimental-features = nix-command flakes + nix-path = nixpkgs=channel:nixos-unstable + + - name: Read current version + id: before + if: inputs.version-file != '' + run: echo "version=$(jq -r '${{ inputs.version-jq }}' '${{ inputs.version-file }}' 2>/dev/null || true)" >>"$GITHUB_OUTPUT" + + - name: Run update script + env: + GITHUB_TOKEN: ${{ github.token }} + run: ${{ inputs.command }} + + - name: Commit update on top of latest main + id: commit + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + if git status --porcelain -- ${{ inputs.paths }} | grep -q .; then + echo "changed=true" >>"$GITHUB_OUTPUT" + else + echo "changed=false" >>"$GITHUB_OUTPUT" + echo "nothing to do" + exit 0 + fi + + # Title: custom snippet wins, else old -> new from version-file. + suffix="" + if [ -n "$TITLE_CMD" ]; then + suffix=$(eval "$TITLE_CMD") + elif [ -n "$VERSION_FILE" ]; then + old="${{ steps.before.outputs.version }}" + new=$(jq -r "$VERSION_JQ" "$VERSION_FILE") + suffix="${old:+$old -> }$new" + fi + msg="update(${{ inputs.module }})${suffix:+: $suffix}" + echo "title=$msg" >>"$GITHUB_OUTPUT" + + # The event checkout may be behind (a sibling updater landed after the + # schedule fired). Each module owns disjoint paths, so this rebase cannot + # conflict; a conflict means something unexpected and must stop the run. + git fetch --unshallow origin main 2>/dev/null || git fetch origin main + git add -- ${{ inputs.paths }} + git commit -m "$msg" + git rebase origin/main + + # Identical content on top of the same main commit: the open PR already + # covers it, and another force-push would re-queue a build for nothing. + if git fetch origin "refs/heads/update/${{ inputs.module }}" 2>/dev/null; then + existing_tree=$(git rev-parse "FETCH_HEAD^{tree}") + existing_parent=$(git rev-parse "FETCH_HEAD^") + if [ "$existing_tree" = "$(git rev-parse HEAD^{tree})" ] && [ "$existing_parent" = "$(git rev-parse origin/main)" ]; then + echo "update/${{ inputs.module }}: already up to date" + echo "head=$(git rev-parse FETCH_HEAD)" >>"$GITHUB_OUTPUT" + echo "title=$(git log -1 --format=%s FETCH_HEAD)" >>"$GITHUB_OUTPUT" + exit 0 + fi + fi + + git push --force origin "HEAD:refs/heads/update/${{ inputs.module }}" + echo "head=$(git rev-parse HEAD)" >>"$GITHUB_OUTPUT" + env: + TITLE_CMD: ${{ inputs.title-command }} + VERSION_FILE: ${{ inputs.version-file }} + VERSION_JQ: ${{ inputs.version-jq }} + + # Runs even when nothing changed: an open PR from a previously failed or + # deferred build must be re-dispatched, otherwise it would never retry. + - name: Ensure module PR + id: pr + if: success() + uses: actions/github-script@v7 + # Script reads CHANGED/PR_TITLE/HEAD via process.env; without this block + # they are undefined and every run looks like "nothing changed". + env: + CHANGED: ${{ steps.commit.outputs.changed }} + PR_TITLE: ${{ steps.commit.outputs.title }} + HEAD: ${{ steps.commit.outputs.head }} + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + const { owner, repo } = context.repo; + const branch = 'update/${{ inputs.module }}'; + const headRef = `${owner}:${branch}`; + const changed = process.env.CHANGED === 'true'; + + const prs = (await github.rest.pulls.list({ owner, repo, head: headRef, state: 'all', per_page: 20 })).data; + let pr = prs.find(p => p.state === 'open'); + + if (changed) { + const title = process.env.PR_TITLE; + const body = title; + if (pr) { + if (pr.title !== title) { + await github.rest.pulls.update({ owner, repo, pull_number: pr.number, title, body }); + } + core.info(`update/${branch}: PR #${pr.number} refreshed`); + } else { + // Reopen a closed-but-unmerged PR; a merged one can never reopen. + const closed = prs.find(p => p.state === 'closed' && !p.merged_at); + if (closed) { + await github.rest.pulls.update({ owner, repo, pull_number: closed.number, state: 'open', title, body }); + pr = (await github.rest.pulls.get({ owner, repo, pull_number: closed.number })).data; + core.info(`update/${branch}: reopened PR #${pr.number}`); + } else { + pr = (await github.rest.pulls.create({ owner, repo, head: headRef, base: 'main', title, body })).data; + core.info(`update/${branch}: opened PR #${pr.number}`); + } + } + core.setOutput('number', String(pr.number)); + core.setOutput('head', process.env.HEAD); + } else if (pr) { + // No new pin: re-dispatch the open PR so a failed/deferred build retries. + core.setOutput('number', String(pr.number)); + core.setOutput('head', pr.head.sha); + } + + - name: Dispatch cache-server external build + if: steps.pr.outputs.number != '' + uses: actions/github-script@v7 + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'IceDOS', + repo: 'cache-server', + workflow_id: 'nix-build.yml', + ref: 'main', + inputs: { + 'source-repo': context.repo.repo, + 'source-pr': process.env.PR_NUMBER, + 'source-head': process.env.PR_HEAD, + }, + }); + env: + PR_NUMBER: ${{ steps.pr.outputs.number }} + PR_HEAD: ${{ steps.pr.outputs.head }} diff --git a/.github/workflows/update-deckbd.yml b/.github/workflows/update-deckbd.yml index 2aeb1b8..8c507cb 100644 --- a/.github/workflows/update-deckbd.yml +++ b/.github/workflows/update-deckbd.yml @@ -1,139 +1,21 @@ name: Update deckbd +# Thin wrapper: all logic lives in .github/workflows/module-update.yml. The +# updater opens a PR and cache-server's external build gates the merge, so this +# repo's main only advances after a green build. on: schedule: - # Daily (UTC), on its own minute so this repo's updaters never push at the same time. - # GitHub may delay or skip scheduled runs under load, and they only run on the - # default branch. + # GitHub may delay or skip scheduled runs, and they only run on the default branch. - cron: "20 4 * * *" workflow_dispatch: - -permissions: - contents: write - -# Never run two updaters at once; let an in-flight one finish. -concurrency: - group: update-deckbd - cancel-in-progress: false +permissions: {} jobs: update: - runs-on: ubuntu-latest - steps: - # main may be ruleset-protected ("changes must be made through a pull request"), - # and github-actions[bot] holds no repository role, so role-based bypasses would - # not cover it. The App is a bypass actor instead — mint its token up front and use - # it for the checkout credentials, the push, and the cache-server dispatch. - - name: Mint app token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.DISPATCH_APP_ID }} - private-key: ${{ secrets.DISPATCH_APP_PRIVATE_KEY }} - owner: IceDOS - repositories: | - hardware - cache-server - # Requested explicitly so a missing grant fails here by name, instead of - # surfacing later as an opaque 403 on push. - permission-contents: write - permission-actions: write - - - uses: actions/checkout@v4 - with: - token: ${{ steps.app-token.outputs.token }} - - # update.sh sources core/lib/update-lib.sh. CI only checks out this repo, so core - # is fetched alongside it; the script falls back to a sibling checkout when run - # from a local IceDOS tree, so .icedos-core never exists outside CI. - - uses: actions/checkout@v4 - with: - repository: IceDOS/core - path: .icedos-core - - - uses: nixbuild/nix-quick-install-action@v30 - with: - nix_conf: | - experimental-features = nix-command flakes - nix-path = nixpkgs=channel:nixos-unstable - - - name: Read current pin - id: before - run: echo "version=$(jq -r '.version' modules/drivers/deckbd/source.json)" >>"$GITHUB_OUTPUT" - - - name: Run deckbd update script - env: - GITHUB_TOKEN: ${{ github.token }} - run: nix-shell modules/drivers/deckbd/update.sh - - - name: Detect changes - id: changes - run: | - if git diff --quiet modules/drivers/deckbd/; then - echo "changed=false" >>"$GITHUB_OUTPUT" - else - echo "changed=true" >>"$GITHUB_OUTPUT" - fi - - - name: Commit and push - if: steps.changes.outputs.changed == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - new=$(jq -r '.version' modules/drivers/deckbd/source.json) - git add -A modules/drivers/deckbd - git commit -m "update(deckbd): ${{ steps.before.outputs.version }} -> $new" - - # Sibling updaters push to this same branch, so a push can be rejected even - # after a successful rebase: another job can land in the window between the - # two. Retry until this one wins. - # - # Committing before rebasing (rather than pulling first) is deliberate. The - # change is already a finished commit, so a conflict stops the run with the - # work intact. Pulling while it was still uncommitted would need --autostash, - # and a conflicted stash re-apply would leave markers in the tree for the - # commit above to pick up. - for attempt in 1 2 3 4 5; do - if push_output=$(git push origin "HEAD:$GITHUB_REF_NAME" 2>&1); then - echo "$push_output" - exit 0 - fi - echo "$push_output" - - # Only a lost race is worth retrying. A ruleset denial (GH013), a bad token - # or a missing permission would fail the same way five times and bury the - # real error, so stop at the first one. - if ! grep -qE 'fetch first|non-fast-forward' <<<"$push_output"; then - echo "push was rejected for a reason retrying cannot fix; giving up" >&2 - exit 1 - fi - - echo "push lost a race (attempt $attempt); rebasing onto the latest $GITHUB_REF_NAME" - # Jittered so simultaneously-rejected jobs do not retry in lockstep. - sleep $(( (RANDOM % 5) + attempt )) - - # Each updater owns one module's files, so a conflict means something - # unexpected touched them. Abort rather than retry on a half-rebased tree. - if ! git pull --rebase origin "$GITHUB_REF_NAME"; then - git rebase --abort || true - echo "rebase onto $GITHUB_REF_NAME conflicted; aborting" >&2 - exit 1 - fi - done - - echo "could not push after 5 attempts" >&2 - exit 1 - - - name: Trigger cache-server build - if: steps.changes.outputs.changed == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ steps.app-token.outputs.token }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'IceDOS', - repo: 'cache-server', - workflow_id: 'nix-build.yml', - ref: 'main', - }); + uses: ./.github/workflows/module-update.yml + with: + module: deckbd + paths: modules/drivers/deckbd + command: nix-shell modules/drivers/deckbd/update.sh + version-file: modules/drivers/deckbd/source.json + secrets: inherit diff --git a/.github/workflows/update-low-latency-vulkan-layer.yml b/.github/workflows/update-low-latency-vulkan-layer.yml index e8d40e9..4690cda 100644 --- a/.github/workflows/update-low-latency-vulkan-layer.yml +++ b/.github/workflows/update-low-latency-vulkan-layer.yml @@ -1,139 +1,21 @@ name: Update low-latency-vulkan-layer +# Thin wrapper: all logic lives in .github/workflows/module-update.yml. The +# updater opens a PR and cache-server's external build gates the merge, so this +# repo's main only advances after a green build. on: schedule: - # Daily (UTC), on its own minute so this repo's updaters never push at the same time. - # GitHub may delay or skip scheduled runs under load, and they only run on the - # default branch. + # GitHub may delay or skip scheduled runs, and they only run on the default branch. - cron: "20 5 * * *" workflow_dispatch: - -permissions: - contents: write - -# Never run two updaters at once; let an in-flight one finish. -concurrency: - group: update-low-latency-vulkan-layer - cancel-in-progress: false +permissions: {} jobs: update: - runs-on: ubuntu-latest - steps: - # main may be ruleset-protected ("changes must be made through a pull request"), - # and github-actions[bot] holds no repository role, so role-based bypasses would - # not cover it. The App is a bypass actor instead — mint its token up front and use - # it for the checkout credentials, the push, and the cache-server dispatch. - - name: Mint app token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.DISPATCH_APP_ID }} - private-key: ${{ secrets.DISPATCH_APP_PRIVATE_KEY }} - owner: IceDOS - repositories: | - hardware - cache-server - # Requested explicitly so a missing grant fails here by name, instead of - # surfacing later as an opaque 403 on push. - permission-contents: write - permission-actions: write - - - uses: actions/checkout@v4 - with: - token: ${{ steps.app-token.outputs.token }} - - # update.sh sources core/lib/update-lib.sh. CI only checks out this repo, so core - # is fetched alongside it; the script falls back to a sibling checkout when run - # from a local IceDOS tree, so .icedos-core never exists outside CI. - - uses: actions/checkout@v4 - with: - repository: IceDOS/core - path: .icedos-core - - - uses: nixbuild/nix-quick-install-action@v30 - with: - nix_conf: | - experimental-features = nix-command flakes - nix-path = nixpkgs=channel:nixos-unstable - - - name: Read current pin - id: before - run: echo "version=$(jq -r '.version' modules/drivers/low-latency-vulkan-layer/source.json)" >>"$GITHUB_OUTPUT" - - - name: Run low-latency-vulkan-layer update script - env: - GITHUB_TOKEN: ${{ github.token }} - run: nix-shell modules/drivers/low-latency-vulkan-layer/update.sh - - - name: Detect changes - id: changes - run: | - if git diff --quiet modules/drivers/low-latency-vulkan-layer/; then - echo "changed=false" >>"$GITHUB_OUTPUT" - else - echo "changed=true" >>"$GITHUB_OUTPUT" - fi - - - name: Commit and push - if: steps.changes.outputs.changed == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - new=$(jq -r '.version' modules/drivers/low-latency-vulkan-layer/source.json) - git add -A modules/drivers/low-latency-vulkan-layer - git commit -m "update(low-latency-vulkan-layer): ${{ steps.before.outputs.version }} -> $new" - - # Sibling updaters push to this same branch, so a push can be rejected even - # after a successful rebase: another job can land in the window between the - # two. Retry until this one wins. - # - # Committing before rebasing (rather than pulling first) is deliberate. The - # change is already a finished commit, so a conflict stops the run with the - # work intact. Pulling while it was still uncommitted would need --autostash, - # and a conflicted stash re-apply would leave markers in the tree for the - # commit above to pick up. - for attempt in 1 2 3 4 5; do - if push_output=$(git push origin "HEAD:$GITHUB_REF_NAME" 2>&1); then - echo "$push_output" - exit 0 - fi - echo "$push_output" - - # Only a lost race is worth retrying. A ruleset denial (GH013), a bad token - # or a missing permission would fail the same way five times and bury the - # real error, so stop at the first one. - if ! grep -qE 'fetch first|non-fast-forward' <<<"$push_output"; then - echo "push was rejected for a reason retrying cannot fix; giving up" >&2 - exit 1 - fi - - echo "push lost a race (attempt $attempt); rebasing onto the latest $GITHUB_REF_NAME" - # Jittered so simultaneously-rejected jobs do not retry in lockstep. - sleep $(( (RANDOM % 5) + attempt )) - - # Each updater owns one module's files, so a conflict means something - # unexpected touched them. Abort rather than retry on a half-rebased tree. - if ! git pull --rebase origin "$GITHUB_REF_NAME"; then - git rebase --abort || true - echo "rebase onto $GITHUB_REF_NAME conflicted; aborting" >&2 - exit 1 - fi - done - - echo "could not push after 5 attempts" >&2 - exit 1 - - - name: Trigger cache-server build - if: steps.changes.outputs.changed == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ steps.app-token.outputs.token }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'IceDOS', - repo: 'cache-server', - workflow_id: 'nix-build.yml', - ref: 'main', - }); + uses: ./.github/workflows/module-update.yml + with: + module: low-latency-vulkan-layer + paths: modules/drivers/low-latency-vulkan-layer + command: nix-shell modules/drivers/low-latency-vulkan-layer/update.sh + version-file: modules/drivers/low-latency-vulkan-layer/source.json + secrets: inherit diff --git a/.github/workflows/update-mesa.yml b/.github/workflows/update-mesa.yml index 733bb74..4294405 100644 --- a/.github/workflows/update-mesa.yml +++ b/.github/workflows/update-mesa.yml @@ -1,137 +1,28 @@ name: Update Mesa Overlays +# Thin wrapper: all logic lives in .github/workflows/module-update.yml. The +# updater opens a PR and cache-server's external build gates the merge, so this +# repo's main only advances after a green build. on: schedule: - # Every 15 minutes (UTC). GitHub may delay or skip scheduled runs under load, - # and they only run on the default branch. + # GitHub may delay or skip scheduled runs, and they only run on the default branch. - cron: "*/15 * * * *" workflow_dispatch: - -permissions: - contents: write - -# Never run two updaters at once; let an in-flight one finish. -concurrency: - group: update-mesa - cancel-in-progress: false +permissions: {} jobs: update: - runs-on: ubuntu-latest - steps: - # main is ruleset-protected ("changes must be made through a pull request"), and - # github-actions[bot] holds no repository role, so role-based bypasses do not cover - # it. The App is a bypass actor instead — mint its token up front and use it for the - # checkout credentials, the push, and the cache-server dispatch. - - name: Mint app token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.DISPATCH_APP_ID }} - private-key: ${{ secrets.DISPATCH_APP_PRIVATE_KEY }} - owner: IceDOS - repositories: | - hardware - cache-server - # Requested explicitly so a missing grant fails here by name, instead of - # surfacing later as an opaque 403 on push. - permission-contents: write - permission-actions: write - - - uses: actions/checkout@v4 - with: - token: ${{ steps.app-token.outputs.token }} - - - uses: nixbuild/nix-quick-install-action@v30 - with: - nix_conf: | - experimental-features = nix-command flakes - nix-path = nixpkgs=channel:nixos-unstable - - - name: Run mesa update script - run: nix-shell modules/graphics/modules/mesa/update.sh - - - name: Detect changes - id: changes - run: | - if git diff --quiet modules/graphics/modules/mesa/; then - echo "changed=false" >>"$GITHUB_OUTPUT" - else - echo "changed=true" >>"$GITHUB_OUTPUT" - fi - - - name: Commit and push - if: steps.changes.outputs.changed == 'true' - run: | - cd modules/graphics/modules/mesa - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - # Message reflects whichever overlay pins actually changed: - # both -> "update(mesa): git -> , rc -> " - # one -> "update(mesa): git -> " or "update(mesa): rc -> " - # patches only -> "update(mesa): sync patches" - body="" - append() { if [ -n "$body" ]; then body+=", "; fi; body+="$1"; } - if ! git diff --quiet git.json; then - append "git -> $(jq -r '.rev' git.json | cut -c1-12)" - fi - if ! git diff --quiet rc.json; then - append "rc -> $(jq -r '.version' rc.json)" - fi - if [ -n "$body" ]; then msg="update(mesa): $body"; else msg="update(mesa): sync patches"; fi - - git add rc.json git.json patches/ - git commit -m "$msg" - # Sibling updaters push to this same branch, so a push can be rejected even - # after a successful rebase: another job can land in the window between the - # two. Retry until this one wins. - # - # Committing before rebasing (rather than pulling first) is deliberate. The - # change is already a finished commit, so a conflict stops the run with the - # work intact. Pulling while it was still uncommitted would need --autostash, - # and a conflicted stash re-apply would leave markers in the tree for the - # commit above to pick up. - for attempt in 1 2 3 4 5; do - if push_output=$(git push origin "HEAD:$GITHUB_REF_NAME" 2>&1); then - echo "$push_output" - exit 0 - fi - echo "$push_output" - - # Only a lost race is worth retrying. A ruleset denial (GH013), a bad token - # or a missing permission would fail the same way five times and bury the - # real error, so stop at the first one. - if ! grep -qE 'fetch first|non-fast-forward' <<<"$push_output"; then - echo "push was rejected for a reason retrying cannot fix; giving up" >&2 - exit 1 - fi - - echo "push lost a race (attempt $attempt); rebasing onto the latest $GITHUB_REF_NAME" - # Jittered so simultaneously-rejected jobs do not retry in lockstep. - sleep $(( (RANDOM % 5) + attempt )) - - # Each updater owns one module's files, so a conflict means something - # unexpected touched them. Abort rather than retry on a half-rebased tree. - if ! git pull --rebase origin "$GITHUB_REF_NAME"; then - git rebase --abort || true - echo "rebase onto $GITHUB_REF_NAME conflicted; aborting" >&2 - exit 1 - fi - done - - echo "could not push after 5 attempts" >&2 - exit 1 - - - name: Trigger cache-server build - if: steps.changes.outputs.changed == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ steps.app-token.outputs.token }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'IceDOS', - repo: 'cache-server', - workflow_id: 'nix-build.yml', - ref: 'main', - }); + uses: ./.github/workflows/module-update.yml + with: + module: mesa + paths: modules/graphics/modules/mesa + command: nix-shell modules/graphics/modules/mesa/update.sh + # Title reflects whichever overlay pins actually changed; patches-only syncs. + title-command: | + cd modules/graphics/modules/mesa + body="" + append() { if [ -n "$body" ]; then body+=", "; fi; body+="$1"; } + if ! git diff --quiet git.json; then append "git -> $(jq -r '.rev' git.json | cut -c1-12)"; fi + if ! git diff --quiet rc.json; then append "rc -> $(jq -r '.version' rc.json)"; fi + [ -n "$body" ] && echo "$body" || echo "sync patches" + secrets: inherit diff --git a/.github/workflows/update-steam-wayland-overlay.yml b/.github/workflows/update-steam-wayland-overlay.yml index 8b06474..a41e89f 100644 --- a/.github/workflows/update-steam-wayland-overlay.yml +++ b/.github/workflows/update-steam-wayland-overlay.yml @@ -1,129 +1,21 @@ name: Update steam-wayland-overlay +# Thin wrapper: all logic lives in .github/workflows/module-update.yml. The +# updater opens a PR and cache-server's external build gates the merge, so this +# repo's main only advances after a green build. on: schedule: - # Own minute so this repo's updaters never push together; cron only runs on the default branch. + # GitHub may delay or skip scheduled runs, and they only run on the default branch. - cron: "35 5 * * *" workflow_dispatch: - -# contents: write covers the bot push; the app token supplies the rest (actions: write). -permissions: - contents: write - -concurrency: - # Never run two updaters at once. - group: update-steam-wayland-overlay - cancel-in-progress: false +permissions: {} jobs: update: - runs-on: ubuntu-latest - steps: - # main may be ruleset-protected and github-actions[bot] holds no role; the App token is the bypass actor. - - name: Mint app token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.DISPATCH_APP_ID }} - private-key: ${{ secrets.DISPATCH_APP_PRIVATE_KEY }} - owner: IceDOS - repositories: | - hardware - cache-server - permission-contents: write - permission-actions: write - - - uses: actions/checkout@v4 - with: - token: ${{ steps.app-token.outputs.token }} - - # update.sh sources core/lib/update-lib.sh; CI checks out core alongside this repo. - - uses: actions/checkout@v4 - with: - repository: IceDOS/core - path: .icedos-core - - - uses: nixbuild/nix-quick-install-action@v30 - with: - nix_conf: | - experimental-features = nix-command flakes - nix-path = nixpkgs=channel:nixos-unstable - - - name: Read current pin - id: before - run: echo "version=$(jq -r '.version' modules/drivers/steam-wayland-overlay/source.json)" >>"$GITHUB_OUTPUT" - - # Path-aware: update.sh rewrites source.json only when the overlay paths changed. - - name: Run steam-wayland-overlay update script - env: - GITHUB_TOKEN: ${{ github.token }} - run: nix-shell modules/drivers/steam-wayland-overlay/update.sh - - - name: Detect changes - id: changes - run: | - if git diff --quiet modules/drivers/steam-wayland-overlay/; then - echo "changed=false" >>"$GITHUB_OUTPUT" - else - echo "changed=true" >>"$GITHUB_OUTPUT" - fi - - # Compile the new pin before committing so a bad rev/hash never reaches main. - - name: Build steam-wayland-overlay (verify new pin) - if: steps.changes.outputs.changed == 'true' - run: | - nix-build --no-out-link -E 'with import {}; callPackage ./modules/drivers/steam-wayland-overlay/package.nix {}' - - - name: Commit and push - if: steps.changes.outputs.changed == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - new=$(jq -r '.version' modules/drivers/steam-wayland-overlay/source.json) - rev=$(jq -r '.rev' modules/drivers/steam-wayland-overlay/source.json) - git add -A modules/drivers/steam-wayland-overlay - git commit -m "update(steam-wayland-overlay): ${{ steps.before.outputs.version }} -> $new (${rev:0:8})" - - # Commit-then-rebase (no pull/--autostash) keeps a conflict from leaving - # stash markers; retry until this job wins the push race. - for attempt in 1 2 3 4 5; do - if push_output=$(git push origin "HEAD:$GITHUB_REF_NAME" 2>&1); then - echo "$push_output" - exit 0 - fi - echo "$push_output" - - # Retry only a lost race; anything else would bury the real error. - if ! grep -qE 'fetch first|non-fast-forward' <<<"$push_output"; then - echo "push was rejected for a reason retrying cannot fix; giving up" >&2 - exit 1 - fi - - echo "push lost a race (attempt $attempt); rebasing onto the latest $GITHUB_REF_NAME" - # Jitter so simultaneous rejects do not retry in lockstep. - sleep $(( (RANDOM % 5) + attempt )) - - # Each updater owns one module's files; a conflict aborts, never a half-rebase. - if ! git pull --rebase origin "$GITHUB_REF_NAME"; then - git rebase --abort || true - echo "rebase onto $GITHUB_REF_NAME conflicted; aborting" >&2 - exit 1 - fi - done - - echo "could not push after 5 attempts" >&2 - exit 1 - - - name: Trigger cache-server build - if: steps.changes.outputs.changed == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ steps.app-token.outputs.token }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'IceDOS', - repo: 'cache-server', - workflow_id: 'nix-build.yml', - ref: 'main', - }); + uses: ./.github/workflows/module-update.yml + with: + module: steam-wayland-overlay + paths: modules/drivers/steam-wayland-overlay + command: nix-shell modules/drivers/steam-wayland-overlay/update.sh + version-file: modules/drivers/steam-wayland-overlay/source.json + secrets: inherit diff --git a/modules/drivers/steam-wayland-overlay/source.json b/modules/drivers/steam-wayland-overlay/source.json index 14082ec..bd85735 100644 --- a/modules/drivers/steam-wayland-overlay/source.json +++ b/modules/drivers/steam-wayland-overlay/source.json @@ -1,7 +1,7 @@ { - "version": "unstable-2026-08-26", - "rev": "bf13e556a1d54c9469b9badfd7ef670301576ff2", - "hash": "sha256-qhsoqLRmDKCm8+uS+M6+tKbq+xHxNZvQ93T1HdnCc3I=", + "version": "unstable-2026-08-29", + "rev": "e841b84667c4b4336f768aabf8b533093f24276b", + "hash": "sha256-lVXTs3XQquPCiGYRqL8WZi2zyArqxHR1zlMu6kMQo/I=", "vkrootsRev": "ee76e620798612c52fb8dcc32a1058a0a3538930", "vkrootsHash": "sha256-E+8Uz3ViMMPZ1sLLz7YXbdDgDo9jCD+KR9x8TEpbECA=", "overlayPaths": [ diff --git a/modules/kernel/modules/cachyos-kernel/config.toml b/modules/kernel/modules/cachyos-kernel/config.toml index 46f35b7..53520c7 100644 --- a/modules/kernel/modules/cachyos-kernel/config.toml +++ b/modules/kernel/modules/cachyos-kernel/config.toml @@ -1,2 +1,3 @@ [icedos.hardware.kernel.cachyos] applyWithoutSubstituter = false +branch = "release" diff --git a/modules/kernel/modules/cachyos-kernel/icedos.nix b/modules/kernel/modules/cachyos-kernel/icedos.nix index 554d9cd..5035169 100644 --- a/modules/kernel/modules/cachyos-kernel/icedos.nix +++ b/modules/kernel/modules/cachyos-kernel/icedos.nix @@ -1,18 +1,32 @@ -{ lib, icedosLib, ... }: +{ + config, + lib, + icedosLib, + ... +}: + +let + cfg = config.hardware.kernel.cachyos or { }; + inherit (lib) importTOML; + defaults = (importTOML ./config.toml).icedos.hardware.kernel.cachyos; + + inherit (defaults) + applyWithoutSubstituter + branch + ; +in { - options.icedos.hardware.kernel.cachyos.applyWithoutSubstituter = + options.icedos.hardware.kernel.cachyos = let - inherit (icedosLib) mkBoolOption; - inherit (lib) importTOML; - - inherit ((importTOML ./config.toml).icedos.hardware.kernel.cachyos) - applyWithoutSubstituter - ; + inherit (icedosLib) mkBoolOption mkStrOption; in - mkBoolOption { default = applyWithoutSubstituter; }; + { + applyWithoutSubstituter = mkBoolOption { default = applyWithoutSubstituter; }; + branch = mkStrOption { default = branch; }; + }; - inputs.nix-cachyos-kernel.url = "github:xddxdd/nix-cachyos-kernel/release"; + inputs.nix-cachyos-kernel.url = "github:xddxdd/nix-cachyos-kernel/${cfg.branch or branch}"; outputs.nixosModules = { inputs, ... }: