From 8420f89f417750f9324327518af7fb78c7ca2c22 Mon Sep 17 00:00:00 2001 From: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:31:45 -0700 Subject: [PATCH 1/3] 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. String.__init__ takes a variadic pack of Writable arguments and Error conforms to Writable, so String(e) is the direct replacement. Test-only; nothing in src/ used the static form. Co-Authored-By: Claude Opus 5 (1M context) --- test/test_errors.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_errors.mojo b/test/test_errors.mojo index 92ebbd2..833d65f 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) def _strict_drain(var source: String) raises: From 3864bbc7a2a1e90ae12b4930699875da91b68b9e Mon Sep 17 00:00:00 2001 From: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:35:01 -0700 Subject: [PATCH 2/3] Build the stripped string into a temporary before reassigning s.strip() borrows s while the String initializer is constructing s as its result, which the compiler now rejects: error: aliasing values passed immutably to 'args' argument and constructed as a result in 'String' initializer call Constructing into a temporary and moving it in separates the borrow from the assignment. This is why mojo-xml's conformance job was already red on 7/29, before the String.write breakage in the previous commit. Co-Authored-By: Claude Opus 5 (1M context) --- test/anchor_dump.mojo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/anchor_dump.mojo b/test/anchor_dump.mojo index cceb6fb..68a5a70 100644 --- a/test/anchor_dump.mojo +++ b/test/anchor_dump.mojo @@ -15,7 +15,8 @@ from xml import fromstring, Element def _esc(s_in: String, strip: Bool) -> String: var s = s_in if strip: - s = String(s.strip()) + var stripped = String(s.strip()) + s = stripped^ # order matters: backslash first var out = String() for ch in s.codepoint_slices(): From 7e8be6c53512f358e71702dba567d3c88c00aa4d Mon Sep 17 00:00:00 2001 From: Conor Bronsdon <120674402+conorbronsdon@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:49:52 -0700 Subject: [PATCH 3/3] 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 1e61ed3..26ad3e2 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 1618fe8..6383c50 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: "57 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 @@ -44,3 +67,32 @@ jobs: run: | .venv/bin/mojo build -I src test/fuzz_runner.mojo -o xml_fuzz MOJO=.venv/bin/mojo python3 test/fuzz_drive.py ./xml_fuzz 300 + + - 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 <