Skip to content
Draft
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
10 changes: 10 additions & 0 deletions .github/workflows/app-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Set up Python
Expand Down Expand Up @@ -84,6 +85,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Install pnpm
Expand Down Expand Up @@ -133,3 +135,11 @@ jobs:
NARUON_FULL_PRODUCT_BASE_URL: "http://127.0.0.1:3001"
NARUON_FULL_PRODUCT_SCREENSHOT_DIR: "/tmp/naruon-full-product-smoke"
run: cd frontend && pnpm run full:smoke

- name: Retain full product smoke PNG evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: naruon-full-product-smoke-${{ github.event.pull_request.number || github.run_id }}-${{ github.event.pull_request.head.sha || github.sha }}
path: /tmp/naruon-full-product-smoke-*/*.png
if-no-files-found: error
retention-days: 14
1 change: 1 addition & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Set up Python
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Log dependency review policy
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Set up QEMU
Expand Down Expand Up @@ -208,6 +209,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Read release version
Expand Down
43 changes: 43 additions & 0 deletions backend/tests/test_application_ci_evidence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Guard hosted buyer-facing evidence retention in Application CI."""

from __future__ import annotations

from pathlib import Path

import yaml


REPO_ROOT = Path(__file__).resolve().parents[2]
APPLICATION_CI_PATH = REPO_ROOT / ".github/workflows/app-ci.yml"
UPLOAD_ARTIFACT_SHA = "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"


def test_application_ci_retains_full_product_smoke_screenshot_evidence() -> None:
"""A passing browser smoke must publish PR-head-bound PNG evidence instead of discarding it."""
workflow_text = APPLICATION_CI_PATH.read_text(encoding="utf-8")
workflow = yaml.load(workflow_text, Loader=yaml.BaseLoader)
frontend_steps = workflow["jobs"]["frontend"]["steps"]

smoke_indices = [
index
for index, step in enumerate(frontend_steps)
if step.get("name") == "Run full product smoke"
]
upload_indices = [
index
for index, step in enumerate(frontend_steps)
if step.get("uses", "").startswith("actions/upload-artifact@")
]

assert len(smoke_indices) == 1
assert len(upload_indices) == 1
assert upload_indices[0] == smoke_indices[0] + 1

upload_step = frontend_steps[upload_indices[0]]
assert upload_step["uses"] == f"actions/upload-artifact@{UPLOAD_ARTIFACT_SHA}"
assert upload_step["with"] == {
"name": "naruon-full-product-smoke-${{ github.event.pull_request.number || github.run_id }}-${{ github.event.pull_request.head.sha || github.sha }}",
"path": "/tmp/naruon-full-product-smoke-*/*.png",
"if-no-files-found": "error",
"retention-days": "14",
}
35 changes: 35 additions & 0 deletions backend/tests/test_repo_local_pr_exact_head.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Guard exact-head source checkout in repository-owned pull-request validation."""

from __future__ import annotations

from pathlib import Path

import pytest
import yaml


REPO_ROOT = Path(__file__).resolve().parents[2]
PR_VALIDATION_WORKFLOWS = (
".github/workflows/app-ci.yml",
".github/workflows/bandit.yml",
".github/workflows/dependency-review.yml",
".github/workflows/docker-publish.yml",
)
EXPECTED_EXACT_REF = "${{ github.event.pull_request.head.sha || github.sha }}"


@pytest.mark.parametrize("workflow_path", PR_VALIDATION_WORKFLOWS)
def test_repo_local_validation_checks_out_exact_pr_head(workflow_path: str) -> None:
"""Do not report pull-request merge-tree execution as exact-head validation."""
workflow_text = (REPO_ROOT / workflow_path).read_text(encoding="utf-8")
workflow = yaml.load(workflow_text, Loader=yaml.BaseLoader)
checkout_steps = [
step
for job in workflow["jobs"].values()
for step in job.get("steps", [])
if step.get("uses", "").startswith("actions/checkout@")
]

assert checkout_steps
for checkout_step in checkout_steps:
assert checkout_step.get("with", {}).get("ref") == EXPECTED_EXACT_REF
Loading