runner-images

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

17posts · 30d
Nov 34 posts
  1. Qiita · · @tobaru

    初めてのCI/CD。pushしただけで公開まで流れる理由を、図で理解した(GitHub Actions + Cloudflare Workers) 1. はじめに CI/CDという言葉は知っていましたが、なかなか勉強するまでには至りませんでした。それが仕事で使うことになり、とうとう勉強を始めることにしました。 とはいえ、どこから手をつければいいのか分かりません。調べると記事はたくさんあり、手順も丁寧に書かれています。ただ、どれも「こう書けば動く」が中心で、なぜpushしただけで最後まで流れるのかは、読んでもいまいち分かりませんでした。 幸い、今はAIという優秀なパートナーがいます。そこで今回は、AI(Claude Code)に助けてもらいながら、自分の環境で1つずつ検証していきました。 まず作ったのは、GitHubにpushすると、GitHub ActionsがCloudflare Workersへ自動で公開してくれるしくみです。画面で操作したのは3つ、書いたファイルも3つだけで、pushすると公開まで勝手に進んでいきます。実際に動かしてみても、やはり疑問は「何で流れるの?」、これだけでした。 この記事では、手順に加えて、なぜ最後まで自動で流れるのかを図で説明します。同じところでもやもやしている人の参考になればうれしいです。 なお、記事の内容はすべて自分の環境で動かして確かめていますが、CI/CDは初心者です。間違いなどがあれば、コメントで教えていただけるとうれしいです。 2. 作ったもの GitHubのmainブランチにpushすると、約30秒で https://test.tobaru.net のページが更新されるしくみです。ページはHTMLファイル1つだけの、いちばん小さなものにしました。 ひとことで言うと、GitHubに置いたファイルを、GitHubのロボット(Actions)がCloudflareまで運んでくれます。運ぶときにCloudflareのドアを開けるので、ロボットに合鍵(APIトークン)を預けてあります。 !04_overview.png 自分で用意したサーバーはありません。DNSのレコードとhttpsの証明書も、手では作っていません。 3. 環境 | 項目 | 内容 | |---|---| | GitHub | Freeプラン、Privateリポジトリ | | Cloudflare | Freeプラン | | ドメイン | Cloudflare Registrarで取得したtobaru.net(DNSもCloudflare) | | デプロイの道具 | cloudflare/wrangler-action@v4、wrangler 4 | | 作業したPC | Windows 11、VS Code、Git | 自分のPCにNode.jsやwranglerは入れていません。wranglerはGitHub Actionsの中でだけ動きます。 ドメインのDNSがCloudflareにあることが、後で出てくる「1行でDNSと証明書ができる」の条件になります(5章の(5))。 4. 手順 画面で操作したのは(1)〜(3)の3つだけです。(4)はファイルを書くだけ、(5)はpushするだけです。 (1) GitHubにリポジトリを作る - 名前はtobaru-net-test、Privateにしました - README・.gitignore・ライセンスは付けず、空のリポジトリにします(ローカルで作ったファイルをそのままpushするため) (2) CloudflareでAPIトークンを作る 1. 右上の人のアイコン → プロフィール → APIトークン → トークンを作成する 2. テンプレート「Cloudflare Workers を編集する」を使う 3. 範囲を絞る - アカウントリソース: 自分のアカウントだけ - ゾーンリソース: 特定のゾーン → tobaru.netだけ 4. 作成する。トークンは一度しか表示されないので、そのまま(3)に貼る 範囲を絞っておくと、万一トークンが漏れても、できることは「Workersの更新」と「tobaru.netの設定」だけです。 (3) GitHubのSecretsに登録する リポジトリの Settings → Secrets and variables → Actions → New repository secret で、2つ登録します。 | Name | 値 | |---|---| | CLOUDFLARE_API_TOKEN | (2)で作ったトークン | | CLOUDFLARE_ACCOUNT_ID | CloudflareのアカウントID | (4) ファイルを3つ書く tobaru-net-test/ ├─ public/ │ └─ index.html … 見せるページそのもの ├─ wrangler.jsonc … Cloudflareへの注文書(何を・どこで出すか) └─ .github/ └─ workflows/ └─ deploy.yml … Actionsへの指示書(いつ・何をするか) public/index.htmlは、ふつうのHTMLです(中身は省略)。 wrangler.jsoncです。ふつうのJSONはコメントが書けませんが、jsonc(JSON with Comments)は//で書けます。 { // Cloudflare側でのWorkerの名前(名札)。 // この名前のWorkerがなければ新しく作り、あれば中身を置き換える "name": "tobaru-net-test", // Workersの動き方の基準日 "compatibility_date": "2026-10-01", // 静的なファイル(HTML・画像など)の置き場所。この中身がそのままサイトに並ぶ "assets": { "directory": "./public" }, // どのURLで出すか。custom_domain: true でDNSレコードと証明書も作ってくれる "routes": [ { "pattern": "test.tobaru.net", "custom_domain": true } ], // xxx.workers.dev というCloudflareのURLでは公開しない "workers_dev": false } .github/workflows/deploy.ymlです。 name: Deploy # いつ動くか(イベントの条件) on: push: branches: [main] # mainブランチにpushされたとき workflow_dispatch: # Actionsタブの「Run workflow」ボタンで手動でも動かせる jobs: deploy: runs-on: ubuntu-latest # GitHubが数十秒だけ貸してくれるLinux permissions: contents: read steps: # 1. このcommitのファイル一式をランナーに取り出す - uses: actions/checkout@v5 # 2. wranglerをインストールして動かす(commandを書かなければ wrangler deploy) - name: Deploy to Cloudflare Workers uses: cloudflare/wrangler-action@v4 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} # 金庫の合鍵 accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} # どのアカウントに送るか wranglerVersion: "4" (5) pushする git init -b main git add -A git commit -m "最初のページとデプロイ設定" git remote add origin https://github.com/<ユーザー名>/tobaru-net-test.git git push -u origin main pushしてから約30秒で、https://test.tobaru.netが開けるようになりました。10秒おきに確かめると、最初の3回はつながらず、4回目で200が返ってきました。 5. なぜ最後まで自動で流れるのか 答えを先に書くと、何かがずっと監視しているわけではありません。「イベントが起きたら、こう動く」という小さな決まりが、前の結果を受け取ってつながっているだけです。 pushしてから公開されるまでを、5つの段階に分けました。 !05_sequence.png 画像をクリックすると、大きく表示されます。 ここからは、私が分からなかったところを順に説明します。 (1) GitHubはフォルダを監視しているの? 監視はしていません。pushやプルリクエストなどのイベントが起きたときにだけ、GitHubがそのcommitの.github/workflows/を開きます。中のYAMLを全部読んで、on:の条件に合うものだけを動かします。 それが分かる例です。 | こんなとき | デプロイは動く? | |---|---| | README.mdだけ変えてpush | 動く。deploy.ymlは変わっていないが、「mainへのpush」というイベントが起きたから | | deploy.ymlを書き換えたが、pushしていない | 動かない。GitHubは自分のPCの中を見ていない | | deploy.ymlを書き換えてpush | 書き換えた後の内容で動く。そのcommitのファイルを読むから | | 別のブランチにpush | 動かない。on:がbranches: [main]だから | | プルリクエストをmainにマージ | 動く。マージするとmainにcommitが入り、mainへのpushと同じになるから | 「受付の人がマニュアルをずっと見張っている」のではありません。「呼び鈴が鳴ったらマニュアル棚を開いて、今回の用件に当てはまるページを探す」イメージです。マニュアル棚が.github/workflows/、ページの見出しがon:です。 (2) ファイルを指定していないのに、なぜwrangler.jsoncが使われるの? deploy.ymlのデプロイの手順には、「どのファイルを送るか」を書いていません。それでも動くのは、書かなかったときの決まり(既定値)が2段階あるからです。 1. wrangler-actionの決まり: commandを書かなければdeployを実行する(action.ymlに「Defaults to "deploy"」とあります)。workingDirectoryも書かなければ、リポジトリのいちばん上で実行する 2. wranglerの決まり: wrangler deployは、実行したフォルダでwrangler.jsonc / wrangler.json / wrangler.tomlを探して読む しかも、wrangler.jsoncそのものは送られません。「何を・どこに・どんな名前で」の指示として読まれ、送られるのはassets.directoryに書いたpublic/の中身です。 決まった名前・決まった場所に置けば何も書かなくても動き、変えたいときだけ書く。この考え方を「設定より規約(Convention over Configuration)」と呼ぶそうです。(1)の.github/workflows/も同じです。 (3) Cloudflareで何も作っていないのに、なぜWorkerができるの? wrangler.jsoncの"name": "tobaru-net-test"で、「この名前のWorkerを、この中身にして」と頼んでいるからです。Cloudflareは、その名前のWorkerがなければ新しく作り、あれば中身を置き換えます。上の図のaltのところです。 どのアカウントに作るかはaccountId、入っていいかはapiTokenで伝えています。どちらもSecretsから渡しているので、deploy.ymlには値そのものを書いていません。 (4) Secretsは何のため? Secretsは合鍵を入れておく金庫です。deploy.ymlには${{ secrets.CLOUDFLARE_API_TOKEN }}と、「金庫のこの引き出しを使って」とだけ書きます。合鍵そのものはどのファイルにも書かないので、コードを人に見せても漏れません。 (5) 1行でDNSと証明書までできるの? できました。routesに"custom_domain": trueと書くと、「このホスト名をまるごとこのWorkerにする」意味になり、CloudflareがDNSのレコードとhttpsの証明書を作ってくれます。 ただし、次の2つがそろっているからできることです。 - tobaru.netのDNSが、同じCloudflareアカウントにある(Cloudflareが自分でレコードを書き込める) - APIトークンにtobaru.netを触る権限がある(4章の(2)でゾーンを絞って許可した) DNSが別の会社にあるドメインでは、この1行だけでは済みません。 custom_domainを付けずに"pattern": "test.tobaru.net/api/*"のように書くと、「このURLに来たアクセスだけWorkerが受け取る」という別の使い方になります。この場合、DNSのレコードは自分で作ります。 6. 警告が出たところ 最初は、他の記事と同じくcloudflare/wrangler-action@v3を使っていました。デプロイは成功しましたが、Actionsの結果のAnnotationsに、警告1件とお知らせ1件が出ました。 !01_annotations.png (1) Node.js 20 is deprecated(警告) > Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: cloudflare/wrangler-action@v3. wrangler-action@v3はNode.js 20向けに作られていて、GitHub側がNode.js 20を廃止していく途中なので、Node.js 24で動かしているという知らせです(GitHubのお知らせ)。今は動いていますが、将来動かなくなるおそれがあります。 調べるとv4が出ていて、action.ymlにusing: "node24"とありました。入力の項目(apiToken・accountIdなど)は同じなので、@v3を@v4に変えるだけで警告が消えました。 この修正もdeploy.ymlを1文字直してpushしただけです。しくみそのものの設定を変えた場合も、ページを変えた場合と同じ流れで反映されます(5章の(1)の表の3行目)。 (2) ubuntu-latestがUbuntu 26に移る(お知らせ) > The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. runs-on: ubuntu-latestの中身が、順に新しいUbuntuに切り替わるという案内です(runner-imagesのIssue)。今回のワークフローはwranglerを動かすだけなので、そのままにしています。 (3) nameを変えるときの注意 つまずいたわけではありませんが、しくみから考えると注意が必要だと分かったところです。 nameを変えてpushすると、Cloudflareは名前が変わったとは受け取りません。「その名前のWorkerがないので新しく作る」と受け取ります(5章の(3))。古いWorkerは残ったままになるので、変えるときは古いほうの片付けも必要です。 7. Cloudflareの画面で確かめる Cloudflareの画面では何も作っていませんが、pushしたあとに開くと、次の2つができていました。 (1) Workersの一覧 「Workers & Pages」を開くと、wrangler.jsoncのnameで付けたtobaru-net-testがあり、routesで頼んだtest.tobaru.netがつながっていました。 !02_workers.png 「デプロイメントを表示」を開くと、pushした回数ぶんの履歴が並んでいます。GitHubのcommitと1回ずつ対応しています。 (2) DNSのレコード tobaru.netのDNSのページには、自動で作られたtestのレコードがありました。自分では1つも作っていないのに、本当にレコードが出ていてびっくりしました。 !03_dns.png | 見えているもの | 意味 | |---|---| | 左端の鍵のアイコン | 自動で作られたレコードなので、ここからは編集できない | | タイプ「Worker」 | AやCNAMEではなく、「このWorkerに届けて」という特別なレコード | | コンテンツ「tobaru-net-test」 | wrangler.jsoncのname。名札でつながっている | | 「プロキシ済み」 | アクセスはいったんCloudflareが受け取る | 外のDNS(1.1.1.1)にtest.tobaru.netを聞いてみると、返ってくるのはCloudflareのIPアドレス(104.21.x.xなど)でした。「プロキシ済み」なので、サーバーの場所ではなくCloudflareのアドレスが返ります。応答のヘッダーにはServer: cloudflareとCF-RAY: …-NRTがあり、東京の拠点から返っていることも分かりました。 8. まとめ 最初は、なぜ自動で最後まで流れるのかが、いまいち理解できませんでした。でも、監視という大掛かりな仕組みではありませんでした。イベントが起きたときの動きが決まっていて、その決まりどおりに動くと、流れるように最後までつながる。それが分かったとき、美しいなーと思いました。DNSの画面に、自分では作っていないレコードが出ていたときも感動しました。 今回分かったことを並べると、次の3つです。 - GitHubは監視していない。イベントが起きたときに.github/workflows/を読み、on:に合うものだけを動かす - 書いていないことは既定値で動く。wrangler-actionはdeployを実行し、wranglerは決まった名前のwrangler.jsoncを探す - CloudflareはnameでWorkerを見分け、なければ作り、あれば置き換える。custom_domain: trueならDNSと証明書も作る 次は、プルリクエストを作ったらチェックだけを動かし、mainにマージしたらデプロイする形(CIとCDの分離)を試す予定です。チェックも、pull_requestというイベントに決まりを足すだけで作れるはずです。

    1 like
  2. macOS runners are vulnerable to the eMudhra intermediate CA compromise · Issue #15228 · actions/runner-images : 👀 --- runner-images#15228

  3. Bluesky · · @josephhall.org

    macOS + GitHub Actions: eMudhra fixes require ongoing curation. Runner images can retain vulnerable CA bundles; updating Homebrew doesn't fix Apple's curl/Python. Apple Python can also ignore CA env overrides. Test each client--and recheck after OS updates. runner-images#15228

  4. macOS + GitHub Actions: eMudhra fixes require ongoing curation. Runner images can retain vulnerable CA bundles; updating Homebrew doesn't fix Apple's curl/Python. Apple Python can also ignore CA env overrides. Test each client--and recheck after OS updates.runner-images#15228

    1 like · 102 views
  5. X · · @xwwwb11

    Github Action 镜像仓库中包含了大量的开发环境安装脚本,满足了我的收集癖。 github.com/actions/runner-images/tree/main/images… 在逛windows blogs的时候看到 python3.15版本的tier2 (jit) 支持了winonarm平台。 blogs.windows.com 然后在文章中看到了Github Action runner的镜像仓库。 进一步发现这些脚本。 t.co

    5 likes · 146 views
  6. GitHub Actions Node 20 Deprecation: What Broke on 23 September (and Why node20 Isn't the Problem) GitHub removed Node 20 from the Actions runners on 23 September 2026, and an action that declares using: node20 still doesn't fail for that: since 16 June the runner rewrites node20 to node24 before it executes anything, and it does the same with node12 and node16. What went away on the 23rd was the escape hatch: the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION variable no longer brings Node 20 back. The next date with real consequences is 2 November, when macos-14 is retired. That inverts who is at risk. If your CI has been green since June without that variable, the 23rd changed nothing for you on GitHub-hosted runners. The ones that break are precisely the teams that already hit an action that can't handle Node 24, set the variable to keep moving, and filed the real fix under later. And around that date there are four more with real consequences: the self-hosted runner minimum version (25 September), retention for checks and runs (1 October), the macos-14 brownouts (October) and its retirement (2 November). I checked this against the runner's source code and against this site's own CI, which uses two node20 actions. Everything below comes with its source. The calendar, by date | Date | What changes | What breaks | How to detect it | |---|---|---|---| | 14, 16 and 18 Sep (11:00-15:00 ET) | Brownouts ahead of the self-hosted runner minimum version | Outdated runners cannot register or run jobs during the window | Runner deprecations API (below) | | 17 Sep | ubuntu-22.04 and ubuntu-22.04-arm enter deprecation | Nothing fails yet; queues may be longer | grep for the label | | 21-30 Sep | windows-11-arm moves to the Visual Studio 2026 image | The toolchain underneath changes | grep windows-11-arm | | 23 Sep | Node 20 leaves the runners | ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION is ignored; Linux ARM32 runners lose support | grep for the variable | | 25 Sep | Self-hosted runner minimum version is enforced | Registering requires ≥ 2.329.0; a runner that doesn't install each release within 30 days stops receiving jobs | Deprecations API | | 1 Oct | Actions retention now covers checks, workflow runs and statuses | Anything past your retention period (90 days by default) is deleted | Repo/org retention setting | | 5-31 Oct | Eight macos-14 brownouts | Jobs scheduled inside the window fail | grep macos-14 | | 2 Nov | macos-14 retired | Jobs with that label terminate with an error | grep macos-14 | | 17 Apr 2027 | ubuntu-22.04 retired (brownouts before, in March and April) | Jobs terminate with an error | grep for the label | 1. Node 20: what actually happens on 23 September What the notice says GitHub's changelog post from 19 September 2025, with an editor's note dated 25 August 2026, sets two dates: from 16 June 2026 runners use Node 24 by default, and the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true opt-out back to Node 20 "will only work until we upgrade the runner and remove Node20 on September 23rd, 2026." It adds two platform limits: Node 24 is incompatible with macOS 13.4 and lower, and it has no official ARM32 support. What the notice does not explain is what happens to an action whose action.yml still says using: node20. That lives in the code. What the runner code says In HandlerFactory.cs (identical at tag v2.337.0, the current release), the runner does two things before launching a JavaScript action: 1. If the action declares node12 or node16, it bumps it to node20. 2. If it ends up on node20, it asks NodeUtil.DetermineActionsNodeVersion which version to use. That function has three phases, driven by feature flags on GitHub's side: // Phase 3: Always use Node 24 regardless of environment variables if (requireNode24) { return (Constants.Runner.NodeMigration.Node24, null); } // ... // Phase 2: Node 24 is the default if (useNode24ByDefault) { if (allowUnsecureNode) { return (Constants.Runner.NodeMigration.Node20, null); } return (Constants.Runner.NodeMigration.Node24, null); } PR #3948, which introduced this, is blunt about it: in phase 3, "All actions use Node 24" and "No opt-out options available." No branch returns an error for declaring node20. The action runs on a different Node, which is very different from not running. Note the order too: node12 → node20 → node24. An old action with using: node16 (for example actions/checkout@v3) ends up on Node 24 as well. The proof: this site's CI This blog's CI workflow uses actions/checkout@v4 and actions/setup-node@v4. The official action.yml files at those tags declare using: node20; from v5 onward they declare node24 (I checked tags v4 through v7 of both). The run on 11 September, on runner 2.337.0 with the ubuntu-24.04 image, finished green with this annotation: Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-node@v4. And in the log of those two actions' steps, this line: Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. The same annotation appears on the very first run of that workflow, on 23 August. In other words, my two node20 actions have never run on Node 20 in this CI, and I didn't touch anything to make that happen. 23 September doesn't change their runtime, because that change already happened in June. Bumping them to v5 or later clears the annotation, and it's worth doing, but there's no clock on it. What it looks like when it does break There are three real ways to fail, and none of them is a GitHub message saying "Node 20 no longer exists." You had the variable set. Some action of yours tripped on Node 24 after 16 June, you set ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true, and it stayed on Node 20. In phase 3 the runner ignores the variable without telling you: the requireNode24 branch returns null as its message. The action goes back to Node 24, and the error you see is its own code's error, the same one you saw the first time. One hint that phase 3 is live: with the current runner code, the "If you need to temporarily use Node 20…" line disappears from the log, because it is only printed while Node 24 is the default but not yet mandatory. The action uses something Node 24 removed. Per the Node 24 deprecations table and the 24.0.0 release notes, these reached end-of-life in that release: | API | Status in Node 24 | How it fails | |---|---|---| | tls.createSecurePair() | Removed (DEP0064) | TypeError: ... is not a function when called | | fs.truncate() with a file descriptor | Removed (DEP0081) | Throws ERR_INVALID_ARG_TYPE: it expects a path. Use fs.ftruncate() | | dirent.path | Removed (DEP0178) | Doesn't throw on its own: the property is gone and you read undefined. Use dirent.parentPath | | OutgoingMessage.prototype._headers / _headerNames | Removed (DEP0066) | You read undefined. Use getHeaders() | dirent.path is the sneakiest one on the list, because reading it throws nothing: the failure shows up later, wherever that path gets used, or never, if it's only concatenated into a string and comes out as undefined/file. If you maintain an action that walks directories, search for that property before you trust the green check. Your self-hosted runner can't run Node 24. On Linux ARM32, the same NodeUtil.cs has a kill switch that fails the step with: Linux ARM32 runners are no longer supported. Please migrate to a supported platform. Before it's flipped, the runner keeps those actions on Node 20 with a warning whose default date is the Node 20 removal date: "Linux ARM32 runners are deprecated and will no longer be supported after September 23rd, 2026." GitHub hasn't published the exact day the switch flips; the changelog only says ARM32 loses support after the Node 20 deprecation. On macOS, Node 24's BUILDING.md requires macOS 13.5 or later. 2. The audit: what to look for and where In one repository Run this from the root: # 1. The switch that stops working on 23 September grep -rn 'ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION' .github/ # 2. Image labels with a date. No "runs-on:" on purpose, # so matrices show up too (os: [ubuntu-22.04, ...]) grep -rnE 'ubuntu-22\.04|macos-14|windows-11-arm' .github/ # 3. Your own or local actions that declare an old Node grep -rnE --include=action.yml --include=action.yaml \ "using:[[:space:]]*['\"]?node(12|16|20)" . Search 2 has a blind spot: runs-on: ${{ vars.RUNNER }} or a label passed as an input to a reusable workflow won't show up in any grep. For those, the log is the source of truth. The "Set up job" step prints the real image: gh run view <run-id> --log | grep -m1 'Image:' # Image: ubuntu-24.04 If the matrix is long or nested, converting it to JSON and filtering it with jq is usually faster than reading the YAML by eye: > Free tool: JSON and YAML Converter — Convert JSON to YAML and YAML to JSON instantly, with configurable indentation, download and copy. All in your browser, nothing uploaded. To find out which runtime each third-party action you use declares, including SHA-pinned ones, you have to read its action.yml at that exact ref. This script does it with gh: grep -rhoE "uses:[[:space:]]*['\"]?[^[:space:]'\"#]+@[^[:space:]'\"#]+" .github/ \ | sed -E "s/uses:[[:space:]]*['\"]?//" | sort -u \ | while read -r ref; do spec=${ref%@*}; ver=${ref#*@} case "$spec" in ./*|docker://*) continue ;; esac repo=$(cut -d/ -f1-2 <<<"$spec"); sub=$(cut -d/ -f3- <<<"$spec") for f in action.yml action.yaml; do rt=$(gh api "repos/$repo/contents/${sub:+$sub/}$f?ref=$ver" --jq .content 2>/dev/null \ | base64 -d | grep -E '^[[:space:]]*using:' | tr -d " '\"" | cut -d: -f2) [ -n "$rt" ] && { echo "$rt $ref"; break; } done done On this repo it prints: node20 actions/checkout@v4 node20 actions/setup-node@v4 It handles subpaths (github/codeql-action/init@v3) and SHAs. A SHA pointing at a node20 release doesn't fail on the 23rd, for the same reason @v4 doesn't: the runner forces it onto Node 24. What's different with a SHA is how it gets updated: nothing moves on its own, so Dependabot or a human has to bump it. One warning before bumping a major: read the release notes. actions/setup-node v5 turned on automatic caching when package.json has a packageManager field, and v6 limited it to npm. If you already pass cache: npm explicitly, as my CI does, it doesn't affect you. Across an organization GitHub code search reaches every repo at once: gh search code --owner YOUR_ORG 'ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION' gh search code --owner YOUR_ORG 'ubuntu-22.04 path:.github/workflows' gh search code --owner YOUR_ORG 'macos-14 path:.github/workflows' gh search code --owner YOUR_ORG node20 --filename action.yml Two limits of the code search API that will bite you: it only searches the default branch (a workflow on a release branch won't show up) and it allows 10 requests per minute. While preparing this post I hit HTTP 403: API rate limit exceeded as soon as I chained a handful of searches; add a sleep between them if you automate it. 3. Self-hosted runners: two dates in three days If you only use GitHub-hosted runners, skip this section. If you run your own, 25 September matters more than the 23rd. The 12 June changelog post resumes enforcement of two rules on github.com: - To register (or re-register) a runner you need version 2.329.0 or later. - To keep running jobs, the runner must install each new release within 30 days of its publication. "A runner pinned to 2.329.0 that never updates again will not pick up jobs." Full enforcement begins on 25 September 2026 (the notice's table labels that date as GitHub Enterprise Cloud, and the same notice states the change applies to github.com), with brownouts first: on 14, 16 and 18 September, from 11:00 AM to 3:00 PM Eastern, out-of-date runners can neither register nor run jobs. What you see, per the notice, is that "workflows targeting unsupported runners may remain queued or fail." The way to find out when your version expires is an endpoint GitHub added on 3 September. I queried it today: gh api repos/OWNER/REPO/actions/runners/deprecations/2.335.1 # {"runner_version":"2.335.1","runtime_deprecates_at":"2026-09-24T15:30:55Z"} gh api repos/OWNER/REPO/actions/runners/deprecations/2.337.0 # {"runner_version":"2.337.0","runtime_deprecates_at":null} A runner on 2.335.1 falls out of support on 24 September, so by the time full enforcement starts on the 25th it no longer gets jobs. You get the installed version on the machine itself with ./config.sh --version. The self-hosted checklist, in order: 1. Update the runner to the current release and leave auto-update on. If it's off, you need a manual cadence shorter than 30 days. 2. Rebuild the images, VM templates and containers you create runners from. The changelog asks you to recreate runners built from older cached images or templates: if the image ships a runner older than 2.329.0, it won't register. 3. Linux ARM32: move to arm64. Node 24 has no official support there, the runner's linux-arm package only bundles the Node 20 binary (externals.sh), and the code that fails the step is already in the runner. 4. macOS 13.4 or earlier: upgrade the OS. GitHub lists Node 24 as incompatible with those versions. 5. Actions that already declare node24 (such as actions/checkout@v5) require runner v2.327.1 at minimum. If you've done step 1, you already have it. 4. Retention, 1 October: history gets deleted This change breaks no build, but it deletes data. According to the 27 August changelog post, starting 1 October 2026 checks, workflow runs and statuses follow the same retention setting that already governed artifacts and logs, 90 days by default. Until now they were kept for "400+ days" regardless of your configuration. - For public repositories the maximum is 90 days. - The change is not retroactive: raising retention later won't restore anything. - Check and workflow run metadata isn't billed as storage; the associated artifacts and logs are. What breaks is anything that reads that history beyond your period: deployment metrics computed from gh run list, audits that link to a specific run, quarterly reports. Export what you need before 1 October. What the changelog does not say: what happens to a pull request open for more than 90 days whose required check expires. I couldn't find it documented. If you have long-lived PRs under branch protection, look at them after 1 October before assuming anything. 5. Images: macos-14 has a date, ubuntu-22.04 doesn't yet macos-14 (actions/runner-images#13518): deprecation started on 6 July and support ends on 2 November 2026. Before that there are eight brownouts during which jobs fail: 5, 12, 16, 19, 23, 26, 29 and 30 October, each from 14:00 UTC to 00:00 UTC the next day. It covers macos-14, macos-14-large and macos-14-xlarge. The replacement is macos-15, macos-26 or macos-latest, which started pointing to macOS 26 with the migration that began on 15 June. ubuntu-22.04 (actions/runner-images#14254): on 17 September deprecation only begins. The issue warns about longer queue times at peak hours, not failures. Retirement is on 17 April 2027, with brownouts that the issue schedules for March and April (no year given, but they fall before the retirement). It also covers ubuntu-22.04-arm. The replacement is ubuntu-24.04, ubuntu-26.04 or ubuntu-latest. If someone tells you ubuntu-22.04 breaks this week, it doesn't. Migrate calmly, but don't leave it for March. What's in the changelog and breaks nothing - cache-mode (10 September): a new key to limit cache access per workflow or job. It's opt-in: workflows that don't set it "continue to use the existing secure defaults." - The separate Code Quality path (20 August): only affects you if you have reports filtering on dynamic/github-code-scanning/codeql or on the github-advanced-security actor. - actions/checkout and pull_request_target (18 June): this one does break, but it already happened. Since 20 July the floating tags, @v4 included, refuse to check out fork code in pull_request_target. If your workflow depended on that, it's already red. It's the same family of risk I covered in the worm that got in through npm trusted publishing. What I'm doing with my CI Nothing urgent. actions/checkout@v4 and actions/setup-node@v4 have been running on Node 24 since June, the workflow doesn't use the opt-out variable, it runs on ubuntu-latest (currently ubuntu-24.04) and on GitHub-hosted runners. 23 September changes nothing for it. I am going to bump both actions to their current major, for two reasons unrelated to the date: a permanent yellow annotation trains you to ignore annotations, and an action its authors no longer test on the runtime it actually runs on is debt. If you're also thinking about your own project's Node rather than the actions', I covered that in what breaks when upgrading to Node 26. Frequently asked questions What happens to GitHub Actions on 23 September 2026? GitHub removes Node 20 from the runners. Since 16 June 2026 runners had already been running actions on Node 24 by default, and ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true let you go back to Node 20. From 23 September that variable stops working and every JavaScript action runs on Node 24. Self-hosted runners on Linux ARM32 lose support, because Node 24 has no official support for that platform. Does an action that declares using: node20 fail after 23 September? Not for declaring it. The runner code (HandlerFactory.cs and NodeUtil.cs in actions/runner) rewrites node12 and node16 to node20, and node20 to node24; in the final phase it ignores the environment variables and always picks Node 24, without returning an error. The action fails if its code uses something Node 24 removed, such as tls.createSecurePair or fs.truncate with a file descriptor, or if it runs on a self-hosted Linux ARM32 runner or on macOS 13.4 or earlier. The logs show the annotation: The following actions target Node.js 20 but are being forced to run on Node.js 24. Do I have to update actions/checkout@v4 and actions/setup-node@v4? It's a good idea, but no date forces it. The v4 tags of both declare node20 and v5 onward declare node24. On GitHub-hosted runners, the v4 tags have already been running on Node 24 since June and keep working. When bumping setup-node, read the release notes: v5 turned on automatic caching when package.json has packageManager, and v6 limited it to npm. How do I find affected workflows across my organization? With GitHub code search: gh search code --owner YOUR_ORG with ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION, with ubuntu-22.04 path:.github/workflows, with macos-14 path:.github/workflows, and with node20 --filename action.yml for your own actions. The API only searches the default branch and allows 10 requests per minute. Labels that come from variables or expressions don't show up in any search: for those, check the Image: line of the Set up job step in the log. Does ubuntu-22.04 break on 17 September 2026? No. On 17 September deprecation only begins, and the notice talks about longer queue times at peak hours. Jobs start failing during the brownouts that issue 14254 in actions/runner-images schedules for March and April, before the image is retired on 17 April 2027. The one with a date this fall is macos-14: brownouts in October and retirement on 2 November 2026. What do self-hosted runners need to do before 25 September? Update the runner. From 25 September 2026 GitHub requires version 2.329.0 to register a runner, and each new release must be installed within 30 days to keep receiving jobs. There are brownouts first on 14, 16 and 18 September, from 11:00 AM to 3:00 PM Eastern. The endpoint GET /repos/OWNER/REPO/actions/runners/deprecations/VERSION returns when your version stops running jobs. Also rebuild the images you create runners from. What changes with GitHub Actions retention on 1 October 2026? Checks, workflow runs and statuses start following the artifact and log retention setting, which defaults to 90 days; before, they were kept for more than 400 days. For public repositories the maximum is 90 days, and the change is not retroactive. If you have metrics or audits that read old runs, export the data before that date. --- Originally published on ortamarco.me.

  7. Qiita · · @cauchye_asia

    systemd-analyze verify はタイプミスでも exit 0|--recursive-errors でunitファイルの警告をCIで落とす TL;DR - systemd-analyze verify は、Restrat= のような未知のキーや Restart=on-failur のような不正な値を警告するだけで、終了コードは 0 です。これは不具合ではなく、systemd-analyze(1) に書かれた仕様です。 - systemd 250 以降は --recursive-errors=no を付けると、対象のunitに警告が出た時点で exit 1 になります。CIではまずこれを使います。 - ただし手元の実測では、=no にすると Requires= の先が存在しないエラーが出力ごと消えて exit 0 になりました。依存先の欠落も止めたいなら --recursive-errors=one を使います。=one なら依存先も検証されます。 - systemd 250 より古い環境(例: GitHub ホストランナーの ubuntu-22.04 は 249)や、出力を1行でも許したくない場合は、「出力が空でなければ失敗」にするラッパーを使います。 検証環境: Debian 13 のコンテナ、systemd 257 (257.13-1~deb13u1)。以下のログはすべてこの環境で実行した結果をそのまま貼っています。 再現:タイプミスがあっても exit 0 検証用のファイルはすべて /tmp/qiita-verify/units/ に置き、/tmp/qiita-verify から相対パスで実行しています。 [Unit] Description=demo agent [Service] ExecStart=/bin/true Restrat=on-failure RestartSec=10s $ cd /tmp/qiita-verify $ systemd-analyze verify --man=no units/typo.service; echo exit=$? /tmp/qiita-verify/units/typo.service:6: Unknown key 'Restrat' in section [Service], ignoring. exit=0 相対パスで渡しても、警告には絶対パスと行番号が出ます。Restart= は書かれていない扱いになるので、プロセスが落ちても再起動しません。それでも verify は成功扱いです。 再起動の設定はZennの記事を参照してください。 値の綴りを間違えた場合も同じです。 [Unit] Description=demo agent [Service] ExecStart=/bin/true Restart=on-failur RestartSec=10s $ systemd-analyze verify --man=no units/badvalue.service; echo exit=$? /tmp/qiita-verify/units/badvalue.service:6: Failed to parse Restart=on-failur, ignoring: Invalid argument exit=0 原因:オプションなしでは警告があっても 0 を返す仕様 systemd-analyze(1) の --recursive-errors=MODE の項には、次のように書かれています。 > If this option is not specified, zero is returned as the exit status regardless whether warnings arise during verification or not. つまり、オプションを付けない限り、警告がいくつ出ても終了コードは 0 です。未知のキーや不正な値は「その行を無視して読み込みを続ける」扱いの警告なので、ここに当てはまります。 同じ項で、モードごとの意味は次のとおり説明されています(Added in version 250)。 | MODE | 非0で終わる条件(マニュアルの記述) | |---|---| | yes | 指定したunitか、その依存先のどれかで警告が出たとき | | no | 指定したunitだけの検証で警告が出たとき | | one | 指定したunitか、その直接の依存先で警告が出たとき | 一方で、ExecStart= のコマンドが存在しない場合は、unitとして読み込めない「エラー」になるので、オプションがなくても exit 1 になります。セクション名の誤りは、どのセクションかで結果が分かれます。[Service] のセクション名を誤ると、その中身がまるごと無視されて ExecStart= がない扱いになり exit 1 です。[Unit] や [Install] の誤りは、ほかの警告と同じく exit 0 で、--recursive-errors=no を付けて初めて exit 1 になります。 flowchart TD A[unitファイル] --> B[systemd-analyze verify --man=no] B -->|読み込めないエラー<br>ExecStart不在・Serviceのセクション名の誤り| E1[exit 1] B -->|警告のみ<br>未知のキー・不正な値・Unit/Installのセクション名の誤り| C{--recursive-errors} C -->|指定なし| E0[exit 0 で素通り] C -->|=no / =one / =yes| E2[exit 1] B -->|Requires= の先が存在しない| D{--recursive-errors} D -->|指定なし / =one / =yes| E3[exit 1] D -->|=no| E4[出力なしで exit 0] E0 -.->|systemd 250 未満| W[ラッパー: 出力が空でなければ NG] 対策1:--recursive-errors=no を付ける(systemd 250 以降) $ systemd-analyze verify --man=no --recursive-errors=no units/typo.service; echo exit=$? /tmp/qiita-verify/units/typo.service:6: Unknown key 'Restrat' in section [Service], ignoring. exit=1 $ systemd-analyze verify --man=no --recursive-errors=no units/badvalue.service; echo exit=$? /tmp/qiita-verify/units/badvalue.service:6: Failed to parse Restart=on-failur, ignoring: Invalid argument exit=1 出力は同じで、終了コードだけが 1 に変わります。複数のファイルをまとめて渡しても、1つでも警告があれば 1 になりました。 $ systemd-analyze verify --man=no --recursive-errors=no units/good.service units/typo.service; echo exit=$? /tmp/qiita-verify/units/typo.service:6: Unknown key 'Restrat' in section [Service], ignoring. exit=1 --man=no は、Documentation= に書いた man ページの存在確認をしないためのオプションです。この検証環境のように man コマンドが入っていない環境では、Documentation=man:demo-agent(8) のようなunitで付けないと Command 'man demo-agent(8)' failed with code 1 で exit 1 になりました。 =no では Requires= の欠落が見えなくなる 依存先のunitが存在しないケースを試すと、=no だけ結果が変わりました。 [Unit] Description=demo agent Requires=missing-dep.service After=missing-dep.service [Service] ExecStart=/bin/true Restart=on-failure $ systemd-analyze verify --man=no units/requires.service; echo exit=$? requires.service: Failed to create requires.service/start: Unit missing-dep.service not found. exit=1 $ systemd-analyze verify --man=no --recursive-errors=no units/requires.service; echo exit=$? exit=0 $ systemd-analyze verify --man=no --recursive-errors=one units/requires.service; echo exit=$? requires.service: Failed to create requires.service/start: Unit missing-dep.service not found. exit=1 オプションなしなら出るエラーが、=no では出力ごと消えて exit 0 になります。これは実装どおりの挙動で、systemd v257 の src/analyze/analyze-verify-util.c では、=no のときに MANAGER_TEST_RUN_IGNORE_DEPENDENCIES を付けて検証用のマネージャーを起動しており、依存先を読みに行きません。自分のunitの書き間違いだけを見たいなら =no、依存先の欠落も同時に止めたいなら =one を使うのが安全です。ただし CI で =one を使うなら、依存先のunitもランナー上に存在していることが前提です。たとえば Requires=postgresql.service と書いたunitは、ランナーに PostgreSQL のunitがなければ、記述が正しくても exit 1 になります。この環境では、Wants=network-online.target を持つunitでも =one・=yes で余計な警告は出ませんでした(exit 0)。 誤りの種類ごとの終了コード systemd 257 で、誤りを1つだけ入れたunitを用意して測った結果です(--man=no は共通)。root と一般ユーザーの両方で実行し、結果は同じでした(/run/systemd が存在する環境での結果。systemd が動いていないコンテナで一般ユーザーが実行すると、Failed to create directory '/run/systemd/': Permission denied で exit 1 になることがあります。手元で /run を空の tmpfs に差し替えて uid 1000 で試したところ、正しいunitでもこのエラーで exit 1 になり、XDG_RUNTIME_DIR を設定しても変わらず、root で実行した場合だけ exit 0 でした)。 | 誤りの種類 | 出力 | オプションなしの終了コード | --recursive-errors=noの終了コード | (参考)=one | |---|---|---|---|---| | 未知のキー(Restrat=) | Unknown key 'Restrat' in section [Service], ignoring. | 0 | 1 | 1 | | 不正な値(Restart=on-failur) | Failed to parse Restart=on-failur, ignoring: Invalid argument | 0 | 1 | 1 | | 旧名のキー StartLimitInterval= を [Service] に書く | なし | 0 | 0 | 0 | | 新名の StartLimitIntervalSec= を [Service] に書く | Unknown key 'StartLimitIntervalSec' in section [Service], ignoring. | 0 | 1 | 1 | | [Service] のセクション名の誤り([Servcie]) | Unknown section 'Servcie'. Ignoring. と Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing. ほか1行 | 1 | 1 | 1 | | [Unit] / [Install] のセクション名の誤り([Unti] / [Instal]) | Unknown section 'Unti'. Ignoring.([Instal] も同様) | 0 | 1 | 1 | | ExecStart= のコマンドが存在しない | Command /opt/demo/bin/agent is not executable: No such file or directory | 1 | 1 | 1 | | Requires= の先が存在しない | オプションなし: Unit missing-dep.service not found./=no: なし | 1 | 0 | 1 | 旧名と新名で結果が逆になる 表の3行目と4行目は紛らわしいので補足します。StartLimitIntervalSec= は [Unit] セクションのキー(systemd.unit(5))なので、[Service] に置くと未知のキーとして無視されます。一方、旧名の StartLimitInterval= は互換のため [Service] でも受け付けられ、systemd v257 のソース(src/core/load-fragment-gperf.gperf.in)でも Service.StartLimitInterval は [Unit] 側と同じ設定値に割り当てられています。つまり旧名は「警告なしで通る」のが正しい挙動で、verify では誤りとして検出できません。新しく書くunitは、新名を [Unit] に書くのに統一しておくと迷いません。 修正後の good.service キーの綴りと値を直し、StartLimitIntervalSec= / StartLimitBurst= を [Unit] に置いたものです。 [Unit] Description=demo agent StartLimitIntervalSec=300 StartLimitBurst=5 [Service] ExecStart=/bin/true Restart=on-failure RestartSec=10s [Install] WantedBy=default.target $ systemd-analyze verify --man=no --recursive-errors=no units/good.service; echo exit=$? exit=0 $ systemd-analyze verify --man=no --recursive-errors=one units/good.service; echo exit=$? exit=0 何も出力されず、exit 0 です。 対策2:出力が空でなければ失敗にするラッパー(systemd 250 未満、または警告を厳しく扱いたい場合) --recursive-errors= は systemd 250 で追加されたオプションです。GitHub のホストランナーのイメージ情報(actions/runner-images、2026-10-12 版)では、ubuntu-24.04 の systemd は 255.4、ubuntu-22.04 は 249.11 と記載されています。ubuntu-latest は現時点で ubuntu-24.04 を指していますが、向き先は新しいイメージの提供にあわせて順次変わるので、バージョンに依存するならラベルを固定しておくと安心です。249 の環境ではこのオプションが使えないので、終了コードに加えて「出力が1行でもあれば失敗」と判定します。警告の文言はバージョンで変わり得るので、文言ではなく出力が空かどうかで見ています。 #!/usr/bin/env bash # systemd-analyze verify を unit ごとに実行し、終了コードが 0 以外か、出力が1行でもあれば失敗にする set -uo pipefail if [ "$#" -eq 0 ]; then echo "usage: $0 UNIT_FILE..." >&2 exit 2 fi rc=0 for f in "$@"; do out="$(systemd-analyze verify --man=no "$f" 2>&1)"; st=$? if [ "$st" -ne 0 ] || [ -n "$out" ]; then echo "NG: $f (exit=$st)"; printf '%s\n' "$out" | sed 's/^/ /'; rc=1 else echo "OK: $f" fi done exit "$rc" $ ./bin/verify-units.sh units/good.service units/typo.service units/requires.service; echo exit=$? OK: units/good.service NG: units/typo.service (exit=0) /tmp/qiita-verify/units/typo.service:6: Unknown key 'Restrat' in section [Service], ignoring. NG: units/requires.service (exit=1) requires.service: Failed to create requires.service/start: Unit missing-dep.service not found. exit=1 typo.service は verify 自体は exit 0 ですが、出力があるので NG になります。オプションを付けていないので、Requires= の欠落も拾えます。 引数が0件のまま成功してしまうと、CIでファイルの指定を間違えたときに気づけません。そのため0件は使い方を表示して exit 2 にしています。 $ ./bin/verify-units.sh; echo exit=$? usage: ./bin/verify-units.sh UNIT_FILE... exit=2 deploy/*.service のようにglobで渡して1件も一致しなかった場合は、bash の既定ではパターンがそのまま渡り、verify 側が失敗するのでこちらも NG になります。 $ ./bin/verify-units.sh deploy/*.service; echo exit=$? NG: deploy/*.service (exit=1) Failed to prepare filename deploy/*.service: Invalid argument exit=1 まとめ - systemd-analyze verify は、オプションなしでは警告があっても exit 0。マニュアルに明記された仕様です。 - systemd 250 以降は --recursive-errors=no で、未知のキー・不正な値を exit 1 にできます。 - =no では Requires= の先の欠落が出力ごと消えます(依存先を読まない実装)。依存先も見るなら =one。 - セクション名の誤りは、[Service] なら exit 1、[Unit] や [Install] なら警告扱いで exit 0。 - 旧名の StartLimitInterval= は [Service] でも有効なキーなので、verify では検出されません。 - systemd 250 未満では、出力が空でなければ失敗にするラッパーで代用します。 参考 - systemd-analyze(1): freedesktop.org - systemd.unit(5): freedesktop.org - systemd v257 src/core/load-fragment-gperf.gperf.in: github.com/systemd/systemd/blob/v257/src/core/loa… - systemd v257 src/analyze/analyze-verify-util.c: github.com/systemd/systemd/blob/v257/src/analyze/… - actions/runner-images Ubuntu 24.04: github.com/actions/runner-images/blob/main/images… - actions/runner-images Ubuntu 22.04: github.com/actions/runner-images/blob/main/images…

  8. Reddit · · r/azuredevops · u/ADringer

    Anyone had any luck with GitHub hosted agents? Hi all, I'm investigating building to arm64 as it will be cheaper to run, and so looking at the GitHub hosted agents as they provide arm vms as oppsed to Microsoft agents that don't. I've updated my pipeline to the following:   pool:     name: 'GitHub-hosted Agents'     vmImage: 'ubuntu-24.04-8-core' But am getting the error: >No agent found in pool GitHub-hosted Agents which satisfies the specified demands: Agent.Version -gtVersion 2.199.0. Is that the actual agent software? I would have thought that would be preinstalled if it's provided as an agent? Also, I can see GitHub runners have a list of agents listed here - is this list different from what DevOps has access to? Thanks!

    3 points · 2 comments
  9. How to tell if the ubuntu-latest 26.04 migration broke your build GitHub is moving the ubuntu-latest label from Ubuntu 24.04 to Ubuntu 26.04. The rollout is gradual and runs between October 19 and November 19, 2026 (GitHub changelog, runner-images announcement #14748). Because it is gradual, one run can land on 24.04 and the next on 26.04, with no change in your repo. If a build that was green yesterday is red today, this is how to check whether the image is the cause. 1. Check which image the job actually ran on Open the failed run and expand the Set up job step. You will see something like: Runner Image Image: ubuntu-26.04 Version: 20261012.163.1 Compare it with the last green run. If the green run says ubuntu-24.04 and the red one says ubuntu-26.04, you have your prime suspect. The same values are available as environment variables in every job, so you can print them explicitly: - run: echo "ImageOS=$ImageOS ImageVersion=$ImageVersion" && lsb_release -ds ImageOS is ubuntu24 or ubuntu26. Logging this in every job makes later debugging much faster. 2. Confirm by pinning (the fastest test) Re-run the same commit with the old image: runs-on: ubuntu-24.04 # instead of ubuntu-latest - Green on 24.04 and red on 26.04 means the image change is the cause. - Red on both means the cause is something else (a dependency release, a flaky test, your own change). Pinning to ubuntu-24.04 is also the officially suggested escape hatch if you are not ready to move. Treat it as a temporary pin, not a permanent fix. To test ahead of time, add a matrix leg: strategy: matrix: os: [ubuntu-24.04, ubuntu-26.04] runs-on: ${{ matrix.os }} 3. Diff the installed software Each image has a software list in actions/runner-images: Ubuntu2404-Readme.md and Ubuntu2604-Readme.md. Diff them for the tools your build uses, and check the releases page for the exact ImageVersion you got. Differences we saw when comparing the two READMEs on 2026-10-20 (always re-check against the current files): | Area | Ubuntu 24.04 | Ubuntu 26.04 | |---|---|---| | Default Python | 3.12 | 3.14 | | Default Node.js | 22 | 24 | | Ruby / PHP / Perl | 3.2 / 8.3 / 5.38 | 3.3 / 8.5 / 5.40 | | Clang | 16–18 | 20–22 | | OpenSSL | 3.0 | 3.5 | | Docker client | 28 | 29 | | Kernel | 6.x azure | 7.0 azure | | Java 8 (JAVA_HOME_8_X64) | present | not listed | | Not listed on 26.04 | | Fastlane, Julia, Lerna, Mercurial, Miniconda, Newman, Parcel, Pulumi, MediaInfo, Haveged, Sphinx search server | 4. Common breakages and quick fixes - "command not found" for a tool that used to be preinstalled: install it explicitly (setup-* action, apt-get install, pip/npm install) instead of relying on the image. - Python 3.14 / Node 24 default changes: pin the version with actions/setup-python / actions/setup-node rather than using whatever the image ships. Watch for removed stdlib/deprecated APIs and native modules that have no wheels/prebuilds yet. - Compiler upgrades (newer GCC/Clang): new warnings become errors under -Werror; old code may need flag changes. - OpenSSL 3.5: legacy algorithms or old TLS settings in tests can fail. - Java 8 missing: use actions/setup-java with java-version: 8. - apt package names changed or removed: apt-get install fails with "Unable to locate package". Check the package name for 26.04 (resolute). - Kernel / system changes: low-level things (eBPF, ACLs, FUSE, container runtimes) can behave differently. One example reported on day 2 of the rollout: setfacl fails on Ubuntu 26.04 (#15004). - Cache keys: if your cache key does not include the OS version, a 24.04 cache restored on 26.04 can break native builds. Include ${{ runner.os }}-${{ env.ImageOS }} in the key. If you find a real regression in the image, search and report it in actions/runner-images issues with the ImageVersion from step 1. 5. Tell "it's the image" from "it's just me" across repos The hard part in a gradual rollout is knowing whether a red build is your problem or everybody's. Disclosure: I built a small free tool for this, CI Early-Warn. A GitHub Action reports minimal job metadata (OS, image version, pass/fail, failing-step category; no code, logs or repo names). When failures across many repos jump on a new image version, it raises an alert, and says "ambiguous" when it can't tell the cause. It is early and only useful with enough participants, so steps 1–4 above are the reliable method. TL;DR 1. Look at Set up job → Runner Image (or $ImageOS / $ImageVersion). 2. Re-run with runs-on: ubuntu-24.04. 3. Diff the runner-images READMEs for the tools you use. 4. Install/pin what you depend on explicitly, then move to 26.04 on your schedule. Free CI cost audit: email your GitHub Actions workflow file(s) to conformance@mail.grokbot.com (subject "CI audit") and I'll reply within 2 business days with a short report on wasted runner minutes and concrete fixes. Workflow YAML only, no secrets or code; files deleted after 30 days.

  10. YouTube · · YouTube description · Doro夫子

    GitHub Ubuntu 遷移今起:latest 吊牌沒變,箱子換了|10/19 GitHub在2026/9/17公告,GitHub-hosted Ubuntu runner的ubuntu-latest標籤將於2026/10/19至11/19逐步由Ubuntu24.04遷移至26.04。今天是窗口起點,不是當天全部工作流程都已切換。 latest名字不變,不等於底下的作業系統、工具與版本都不變。官方提醒依賴特定工具/套件/版本的build可能受影響,建議先用ubuntu-26.04測自己的流程;還未準備好時,可先固定ubuntu-24.04暫留舊image。這不是永久支援承諾,也不是替個人電腦自動升級。 官方來源:github.blog 工具差異:runner-images#14747 本片為Doro原創中文評論。行李廳/輸送帶/吊牌/工具插槽是原創空間比喻,不是GitHub介面、真實build失敗或工具效能測試;方塊不合僅表達相容性差異,不代表所有流程必壞。官方原文為實際瀏覽器短引,AI旁白、本地合成音效。 #GitHub #Ubuntu #科技新聞 #Doro夫子

    101 views

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

Scan pending.

README badges

Embed a badge in your README or website.

Live mentions · last 30 days
actions/runner-images mentions in the last 30 days