The layout suite runs in CI
It has caught five regressions the other two suites cannot see, because what it checks is what Flutter does with the tree rather than what the tree says: `Expanded` outside a Flex, a `Wrap` handing a child unbounded width, a scrollbar on a different controller from its view. Every one of those passed `just test nim` and broke the running app. The job needs a Flutter toolchain, which is the reason it was left out and the only reason. Pinned to the version and hash `tools/toolchain.sh` already uses, since a suite that passes in CI and fails on the developer's machine is worse than no suite. Flutter unpacks into HOME rather than /opt: it writes a cache and a version stamp inside its own directory on the first command, and a root-owned copy fails as whatever user the job runs as. It needs the library from the Nim job and nothing from the Dart one, so it waits on `nim-test` alone. The header comment went with it. It described handing the build to a Modal Sandbox for the web bundle, and both the web target and that container are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f912073 parent: 5594a62 modified
.rickub/workflows/build.yml +69 -8 | @@ -1,18 +1,23 @@ | ||
| 1 | -# The build, on rickub. GitLab CI next door reads source and no more — | |
| 2 | -# check-common on every push — and deliberately builds nothing. This is the | |
| 3 | -# other half: the web bundle, actually compiled. | |
| 1 | +# The build, on rickub. GitLab CI next door reads source and no more, and | |
| 2 | +# deliberately builds nothing. This is the other half: three suites, each one | |
| 3 | +# checking a different seam. | |
| 4 | 4 | # |
| 5 | -# It is not compiled *here*. The job hands the work to Modal exactly as a | |
| 6 | -# person at a terminal would, and the Sandbox does it against the `devshell` | |
| 7 | -# volume. What a runner contributes is a checkout, a python, and somewhere to | |
| 8 | -# put the result afterwards. The reason it goes to Modal is the volume the | |
| 9 | -# toolchain is cached on, not the size of the build. | |
| 5 | +# Nothing here is handed to Modal. The header used to say this job passed the | |
| 6 | +# work to a Sandbox against the `devshell` volume — that was the web bundle, | |
| 7 | +# and both the web target and the container that built it are gone. What runs | |
| 8 | +# now is small enough for a runner: Nim in about a second, Dart in less, and | |
| 9 | +# Flutter for as long as a Flutter toolchain takes to arrive. | |
| 10 | +# | |
| 11 | +# Every toolchain is a sha256-pinned tarball rather than an apt package or a | |
| 12 | +# third-party setup action, which is this repo's habit — see | |
| 13 | +# `tools/toolchain.sh`, which fetches the same two by the same hashes. | |
| 10 | 14 | # |
| 11 | 15 | # Lives in .rickub/workflows/ rather than .github/workflows/ because rickub |
| 12 | 16 | # reads one or the other and never both: with this directory present, a |
| 13 | 17 | # .github/workflows/ added later would be silently ignored. There is none |
| 14 | 18 | # today, so nothing is being shadowed — see |
| 15 | 19 | # https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github |
| 20 | + | |
| 16 | 21 | name: build |
| 17 | 22 | |
| 18 | 23 | on: |
| @@ -109,3 +114,59 @@ jobs: | ||
| 109 | 114 | path: build/nim |
| 110 | 115 | - run: cd dart/frq_core && dart pub get && dart test -r expanded |
| 111 | 116 | |
| 117 | + # The screens, laid out for real. Widget tests on the Dart VM: headless, no | |
| 118 | + # GL and no window, which is exactly what makes them the check a Wayland | |
| 119 | + # window cannot be — a GUI on Wayland cannot be clicked by a script, so for | |
| 120 | + # a long time the biggest screen in the app went out unverified. | |
| 121 | + # | |
| 122 | + # They have caught five regressions that the Nim and Dart suites cannot see, | |
| 123 | + # because what they check is what Flutter does with the tree rather than | |
| 124 | + # what the tree says: `Expanded` outside a Flex, a `Wrap` handing a child | |
| 125 | + # unbounded width, a scrollbar on a different controller from its view. | |
| 126 | + # | |
| 127 | + # The cost is a Flutter toolchain, which is why this job is last and why it | |
| 128 | + # needs neither of the others to pass first — it needs the library the Nim | |
| 129 | + # job builds, and nothing from Dart. | |
| 130 | + layout-test: | |
| 131 | + runs-on: ubuntu-latest | |
| 132 | + needs: [nim-test] | |
| 133 | + env: | |
| 134 | + # The same version and hash `tools/toolchain.sh` pins, because a suite | |
| 135 | + # that passes here and fails on the developer's machine is worse than no | |
| 136 | + # suite. Two places rather than one is the price of a workflow file that | |
| 137 | + # cannot source a shell script it also has to trust. | |
| 138 | + FLUTTER_VERSION: "3.47.0" | |
| 139 | + FLUTTER_SHA256: "26cd99d3d94b1367e6b50535a18aeef0282c10a535bbe3ec493534dcdab75296" | |
| 140 | + steps: | |
| 141 | + - uses: actions/checkout@v4 | |
| 142 | + | |
| 143 | + - name: Flutter ${{ env.FLUTTER_VERSION }} | |
| 144 | + run: | | |
| 145 | + set -euo pipefail | |
| 146 | + url="https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" | |
| 147 | + curl -fsSL -o /tmp/flutter.tar.xz "$url" | |
| 148 | + echo "${FLUTTER_SHA256} /tmp/flutter.tar.xz" | sha256sum -c - | |
| 149 | + # Into HOME rather than /opt: flutter writes its own cache and | |
| 150 | + # version stamp inside its directory on the first command, so a | |
| 151 | + # root-owned copy fails as whatever user the job runs as. | |
| 152 | + mkdir -p "$HOME/flutter" | |
| 153 | + tar -xJf /tmp/flutter.tar.xz -C "$HOME/flutter" --strip-components=1 | |
| 154 | + echo "$HOME/flutter/bin" >> "$GITHUB_PATH" | |
| 155 | + # The tarball is an unpacked git checkout, and flutter refuses to | |
| 156 | + # report its own version out of a repository it thinks belongs to | |
| 157 | + # somebody else. `tools/toolchain.sh` does this too. | |
| 158 | + git config --global --add safe.directory "$HOME/flutter" | |
| 159 | + | |
| 160 | + # The .so dlopens OpenSSL at startup; the test loads it through FFI | |
| 161 | + # exactly as the app does. | |
| 162 | + - run: sudo apt-get update -qq && sudo apt-get install -y -qq libssl3 | |
| 163 | + | |
| 164 | + - uses: actions/download-artifact@v4 | |
| 165 | + with: | |
| 166 | + name: libfrqcore | |
| 167 | + path: build/nim | |
| 168 | + | |
| 169 | + # `../build/nim/libfrqcore.so` is one of the paths `frq_core` looks in, | |
| 170 | + # named for this case — `flutter test` runs from `flutter/`. | |
| 171 | + - name: The layout suite | |
| 172 | + run: cd flutter && flutter pub get && flutter test test/nim_layout_test.dart | |
| @@ -1,18 +1,23 @@ | |||
| 1 | -# The build, on rickub. GitLab CI next door reads source and no more — | 1 | +# The build, on rickub. GitLab CI next door reads source and no more, and |
| 2 | -# check-common on every push — and deliberately builds nothing. This is the | 2 | +# deliberately builds nothing. This is the other half: three suites, each one |
| 3 | -# other half: the web bundle, actually compiled. | 3 | +# checking a different seam. |
| 4 | # | 4 | # |
| 5 | -# It is not compiled *here*. The job hands the work to Modal exactly as a | 5 | +# Nothing here is handed to Modal. The header used to say this job passed the |
| 6 | -# person at a terminal would, and the Sandbox does it against the `devshell` | 6 | +# work to a Sandbox against the `devshell` volume — that was the web bundle, |
| 7 | -# volume. What a runner contributes is a checkout, a python, and somewhere to | 7 | +# and both the web target and the container that built it are gone. What runs |
| 8 | -# put the result afterwards. The reason it goes to Modal is the volume the | 8 | +# now is small enough for a runner: Nim in about a second, Dart in less, and |
| 9 | -# toolchain is cached on, not the size of the build. | 9 | +# Flutter for as long as a Flutter toolchain takes to arrive. |
| 10 | +# | ||
| 11 | +# Every toolchain is a sha256-pinned tarball rather than an apt package or a | ||
| 12 | +# third-party setup action, which is this repo's habit — see | ||
| 13 | +# `tools/toolchain.sh`, which fetches the same two by the same hashes. | ||
| 10 | # | 14 | # |
| 11 | # Lives in .rickub/workflows/ rather than .github/workflows/ because rickub | 15 | # Lives in .rickub/workflows/ rather than .github/workflows/ because rickub |
| 12 | # reads one or the other and never both: with this directory present, a | 16 | # reads one or the other and never both: with this directory present, a |
| 13 | # .github/workflows/ added later would be silently ignored. There is none | 17 | # .github/workflows/ added later would be silently ignored. There is none |
| 14 | # today, so nothing is being shadowed — see | 18 | # today, so nothing is being shadowed — see |
| 15 | # https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github | 19 | # https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github |
| 20 | + | ||
| 16 | name: build | 21 | name: build |
| 17 | 22 | ||
| 18 | on: | 23 | on: |
| @@ -109,3 +114,59 @@ jobs: | |||
| 109 | path: build/nim | 114 | path: build/nim |
| 110 | - run: cd dart/frq_core && dart pub get && dart test -r expanded | 115 | - run: cd dart/frq_core && dart pub get && dart test -r expanded |
| 111 | 116 | ||
| 117 | + # The screens, laid out for real. Widget tests on the Dart VM: headless, no | ||
| 118 | + # GL and no window, which is exactly what makes them the check a Wayland | ||
| 119 | + # window cannot be — a GUI on Wayland cannot be clicked by a script, so for | ||
| 120 | + # a long time the biggest screen in the app went out unverified. | ||
| 121 | + # | ||
| 122 | + # They have caught five regressions that the Nim and Dart suites cannot see, | ||
| 123 | + # because what they check is what Flutter does with the tree rather than | ||
| 124 | + # what the tree says: `Expanded` outside a Flex, a `Wrap` handing a child | ||
| 125 | + # unbounded width, a scrollbar on a different controller from its view. | ||
| 126 | + # | ||
| 127 | + # The cost is a Flutter toolchain, which is why this job is last and why it | ||
| 128 | + # needs neither of the others to pass first — it needs the library the Nim | ||
| 129 | + # job builds, and nothing from Dart. | ||
| 130 | + layout-test: | ||
| 131 | + runs-on: ubuntu-latest | ||
| 132 | + needs: [nim-test] | ||
| 133 | + env: | ||
| 134 | + # The same version and hash `tools/toolchain.sh` pins, because a suite | ||
| 135 | + # that passes here and fails on the developer's machine is worse than no | ||
| 136 | + # suite. Two places rather than one is the price of a workflow file that | ||
| 137 | + # cannot source a shell script it also has to trust. | ||
| 138 | + FLUTTER_VERSION: "3.47.0" | ||
| 139 | + FLUTTER_SHA256: "26cd99d3d94b1367e6b50535a18aeef0282c10a535bbe3ec493534dcdab75296" | ||
| 140 | + steps: | ||
| 141 | + - uses: actions/checkout@v4 | ||
| 142 | + | ||
| 143 | + - name: Flutter ${{ env.FLUTTER_VERSION }} | ||
| 144 | + run: | | ||
| 145 | + set -euo pipefail | ||
| 146 | + url="https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" | ||
| 147 | + curl -fsSL -o /tmp/flutter.tar.xz "$url" | ||
| 148 | + echo "${FLUTTER_SHA256} /tmp/flutter.tar.xz" | sha256sum -c - | ||
| 149 | + # Into HOME rather than /opt: flutter writes its own cache and | ||
| 150 | + # version stamp inside its directory on the first command, so a | ||
| 151 | + # root-owned copy fails as whatever user the job runs as. | ||
| 152 | + mkdir -p "$HOME/flutter" | ||
| 153 | + tar -xJf /tmp/flutter.tar.xz -C "$HOME/flutter" --strip-components=1 | ||
| 154 | + echo "$HOME/flutter/bin" >> "$GITHUB_PATH" | ||
| 155 | + # The tarball is an unpacked git checkout, and flutter refuses to | ||
| 156 | + # report its own version out of a repository it thinks belongs to | ||
| 157 | + # somebody else. `tools/toolchain.sh` does this too. | ||
| 158 | + git config --global --add safe.directory "$HOME/flutter" | ||
| 159 | + | ||
| 160 | + # The .so dlopens OpenSSL at startup; the test loads it through FFI | ||
| 161 | + # exactly as the app does. | ||
| 162 | + - run: sudo apt-get update -qq && sudo apt-get install -y -qq libssl3 | ||
| 163 | + | ||
| 164 | + - uses: actions/download-artifact@v4 | ||
| 165 | + with: | ||
| 166 | + name: libfrqcore | ||
| 167 | + path: build/nim | ||
| 168 | + | ||
| 169 | + # `../build/nim/libfrqcore.so` is one of the paths `frq_core` looks in, | ||
| 170 | + # named for this case — `flutter test` runs from `flutter/`. | ||
| 171 | + - name: The layout suite | ||
| 172 | + run: cd flutter && flutter pub get && flutter test test/nim_layout_test.dart | ||