Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/actions/setup-runtime/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ runs:

- name: Install uv
if: inputs.setup-python == 'true'
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version-file: .tool-versions
enable-cache: ${{ inputs.restore-cache }}
cache-dependency-glob: ${{ inputs.uv-cache-dependency-glob }}

- name: Set up Python
if: inputs.setup-python == 'true'
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version-file: .tool-versions

Expand All @@ -58,7 +58,7 @@ runs:
# this action fails unpacking it (https://github.com/pnpm/setup/issues/29).
- name: Set up pnpm and Node.js
if: inputs.setup-node == 'true'
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
uses: pnpm/setup@a0881977cd8ea73b1ed212236c99ca10dcc80cf4 # v3.1.1
with:
runtime: node@${{ steps.versions.outputs.node }}
cache: ${{ inputs.restore-cache == 'true' }}
Expand Down
4 changes: 2 additions & 2 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@
"automerge": false
},
{
"description": "A Python minor version has to move .tool-versions, backend/.python-version, ruff's target-version and the Docker base images together, and every dependency needs wheels for it. One PR, merged by hand, so CI never tests on a newer Python than the images run.",
"matchPackageNames": ["python"],
"description": "A Python minor version has to move .tool-versions, backend/.python-version, ruff's target-version and the Docker base images together, and every dependency needs wheels for it. One PR, merged by hand, so CI never tests on a newer Python than the images run. Matched by depName: .tool-versions looks Python up as python/cpython, which a packageName match misses.",
"matchDepNames": ["python"],
"matchUpdateTypes": ["minor"],
"groupName": "python minor",
"groupSlug": "python-minor",
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ jobs:
persist-credentials: false

- name: Scan IaC with Trivy
uses: docker://aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa
uses: docker://aquasec/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c
with:
args: >-
config .
Expand All @@ -462,7 +462,7 @@ jobs:

- name: Upload Trivy IaC SARIF
if: always()
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
sarif_file: trivy-iac.sarif

Expand Down Expand Up @@ -491,15 +491,15 @@ jobs:
persist-credentials: false

- name: Initialize CodeQL
uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
languages: ${{ matrix.language }}
build-mode: none
config-file: ./.github/codeql-config.yml

- name: Perform CodeQL analysis
id: analyze
uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
category: "/language:${{ matrix.language }}"
output: sarif-results
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/container-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ jobs:
- name: Scan image with Trivy
# A non-blocking scan still marks this step failed in the run view.
continue-on-error: ${{ !inputs.blocking }}
uses: docker://aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa
uses: docker://aquasec/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c
with:
args: >-
image --exit-code 1 --ignore-unfixed --severity HIGH,CRITICAL
--ignorefile .github/trivyignore.yaml
${{ matrix.image }}:${{ github.sha }}

- name: Generate SPDX SBOM
uses: docker://aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa
uses: docker://aquasec/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c
with:
args: >-
image --format spdx-json --output sbom-${{ matrix.service }}.spdx.json
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,19 @@ jobs:

# Same policy as the scan in ci.yml; suppressions live in .github/trivyignore.yaml.
- name: Scan image with Trivy
uses: docker://aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa
uses: docker://aquasec/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c
with:
args: >-
image --exit-code 1 --ignore-unfixed --severity HIGH,CRITICAL --ignorefile .github/trivyignore.yaml ${{ steps.meta.outputs.ref }}

- name: Generate SPDX SBOM
uses: docker://aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa
uses: docker://aquasec/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c
with:
args: >-
image --format spdx-json --output ${{ steps.meta.outputs.sbom }} ${{ steps.meta.outputs.ref }}

- name: Log in to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/renovate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
steps:
- name: Create app token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.RENOVATE_APP_CLIENT_ID }}
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
Expand All @@ -56,7 +56,7 @@ jobs:
permission-workflows: write

- name: Run Renovate
uses: renovatebot/github-action@230ce922b08968d0a4f6f70f295601daff09ef1d # v46.3.7
uses: renovatebot/github-action@a63d9a7bc606f7336fd20b47f2f32259a35e6213 # v46.3.9
with:
token: ${{ steps.app-token.outputs.token }}
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ jobs:
persist-credentials: false

- name: Run Scorecard
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
uses: ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0
with:
results_file: results.sarif
results_format: sarif
publish_results: true

- name: Upload SARIF
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
sarif_file: results.sarif
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
26.11.1
26.12.1
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ repos:
files: ^\.github/workflows/.*\.ya?ml$

- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: fa412071e4f5d44d44f9e365f4676f9df92456a2 # frozen: v1.30.1
rev: c92afdf2c6ca336aaa8f39e2bd8d48f62dbd8e1f # frozen: v1.31.1
hooks:
- id: zizmor
args: [ "--min-confidence=high" ]
Expand Down Expand Up @@ -71,7 +71,7 @@ repos:
exclude: '^(?:frontend[^/]*(?:/.*)?/data/.*\.json|(?:.*/)?package-lock\.json)$'

