neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Runners: a bare runs-on: self-hosted is x86_64 work only
c44c033 · Gabriel Morell · 2026-10-25 01:26
Message
{commit_body(@commit)}
Files changed
modified
lib/git_gud/workflows.ex
+23
−9
@@ -1527,10 +1527,7 @@ defmodule GitGud.Workflows do
| 1527 | 1527 | |> where([j], j.id == ^job_id) |
| 1528 | 1528 | |> where([j], j.status == "queued") |
| 1529 | 1529 | |> approved_only() |
| 1530 | − |> where( | |
| 1531 | − [j], | |
| 1532 | − fragment("(? -> 'labels') <@ to_jsonb(?::text[])", j.runs_on, ^match_labels) | |
| 1533 | − ) | |
| 1530 | + |> runs_on_matches(runner, match_labels) | |
| 1534 | 1531 | |> under_max_parallel() |
| 1535 | 1532 | |> lock("FOR UPDATE SKIP LOCKED") |
| 1536 | 1533 | |> Repo.one() |
@@ -1572,10 +1569,7 @@ defmodule GitGud.Workflows do
| 1572 | 1569 | scoped_job_query(runner) |
| 1573 | 1570 | |> where([j], j.status == "queued") |
| 1574 | 1571 | |> approved_only() |
| 1575 | − |> where( | |
| 1576 | − [j], | |
| 1577 | − fragment("(? -> 'labels') <@ to_jsonb(?::text[])", j.runs_on, ^match_labels) | |
| 1578 | − ) | |
| 1572 | + |> runs_on_matches(runner, match_labels) | |
| 1579 | 1573 | |> where([j], j.id not in ^passed_over) |
| 1580 | 1574 | |> under_max_parallel() |
| 1581 | 1575 | |> order_by([j], asc: j.id) |
@@ -1685,7 +1679,8 @@ defmodule GitGud.Workflows do
| 1685 | 1679 | # `self-hosted` is implied for an x86_64 runner only: the workflows |
| 1686 | 1680 | # that say `runs-on: self-hosted` were written when every runner was |
| 1687 | 1681 | # x86_64, and some build images that deploy to x86_64 machines. Any |
| 1688 | − # other runner takes `self-hosted` work only if its labels list it. | |
| 1682 | + # other runner needs `self-hosted` in its labels for `[self-hosted, | |
| 1683 | + # ...]` work, and never takes a bare `self-hosted` (`runs_on_matches/3`). | |
| 1689 | 1684 | # |
| 1690 | 1685 | # Built into jsonb on the SQL side from a text array rather than |
| 1691 | 1686 | # interpolated as a JSON string: a string parameter bound to a |
@@ -1697,6 +1692,25 @@ defmodule GitGud.Workflows do
| 1697 | 1692 | Enum.uniq(implied ++ labels) |
| 1698 | 1693 | end |
| 1699 | 1694 | |
| 1695 | + # A bare `runs-on: self-hosted` is x86_64 work, whatever labels another | |
| 1696 | + # runner carries: labels are fixed at registration, and an arm runner | |
| 1697 | + # needs `self-hosted` anyway for the `[self-hosted, runner-aarch64]` | |
| 1698 | + # legs written for it. | |
| 1699 | + defp runs_on_matches(query, %Runner{architecture: arch}, match_labels) do | |
| 1700 | + query = | |
| 1701 | + where( | |
| 1702 | + query, | |
| 1703 | + [j], | |
| 1704 | + fragment("(? -> 'labels') <@ to_jsonb(?::text[])", j.runs_on, ^match_labels) | |
| 1705 | + ) | |
| 1706 | + | |
| 1707 | + if arch == "x86_64" do | |
| 1708 | + query | |
| 1709 | + else | |
| 1710 | + where(query, [j], fragment("(? -> 'labels') <> '[\"self-hosted\"]'::jsonb", j.runs_on)) | |
| 1711 | + end | |
| 1712 | + end | |
| 1713 | + | |
| 1700 | 1714 | # A runner only claims jobs in its scope: |
| 1701 | 1715 | # * repo-scoped runner → jobs in that repo |
| 1702 | 1716 | # * org-scoped runner → jobs in any repo owned by that org |
modified
test/git_gud/workflows/matrix_dispatch_test.exs
+20
−3
@@ -329,11 +329,28 @@ defmodule GitGud.Workflows.MatrixDispatchTest do
| 329 | 329 | assert %WorkflowJob{job_id: "arm"} = Workflows.claim_next_job(arm) |
| 330 | 330 | end |
| 331 | 331 | |
| 332 | − test "an aarch64 runner labelled self-hosted takes self-hosted work", %{repo: repo} do | |
| 332 | + test "an aarch64 runner labelled self-hosted still leaves bare self-hosted work", %{ | |
| 333 | + repo: repo | |
| 334 | + } do | |
| 333 | 335 | self_hosted_job(repo) |
| 334 | 336 | |
| 335 | − arm = aarch64_runner(repo, ["self-hosted", "ubuntu-24.04-arm"]) | |
| 336 | − assert %WorkflowJob{job_id: "anywhere"} = Workflows.claim_next_job(arm) | |
| 337 | + arm = aarch64_runner(repo, ["self-hosted", "runner-aarch64"]) | |
| 338 | + assert Workflows.claim_next_job(arm) == nil | |
| 339 | + | |
| 340 | + run_workflow(repo, """ | |
| 341 | + name: arm | |
| 342 | + on: push | |
| 343 | + jobs: | |
| 344 | + arm: | |
| 345 | + runs-on: [self-hosted, runner-aarch64] | |
| 346 | + steps: | |
| 347 | + - run: true | |
| 348 | + """) | |
| 349 | + | |
| 350 | + assert %WorkflowJob{job_id: "arm"} = Workflows.claim_next_job(arm) | |
| 351 | + | |
| 352 | + assert %WorkflowJob{job_id: "anywhere"} = | |
| 353 | + Workflows.claim_next_job(runner(repo, ["ubuntu-latest"])) | |
| 337 | 354 | end |
| 338 | 355 | |
| 339 | 356 | test "the architecture is a label", %{repo: repo} do |
Parents: a119a0e