🔧 Make all CI jobs use Rust 1.99 (#12903)

* 🔧 Bump Rust version of the devenv image to 1.99

* 🔧 Run the pinned Rust setup from the lint and format scripts

./build and ./test switch to the Rust version in render-wasm/_build_env
through setup, but ./lint did not, and formatting called cargo fmt
directly. In CI both ran on the devenv image's default toolchain.

Make ./lint run setup and add a ./fmt script that does the same, so all
render-wasm entry points use the pinned version. CI and scripts/ci now
call ./fmt instead of cargo fmt.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Belén Albeza 2026-11-09 16:23:47 +01:00 committed by GitHub
parent 01191051c7
commit fb7468c3f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 50 additions and 41 deletions

View File

@ -49,7 +49,7 @@ jobs:
- name: Format
working-directory: ./render-wasm
run: |
cargo fmt --check
./fmt --check
- name: Lint
working-directory: ./render-wasm

View File

@ -36,6 +36,7 @@ From `render-wasm/`:
- Rust tests: `./test` or `cargo test <name>`.
- Cross-cutting testing principles and anti-patterns: `mem:testing`.
- Lint: `./lint`.
- Format check: `cargo fmt --check`.
- Format: `./fmt`; check only: `./fmt --check`.
- `./build`, `./test`, `./lint` and `./fmt` all run `setup`, which selects the Rust version pinned in `_build_env` (skip with `PENPOT_WASM_PREPARED=1`).
Do not change exported WASM function signatures without updating the corresponding frontend bridge and verifying the frontend renderer path.

View File

@ -106,7 +106,7 @@ produce deployable artifacts.
## Notes
- `mcp` has no lint task and `media-processor` has no lint task; both show
as skipped. `render-wasm` uses `./lint`, `./test`, and `cargo fmt`.
as skipped. `render-wasm` uses `./lint`, `./test`, and `./fmt`.
- `scripts/test_ci.py` (unittest, `python3 scripts/test_ci.py`) covers the
flag wiring, one build command per module and the help text through
`--dry-run`; it executes no real build.

View File

@ -187,7 +187,7 @@ ENV PATH=/opt/cargo/bin:$PATH \
RUSTUP_HOME=/opt/rustup \
CARGO_HOME=/opt/cargo \
RUSTUP_VERSION=1.28.2 \
RUST_VERSION=1.91.0 \
RUST_VERSION=1.99.0 \
EMSCRIPTEN_VERSION=4.0.6
WORKDIR /opt

View File

@ -54,8 +54,9 @@ between them does not invalidate the other's cache. Set `BUILD_MODE=release`
Set `PENPOT_WASM_PREPARED=1` when the pinned Rust toolchain is selected, its
Emscripten target is installed, and this package's Node dependencies are
installed. `./build` and `./test` then skip `rustup` setup and `pnpm install`.
Benchmark preparation sets this flag for each retained build.
installed. `./build`, `./test`, `./lint` and `./fmt` then skip `rustup` setup
and `pnpm install`. Benchmark preparation sets this flag for each retained
build.
### Serialize builds in one checkout

18
render-wasm/fmt Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Usage: ./fmt [--check] [extra cargo fmt params...]
#
# Formats the crate with the Rust toolchain pinned in `_build_env`. Set
# PENPOT_WASM_PREPARED=1 to skip the toolchain setup.
set -e
_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
pushd "$_SCRIPT_DIR" > /dev/null
. ./_build_env frontend
setup
cargo fmt "$@"
popd > /dev/null

View File

@ -10,6 +10,8 @@ fi
. ./_build_env frontend
setup || exit $?
ALLOWED_RULES="-D static_mut_refs"
# ./lint --fix

View File

@ -419,11 +419,7 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec<Segment> {
let mut last_end = (first_bezier.end.x as f32, first_bezier.end.y as f32);
let mut cur_end = first_bezier.end;
loop {
let Some((next_src, next_bezier)) = find_next_in_pool(&mut pool, cur_end, cur_src)
else {
break;
};
while let Some((next_src, next_bezier)) = find_next_in_pool(&mut pool, cur_end, cur_src) {
push_bezier(&mut result, &next_bezier);
last_end = (next_bezier.end.x as f32, next_bezier.end.y as f32);
cur_end = next_bezier.end;

View File

@ -444,7 +444,8 @@ impl PendingTiles {
// Farthest first, since we use pop() to process the tiles
// in order of priority (closest first)
self.tile_order.sort_unstable_by(|a, b| b.0.cmp(&a.0));
self.tile_order
.sort_unstable_by_key(|a| std::cmp::Reverse(a.0));
for (_, tile) in self.tile_order.iter() {
let tile = *tile;

View File

@ -87,13 +87,11 @@ impl TryFrom<&[u8]> for RawFillData {
// FIXME: return Result
pub fn read_fills_from_bytes(buffer: &[u8], num_fills: usize) -> Vec<shapes::Fill> {
buffer
.chunks_exact(RAW_FILL_DATA_SIZE)
.as_chunks::<RAW_FILL_DATA_SIZE>()
.0
.iter()
.take(num_fills)
.map(|bytes| {
RawFillData::try_from(bytes)
.expect("Invalid fill data")
.into()
})
.map(|bytes| RawFillData::from(*bytes).into())
.collect()
}

View File

@ -297,12 +297,9 @@ pub fn replace_text_with_newlines(
let mut current_cursor = *cursor;
if let Some(new_offset) = replace_text_at_cursor(text_content, &current_cursor, lines[0]) {
current_cursor =
TextPositionWithAffinity::new_downstream_affinity(current_cursor.paragraph, new_offset);
} else {
return None;
}
let new_offset = replace_text_at_cursor(text_content, &current_cursor, lines[0])?;
current_cursor =
TextPositionWithAffinity::new_downstream_affinity(current_cursor.paragraph, new_offset);
for line in lines.iter().skip(1) {
if !split_paragraph_at_cursor(text_content, &current_cursor) {
@ -336,12 +333,9 @@ pub fn insert_text_with_newlines(
let mut current_cursor = *cursor;
if let Some(new_offset) = insert_text_at_cursor(text_content, &current_cursor, lines[0]) {
current_cursor =
TextPositionWithAffinity::new_downstream_affinity(current_cursor.paragraph, new_offset);
} else {
return None;
}
let new_offset = insert_text_at_cursor(text_content, &current_cursor, lines[0])?;
current_cursor =
TextPositionWithAffinity::new_downstream_affinity(current_cursor.paragraph, new_offset);
for line in lines.iter().skip(1) {
if !split_paragraph_at_cursor(text_content, &current_cursor) {
@ -477,8 +471,7 @@ pub fn delete_selection_range(text_content: &mut TextContent, selection: &TextSe
delete_range_in_paragraph(&mut paragraphs[end_paragraph], 0, end.offset);
let end_para_children: Vec<_> =
paragraphs[end_paragraph].children_mut().drain(..).collect();
let end_para_children: Vec<_> = std::mem::take(paragraphs[end_paragraph].children_mut());
paragraphs[start.paragraph]
.children_mut()
.extend(end_para_children);
@ -593,10 +586,7 @@ pub fn delete_char_before(
let paragraphs = text_content.paragraphs_mut();
let prev_para_len = paragraph_char_count(&paragraphs[prev_para_idx]);
let current_children: Vec<_> = paragraphs[cursor.paragraph]
.children_mut()
.drain(..)
.collect();
let current_children: Vec<_> = std::mem::take(paragraphs[cursor.paragraph].children_mut());
paragraphs[prev_para_idx]
.children_mut()
.extend(current_children);
@ -770,7 +760,7 @@ pub fn delete_char_after(text_content: &mut TextContent, cursor: &TextPositionWi
delete_range_in_paragraph(para, cursor.offset, end);
} else if cursor.paragraph < paragraphs.len() - 1 {
let next_para_idx = cursor.paragraph + 1;
let next_children: Vec<_> = paragraphs[next_para_idx].children_mut().drain(..).collect();
let next_children: Vec<_> = std::mem::take(paragraphs[next_para_idx].children_mut());
paragraphs[cursor.paragraph]
.children_mut()
.extend(next_children);

View File

@ -76,11 +76,13 @@ impl TryFrom<&[u8]> for RawGuide {
fn read_guides_from_bytes(buffer: &[u8], count: usize) -> Result<Vec<Guide>> {
buffer
.chunks_exact(RAW_GUIDE_SIZE)
.as_chunks::<RAW_GUIDE_SIZE>()
.0
.iter()
.take(count)
.enumerate()
.map(|(i, bytes)| {
RawGuide::try_from(bytes).map(|raw_guide| {
RawGuide::try_from(bytes.as_slice()).map(|raw_guide| {
let mut guide: Guide = raw_guide.into();
guide.index = i;
guide

View File

@ -55,7 +55,7 @@ declare -A FMT_CHECK_CMD=(
[frontend]="pnpm run check-fmt:clj && pnpm run check-fmt:js && pnpm run check-fmt:scss"
[backend]="pnpm run check-fmt:clj"
[common]="pnpm run check-fmt:clj && pnpm run check-fmt:js"
[render-wasm]="cargo fmt --check"
[render-wasm]="./fmt --check"
[exporter]="pnpm run check-fmt:clj"
[mcp]="pnpm run fmt:check"
[plugins]="pnpm run format:check"
@ -67,7 +67,7 @@ declare -A FMT_FIX_CMD=(
[frontend]="pnpm run fmt:clj && pnpm run fmt:js && pnpm run fmt:scss"
[backend]="pnpm run fmt:clj"
[common]="pnpm run fmt:clj && pnpm run fmt:js"
[render-wasm]="cargo fmt"
[render-wasm]="./fmt"
[exporter]="pnpm run fmt:clj"
[mcp]="pnpm run fmt"
[plugins]="pnpm run format"