- repo: https://github.com/astral-sh/uv-pre-commit
rev: 308b7fa26251088f0f2a9ad4a5b5d86e30ac5455 # frozen: 0.12.24
rev: eeca16c54e09b4d9a848259e46bae14737944348 # frozen: 0.13.5
hooks:
- id: uv-lock
name: uv-lock-root
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
python 3.14.8
uv 0.12.24
just 1.58.0
uv 0.13.5
just 1.59.0
4 changes: 2 additions & 2 deletions app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ RUN --mount=type=cache,target=/tmp/metro-cache,id=metro-${EXPO_PUBLIC_API_URL}-$
# copy-up leaves a second copy in the layer, so strip in a throwaway stage and
# copy onto plain Alpine. BuildKit preserves caps across COPY --from, so the
# strip must precede the copy.
FROM caddy:2-alpine@sha256:d8542f48d34a9cf4e4c11a478865229840e87e4c96ea3f439101f31a5d35f75f AS caddybin
FROM caddy:2-alpine@sha256:eb4d35b7452d1ab6171fbdbe8d9275b2301dad6dad92115d91e19fb9b24d510f AS caddybin
RUN setcap -r /usr/bin/caddy

FROM alpine:3.24@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 AS runtime
FROM alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a AS runtime

ARG WORKDIR

Expand Down
28 changes: 28 additions & 0 deletions app/biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@
},
"correctness": {
"noReactPropAssignments": "error",
// These components wrap string children in <Text> themselves; <title>
// renders inside expo-router's web-only <Head>.
"noReactNativeRawText": {
"level": "error",
"options": {
"skip": [
"AppButton",
"AppText",
"Chip",
"DocsLink",
"FilterChip",
"MutedText",
"RNText",
"ScreenTitle",
"UiText",
"title"
]
}
},
"useExhaustiveDependencies": "error",
"useHookAtTopLevel": "error",
"useImportExtensions": "off"
Expand Down Expand Up @@ -77,12 +96,21 @@
"complexity": {
"noExcessiveLinesPerFunction": "off"
},
"correctness": {
// jest.mock factories are hoisted and must build their stub components inline.
"noComponentHookFactories": "off"
},
"performance": {
// Inline handlers in a test render once; rerender cost is not a concern.
"noJsxPropsBind": "off"
},
"security": {
"noSecrets": "off"
},
// Same reasons as the shared test override in biome.base.jsonc.
"suspicious": {
"noConditionalExpect": "off",
"useExpect": "off"
}
}
}
Expand Down
1 change: 1 addition & 0 deletions app/e2e/gallery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ async function mockGalleryProductApi(page: Page) {
});
}

