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
2 changes: 0 additions & 2 deletions .github/scripts/check_companion_release_tag_absent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3

from __future__ import annotations

import argparse
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/cleanup_companion_releases.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Delete obsolete Companion GitHub Releases after validating every feed marker."""

from __future__ import annotations
Expand Down
2 changes: 0 additions & 2 deletions .github/scripts/cleanup_test_container_versions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3

from __future__ import annotations

import argparse
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/companion_build_required.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Decide whether a pull request needs the expensive companion package build."""

from __future__ import annotations
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/manage_companion_artifact_manifest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Create and verify immutable Companion package artifact manifests."""

from __future__ import annotations
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/prepare_release_candidate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Reserve or resolve the immutable source commit for a publishable build."""

from __future__ import annotations
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/promote_companion_release.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3

from __future__ import annotations

import argparse
Expand Down Expand Up @@ -176,6 +174,8 @@ def request_asset_bytes(asset: dict, token: str | None = None) -> bytes:
with urllib.request.urlopen(request) as response:
return response.read()
except urllib.error.URLError as error:
if isinstance(error, urllib.error.HTTPError):
error.close()
if (
not is_retryable_asset_download_error(error)
or attempt == ASSET_DOWNLOAD_MAX_ATTEMPTS - 1
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/resolve_companion_release_state.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Resolve the immutable publication state for a Companion release tag."""

from __future__ import annotations
Expand Down
22 changes: 17 additions & 5 deletions .github/scripts/reuse_release_ci_tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Reuse successful source checks from CI on the exact release commit."""

from __future__ import annotations
Expand All @@ -15,6 +14,9 @@
CI_WORKFLOW = "test.yml"
API_TIMEOUT_SECONDS = 10
JOBS_PER_PAGE = 100
FRONTEND_REQUIRED_STEPS = frozenset(
{"Run browser correctness contracts", "Verify frontend type correctness"}
)


def github_get(url: str, token: str) -> dict[str, Any]:
Expand Down Expand Up @@ -63,17 +65,27 @@ def reusable_jobs(api_url: str, repository: str, sha: str, token: str) -> set[st
successful: set[str] = set()
page = 1
while True:
jobs_url = (
f"{api_url}/repos/{repository}/actions/runs/{run_id}/jobs?"
f"filter=latest&per_page={JOBS_PER_PAGE}&page={page}"
)
jobs_url = f"{api_url}/repos/{repository}/actions/runs/{run_id}/jobs?filter=latest&per_page={JOBS_PER_PAGE}&page={page}"
jobs = github_get(jobs_url, token).get("jobs")
if not isinstance(jobs, list):
raise ValueError("CI workflow jobs response is missing jobs")
for job in jobs:
if not isinstance(job, dict):
raise ValueError("CI workflow job is not an object")
if job.get("name") in COMPONENTS and job.get("conclusion") == "success":
if job["name"] == "frontend":
steps = job.get("steps", [])
if not isinstance(steps, list):
raise ValueError("Frontend CI job steps are not a list")
successful_steps = {
step.get("name")
for step in steps
if isinstance(step, dict)
and step.get("conclusion") == "success"
and isinstance(step.get("name"), str)
}
if not FRONTEND_REQUIRED_STEPS <= successful_steps:
continue
successful.add(job["name"])
if len(jobs) < JOBS_PER_PAGE:
return successful
Expand Down
2 changes: 0 additions & 2 deletions .github/scripts/should_promote_beta_tag.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3

from __future__ import annotations

import argparse
Expand Down
1 change: 0 additions & 1 deletion .github/scripts/validate_release_scope.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""Validate a public Sambee release's component-promotion scope."""

from __future__ import annotations
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/devcontainer-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ permissions:

jobs:
prepare:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
os_packages_refresh_key: ${{ steps.metadata.outputs.os_packages_refresh_key }}
steps:
Expand All @@ -44,7 +44,7 @@ jobs:

validate-devcontainer:
needs: prepare
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
34 changes: 28 additions & 6 deletions .github/workflows/docker-image-preview-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,36 @@ jobs:
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npm ci --prefer-offline

- name: Run frontend type checks
- name: Verify frontend type correctness
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npx tsc --noEmit
id: frontend-types
run: cd frontend && npm run typecheck

- name: Run frontend tests
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npm run test:ci

- name: Build production frontend for browser checks
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npm run build

- name: Install browser dependencies
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npm run test:e2e:install -- --with-deps

- name: Run browser correctness contracts
if: steps.source-ci.outputs.frontend != 'true'
run: cd frontend && npm run test:browser:ci

- name: Upload browser correctness reports
if: ${{ !cancelled() && steps.source-ci.outputs.frontend != 'true' }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-browser-correctness
path: frontend/test-results/ci/
retention-days: 7
if-no-files-found: warn

build-and-validate-platforms:
needs: [prepare, validate-tests]
if: ${{ needs.prepare.outputs.publication_state == 'build' }}
Expand All @@ -243,10 +265,10 @@ jobs:
include:
- docker_platform: linux/amd64
platform_id: amd64
runner_label: ubuntu-24.04
runner_label: ubuntu-latest
- docker_platform: linux/arm64
platform_id: arm64
runner_label: ubuntu-24.04-arm
runner_label: ubuntu-26.04-arm
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -564,10 +586,10 @@ jobs:
include:
- docker_platform: linux/amd64
platform_id: amd64
runner_label: ubuntu-24.04
runner_label: ubuntu-latest
- docker_platform: linux/arm64
platform_id: arm64
runner_label: ubuntu-24.04-arm
runner_label: ubuntu-26.04-arm
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-image-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ permissions:

jobs:
prepare:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
checkout_ref: ${{ steps.metadata.outputs.checkout_ref }}
release_type: ${{ steps.metadata.outputs.release_type }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docker-image-security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissions:

jobs:
prepare:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
created: ${{ steps.metadata.outputs.created }}
os_packages_refresh_key: ${{ steps.metadata.outputs.os_packages_refresh_key }}
Expand All @@ -39,10 +39,10 @@ jobs:
include:
- docker_platform: linux/amd64
platform_id: amd64
runner_label: ubuntu-24.04
runner_label: ubuntu-latest
- docker_platform: linux/arm64
platform_id: arm64
runner_label: ubuntu-24.04-arm
runner_label: ubuntu-26.04-arm
steps:
- name: Checkout main
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docker-image-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ permissions:

jobs:
prepare:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
created: ${{ steps.metadata.outputs.created }}
os_packages_refresh_key: ${{ steps.metadata.outputs.os_packages_refresh_key }}
Expand All @@ -103,10 +103,10 @@ jobs:
include:
- docker_platform: linux/amd64
platform_id: amd64
runner_label: ubuntu-24.04
runner_label: ubuntu-latest
- docker_platform: linux/arm64
platform_id: arm64
runner_label: ubuntu-24.04-arm
runner_label: ubuntu-26.04-arm
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
branches: [ main ]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
# ─────────────────────────────────────────────────────────────────────
# Detect which components changed
Expand Down Expand Up @@ -42,6 +46,8 @@ jobs:
}
backend_extras = {
"Dockerfile",
".github/workflows/lint.yml",
"scripts/test",
"scripts/verify-backend-lockfiles.py",
"scripts/verify-python-runtime-image.py",
".github/actions/setup-runtime-python/action.yml",
Expand Down Expand Up @@ -117,7 +123,7 @@ jobs:
else:
raise SystemExit(f"Unsupported event for change detection: {event_name}")

backend = any(name.startswith("backend/") or name in backend_extras for name in filenames)
backend = any(name.startswith("backend/") or name.endswith(".py") or name in backend_extras for name in filenames)
frontend = any(name.startswith("frontend/") or name in frontend_extras for name in filenames)
companion = any(name.startswith("companion/") or name in companion_extras for name in filenames)

Expand Down Expand Up @@ -152,6 +158,9 @@ jobs:
- name: Ruff check
run: cd backend && ruff check app tests/test_verify_backend_lockfiles_script.py ../scripts/verify-backend-lockfiles.py

- name: Ruff executable-script check
run: python -m ruff check --select EXE . .github

- name: Ruff format check
run: cd backend && ruff format --check app tests/test_verify_backend_lockfiles_script.py ../scripts/verify-backend-lockfiles.py

Expand Down
49 changes: 34 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
branches: [ main ]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
# ──────────────────────────────────────────────
# Detect which components changed
Expand Down Expand Up @@ -76,7 +80,7 @@ jobs:
backend:
needs: changes
if: needs.changes.outputs.backend == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -148,6 +152,7 @@ jobs:
needs: changes
if: needs.changes.outputs.frontend == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -176,12 +181,31 @@ jobs:
- name: Install dependencies
run: cd frontend && npm ci --prefer-offline

- name: TypeScript type check
run: cd frontend && npx tsc --noEmit
- name: Verify frontend type correctness
id: frontend-types
run: cd frontend && npm run typecheck

- name: Run tests
run: cd frontend && npm run test:ci

- name: Build production frontend
run: cd frontend && npm run build

- name: Install browser dependencies
run: cd frontend && npm run test:e2e:install -- --with-deps

- name: Run browser correctness contracts
run: cd frontend && npm run test:browser:ci

- name: Upload browser correctness reports
if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: frontend-browser-correctness
path: frontend/test-results/ci/
retention-days: 7
if-no-files-found: warn

# ──────────────────────────────────────────────
# Companion: Node.js + Rust + Tauri tests
# ──────────────────────────────────────────────
Expand Down Expand Up @@ -241,17 +265,12 @@ jobs:
needs: [changes, backend, frontend, companion, website-docs]
runs-on: ubuntu-latest
steps:
- name: Checkout gate script
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Check results
env:
NEEDS_JSON: ${{ toJSON(needs) }}
EVENT_NAME: ${{ github.event_name }}
run: |
if [[ "${{ needs.changes.result }}" != "success" ]]; then
echo "Change detection failed or was cancelled: ${{ needs.changes.result }}"
exit 1
fi
results="${{ needs.backend.result }} ${{ needs.frontend.result }} ${{ needs.companion.result }} ${{ needs.website-docs.result }}"
for r in $results; do
if [[ "$r" != "success" && "$r" != "skipped" ]]; then
echo "Job failed or was cancelled: $r"
exit 1
fi
done
echo "All tests passed or were skipped."
python3 scripts/check_ci_test_gate.py
Loading
Loading