From 168224cfde807a2d1628c5a6533ba954352f3c1f Mon Sep 17 00:00:00 2001 From: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:29:40 -0700 Subject: [PATCH 1/2] Replace the removed String.write static call with the String constructor Mojo's stdlib made String.write an instance method -- both overloads now take `mut self` -- so the old static form no longer resolves and the test module fails to parse: test/test_errors.mojo:15:18: error: no matching function in call to 'write' note: candidate not viable: value passed to mutable argument 'self' must be mutable String.__init__ takes a variadic pack of Writable arguments, and Error conforms to Writable, so String(e) is the direct replacement and produces the same bytes. Nothing in src/ used the old form; this is test-only. Co-Authored-By: Claude Opus 5 (1M context) --- test/test_date.mojo | 2 +- test/test_errors.mojo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_date.mojo b/test/test_date.mojo index e51bd2a..edf695b 100644 --- a/test/test_date.mojo +++ b/test/test_date.mojo @@ -64,7 +64,7 @@ def test_epoch() raises: def test_writable_roundtrip() raises: var d = parse_date("2026-07-03T01:02:03-07:00") - assert_equal(String.write(d), "2026-07-03T01:02:03-07:00") + assert_equal(String(d), "2026-07-03T01:02:03-07:00") def test_item_date_method() raises: diff --git a/test/test_errors.mojo b/test/test_errors.mojo index fe0944d..4d622df 100644 --- a/test/test_errors.mojo +++ b/test/test_errors.mojo @@ -12,7 +12,7 @@ def _assert_lc(source: String, offset: Int, line: Int, col: Int) raises: def _msg(e: Error) -> String: - return String.write(e) + return String(e) # -------------------------------------------------------------------------- From 61699f7e78bf85c1adc3cc8f16afb274efcae58c Mon Sep 17 00:00:00 2001 From: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:46:20 -0700 Subject: [PATCH 2/2] Pin CI to a known-good Mojo nightly; check drift weekly Both workflows installed `mojo` from the nightly index unpinned, so every run resolved whatever nightly was current. That is why this repo went green on 7/29 and red on 7/31 with no commits in between -- a frozen library's CI decayed on its own, and the breakage surfaced on an unrelated docs PR. CI now installs exactly the nightly the repo is known to pass on. Upstream churn can no longer turn a green repo red without a commit. Pinning alone would trade one problem for a worse one: silence. So test.yml also runs weekly against the LATEST nightly. That run is advisory -- it fires on main, so it never gates a PR -- and on failure it opens (or comments on) a drift issue, because a red X on the Actions tab of a repo nobody is watching is not a signal. The weekly check reuses test.yml rather than living in its own file. A separate workflow would have to duplicate this repo's bespoke test steps and would drift out of sync with them. docs.yaml is pinned but not scheduled -- it deploys Pages, and a weekly redeploy is not wanted. `mojo doc` compiles the sources, so the test job catches the same stdlib breakage anyway. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/docs.yaml | 9 ++++-- .github/workflows/test.yml | 60 ++++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 3de42ac..38bd3cc 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -14,6 +14,11 @@ concurrency: group: pages cancel-in-progress: true +env: + # Keep in step with test.yml's pin -- docs runs `mojo doc`, which compiles the + # sources and so breaks on the same stdlib changes the tests do. + MOJO_VERSION: "1.0.0b3.dev2026073014" + jobs: build-deploy: runs-on: ubuntu-latest @@ -27,10 +32,10 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 - - name: Install Mojo nightly + - name: Install Mojo run: | uv venv - uv pip install mojo \ + uv pip install "mojo==$MOJO_VERSION" \ --index https://whl.modular.com/nightly/simple/ \ --prerelease allow diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index beedc81..470f1dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,23 +4,46 @@ on: push: branches: [main] pull_request: + # Weekly drift check against the latest nightly. Advisory only -- it runs on + # main, so a failure never blocks a PR; it tells us the language moved under a + # frozen repo. See the install step for how the two modes differ. + schedule: + - cron: "17 13 * * 1" + workflow_dispatch: + +env: + # The nightly this repo is known to build and pass on. CI installs exactly + # this for pushes and PRs, so upstream churn cannot turn a green repo red + # without a commit. Bump it when the weekly drift check goes green on a newer + # one, or after fixing whatever it caught. + MOJO_VERSION: "1.0.0b3.dev2026073014" jobs: test: runs-on: ubuntu-latest timeout-minutes: 20 + permissions: + contents: read + issues: write steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v5 - - name: Install Mojo nightly + - name: Install Mojo run: | uv venv - uv pip install mojo \ - --index https://whl.modular.com/nightly/simple/ \ - --prerelease allow + if [ "${{ github.event_name }}" = "schedule" ]; then + echo "::notice::Drift check: installing the LATEST nightly, ignoring the $MOJO_VERSION pin" + uv pip install mojo \ + --index https://whl.modular.com/nightly/simple/ \ + --prerelease allow + else + uv pip install "mojo==$MOJO_VERSION" \ + --index https://whl.modular.com/nightly/simple/ \ + --prerelease allow + fi .venv/bin/mojo --version - name: Format check @@ -47,3 +70,32 @@ jobs: run: | .venv/bin/mojo build -I src bench/bench_parse.mojo -o bench_parse ./bench_parse + + - name: Report nightly drift + if: failure() && github.event_name == 'schedule' + env: + GH_TOKEN: ${{ github.token }} + run: | + title="Mojo nightly drift: tests fail against the latest nightly" + installed=$(.venv/bin/mojo --version 2>/dev/null || echo "unknown") + # Heredoc, not a quoted string: a multi-line shell string would keep + # this block's indentation and markdown would render the body as code. + body=$(cat <