// biome-ignore lint/correctness/usePlaywrightValidDescribeCallback: Playwright accepts a details object before the callback.
test.describe('Product gallery (e2e)', { tag: '@cross-browser' }, () => {
// Increase the per-test timeout for slower CI environments
test.setTimeout(60_000);
Expand Down
10 changes: 5 additions & 5 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@
"@types/jest": "30.0.0",
"@types/node": "catalog:",
"@types/react": "~19.2.18",
"@typescript-eslint/parser": "8.71.1",
"@typescript-eslint/parser": "8.72.0",
"babel-plugin-react-compiler": "1.0.0",
"babel-preset-expo": "~57.0.13",
"baseline-browser-mapping": "2.11.28",
"eslint": "10.12.0",
"baseline-browser-mapping": "2.11.31",
"eslint": "10.13.0",
"eslint-plugin-react-hooks": "7.1.1",
"eslint-plugin-react-native-a11y": "3.5.1",
"eslint-plugin-react-refresh": "0.5.7",
Expand All @@ -109,10 +109,10 @@
"jest-junit": "17.0.0",
"msw": "3.0.3",
"openapi-typescript": "7.13.0",
"postcss": "8.5.29",
"postcss": "8.5.31",
"serve": "14.2.6",
"tailwindcss": "catalog:",
"test-renderer": "1.3.0",
"test-renderer": "1.3.1",
"typescript": "catalog:"
},
"private": true
Expand Down
1 change: 1 addition & 0 deletions app/src/components/base/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type BrandGlyphProps = { size?: number; color?: string; strokeWidth?: number };
/** Turns a single-path brand SVG's `d` data into a Lucide-shaped component. */
function createBrandGlyph(d: string) {
// strokeWidth is ignored: brand marks are filled, not stroked.
// biome-ignore lint/correctness/noComponentHookFactories: called once per glyph at module load, not during render.
return function BrandGlyph({ size = 24, color = 'currentColor' }: BrandGlyphProps) {
return (
<Svg viewBox="0 0 24 24" width={size} height={size}>
Expand Down
1 change: 1 addition & 0 deletions app/src/components/base/lazyWithRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export function lazyWithRetry<T extends ComponentType<any>>(
}),
);
let current = make();
// biome-ignore lint/correctness/noComponentHookFactories: called once at module scope, like React.lazy.
return function LazyWithRetry(props: ComponentProps<T>) {
return createElement(current, props);
};
Expand Down
32 changes: 30 additions & 2 deletions biome.base.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@
}
},
"overrides": [
{
// The sites already ship light-dark() and mask-image without fallbacks,
// so the target is Baseline newly available, not widely. An override,
// so a subrepo's `"suspicious": "on"` does not reset the option.
"includes": ["**/*.css"],
"linter": {
"rules": {
"suspicious": {
"useBaseline": { "level": "warn", "options": { "available": "newly" } }
}
}
}
},
{
// Standalone .svg assets are decorative icon sources; the component that
// renders one supplies the accessible name (title/aria-label), not the file.
Expand All @@ -60,8 +73,13 @@
"linter": {
"rules": {
"complexity": { "noExcessiveLinesPerFunction": "off" },
"performance": { "useTopLevelRegex": "off" },
"security": { "noSecrets": "off" }
// Playwright's locator.innerText() returns the text as rendered, which is
// what the tests assert on; textContent would include hidden nodes.
"performance": { "useDomNodeTextContent": "off", "useTopLevelRegex": "off" },
"security": { "noSecrets": "off" },
// Assertions often live in shared expect* helpers, and `if (!result.success)`
// guards narrow types after the outcome is already asserted.
"suspicious": { "noConditionalExpect": "off", "useExpect": "off" }
}
}
}
Expand All @@ -85,5 +103,15 @@
"formatter": {
"trailingCommas": "none"
}
},
// rumdl (prek hook) owns Markdown style, and YAML is hand-formatted or
// tool-written (pnpm-lock.yaml, prek's frozen revs). Keep Biome to code.
"markdown": {
"formatter": { "enabled": false },
"linter": { "enabled": false }
},
"yaml": {
"formatter": { "enabled": false },
"linter": { "enabled": false }
}
}
2 changes: 1 addition & 1 deletion compose.telemetry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ services:
logging: *default-logging

alloy:
image: grafana/alloy:v1.20.1@sha256:2aa2099af76c0098d4af7a4d6e48f86cb66dc1a000222ad927a1c67c6542d13f
image: grafana/alloy:v1.21.2@sha256:89a5a320168387336b9ac3c850609c3ba7757eadd4ae5f1d819deb35e9854b18
restart: unless-stopped
# Root to read root-only files under the host mounts below. The Docker API is
# behind the proxy, so root here does not imply control of the daemon.
Expand Down
14 changes: 13 additions & 1 deletion docs/e2e/accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,18 @@ async function analyzePage(page: Page, options: { exclude?: string[] } = {}) {
for (const selector of options.exclude ?? []) {
builder = builder.exclude(selector);
}
return builder.analyze();
const results = await builder.analyze();
// Starlight's search button is named "Search" and also shows its "Ctrl K" shortcut.
// axe 4.14 counts the shortcut as label text; the name still holds the label, so
// 2.5.3 is met. Starlight won't change it (withastro/starlight#4260).
results.violations = results.violations.flatMap((violation) => {
if (violation.id !== 'label-content-name-mismatch') return [violation];
const nodes = violation.nodes.filter(
(node) => !node.html.startsWith('<button data-open-modal=""'),
);
return nodes.length > 0 ? [{ ...violation, nodes }] : [];
});
return results;
}

async function switchToDarkTheme(page: Page) {
Expand Down Expand Up @@ -102,6 +113,7 @@ test.describe('Viewport overflow', () => {
for (const path of ['/architecture/datamodel/', '/architecture/system-design/', '/api/public/']) {
test(`${path} has no horizontal overflow at ${VIEWPORT_WIDTH}px`, async ({ page }) => {
await page.goto(path);
// biome-ignore lint/suspicious/noPlaywrightNetworkidle: the overflow check must run after fonts and diagrams have loaded.
await page.waitForLoadState('networkidle');
const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth);
expect(scrollWidth).toBeLessThanOrEqual(VIEWPORT_WIDTH + TOLERANCE_PX);
Expand Down
1 change: 1 addition & 0 deletions docs/e2e/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test('pages run under the built CSP without violations', async ({ page }) => {
for (const path of ['/', '/architecture/system-design/', '/api/public/']) {
// biome-ignore lint/performance/noAwaitInLoops: one page, so each route loads after the last.
await page.goto(path);
// biome-ignore lint/suspicious/noPlaywrightNetworkidle: CSP errors surface only once every subresource has been requested.
await page.waitForLoadState('networkidle');
}

Expand Down
Loading
Loading