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
6 changes: 5 additions & 1 deletion .github/workflows/build-ci-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ on:
- 'Dockerfile.test'
- '.github/workflows/build-ci-image.yml'

concurrency:
group: build-ci-environment-image-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false

# Least-privilege default: read-only GITHUB_TOKEN at the top level.
# packages:write is granted only to the job that pushes to GHCR (Scorecard TokenPermissions, alert #39).
permissions:
Expand Down Expand Up @@ -42,7 +46,7 @@ jobs:
- name: Lowercase repository owner
id: lowercase_owner
run: |
echo "owner=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT
echo "owner=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT"

- name: Extract metadata for Docker
id: meta
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/clusterfuzzlite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ name: clusterfuzzlite

on:
pull_request:
paths-ignore:
- "docs/**"
- "manual/**"
- "**.md"
workflow_dispatch:

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

permissions:
contents: read

Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ name: codeql
on:
push:
branches: [main, develop]
pull_request:
schedule:
- cron: '43 5 * * 1'

concurrency:
group: codeql-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: false

permissions:
actions: read
contents: read
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/container-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ name: container-image

on:
pull_request:
paths-ignore:
- "docs/**"
- "manual/**"
- "**.md"
push:
tags:
- 'v*'
Expand All @@ -12,6 +16,10 @@ on:
required: false
default: 'false'

concurrency:
group: container-image-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false

permissions:
contents: read

Expand Down
23 changes: 0 additions & 23 deletions .github/workflows/dependency-review.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ permissions:
contents: read

concurrency:
group: github-pages
cancel-in-progress: true
group: deploy-web-manual-to-github-pages-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: false

jobs:
build:
Expand Down
37 changes: 0 additions & 37 deletions .github/workflows/quality-gate.yml

This file was deleted.

4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- 'v*'
workflow_dispatch:

concurrency:
group: release-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ name: scorecards
on:
push:
branches: [develop]
pull_request:
schedule:
- cron: '31 5 * * 1'

concurrency:
group: scorecards-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: false

permissions: {}

jobs:
Expand All @@ -32,10 +35,9 @@ jobs:
repo_token: ${{ secrets.SCORECARD_TOKEN || github.token }}
results_file: results.sarif
results_format: sarif
publish_results: ${{ github.event_name != 'pull_request' }}
publish_results: true

- name: Upload SARIF results
if: github.event_name != 'pull_request'
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
name: tests

on:
push:
branches: [main, develop]
pull_request:

concurrency:
group: tests-${{ github.repository }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
packages: read
Expand Down Expand Up @@ -35,4 +37,4 @@ jobs:
env:
PYTHONWARNINGS: error
PYTHONPATH: src
run: uv run pytest --cov=src/newsdom_api --cov-branch --cov-report=term-missing --cov-fail-under=100
run: uv run pytest --cov=src/newsdom_api --cov-branch --cov-report=term-missing --cov-fail-under=100
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ follow it.
DB-backed KV is fine) unless a dedicated KV is adopted.
- **This repo today:** no runtime secrets or credentials — it holds no
API keys, no DB creds, and makes no authenticated external calls (it
shells out to a local MinerU binary). CI secrets are only the
standard `GITHUB_TOKEN` / `SCORECARD_TOKEN`, which are build-time,
not runtime app secrets.
shells out to a local MinerU binary). CI uses the standard `GITHUB_TOKEN`
and an optional `SCORECARD_TOKEN` for the scheduled Scorecard backstop;
both are build-time, not runtime app secrets.
- **Known deviation to migrate:** `mineru_runner._resolve_mineru_bin`
reads `os.environ.get("NEWSDOM_MINERU_BIN")` (a local executable-path
override) at runtime. This is a deployment knob, not a secret, so it
Expand Down
7 changes: 0 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,6 @@ artifacts, generate SHA256 checksums, emit a JSON manifest, export
`*.intoto.jsonl` provenance bundles, and publish a GitHub Release with
provenance attestation.

For full OpenSSF Scorecard branch-protection visibility against
classic GitHub branch protection rules, set a repository secret named
`SCORECARD_TOKEN` with the fine-grained administration-read scope
recommended by the Scorecard Action documentation. Without that
secret, Scorecard still runs but may report the Branch-Protection
check as inconclusive.

## Fixture policy

This project intentionally separates public test artifacts from
Expand Down
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ budget on each pull request. Targets cover the untrusted-input boundaries: the
MinerU DOM normalizer (`build_dom`), the `ParseResponse` schema validator, and
the equivalence metrics normalizer. See `docs/papers/` for background.

The repository also enforces a `quality-gate` workflow with 100% source
coverage and docstring audit coverage.
The repository-local `tests` workflow enforces 100% source coverage and
docstring audit coverage once per pull-request head.

## Fixtures and provenance

Expand All @@ -221,9 +221,16 @@ maintenance are documented in `CONTRIBUTING.md`.

Mechanical branch updates and merges are attributed to `github-actions[bot]`.
Scratch PoC files are not committed. Failed GitHub Checks are not reviewed as URL lists.
OpenCode Review, Strix Security Scan, and PR Review Merge Scheduler are
provided by the organization-level required workflows in
`ContextualWisdomLab/.github`, not copied into this repository.
CodeQL PR, Noema Review, OpenCode Review, Security Scan, Strix Security Scan,
SAST Semgrep, and PR Review Merge Scheduler are provided by the organization
required-workflow ruleset, not copied into this repository. This ownership was
audited against `ContextualWisdomLab/.github` main commit
`769691526f8c73cf714de8fe8ba51ae6cfa2901a`. Security Scan is the single PR
owner for dependency review and Scorecard. `pytest` is the sole
repository-local required check; fuzzing and container builds remain local
because they validate NewsDOM-specific inputs and delivery artifacts. Local
CodeQL and Scorecard workflows retain only default-branch and scheduled
backstops; they do not run for pull requests.

Security reporting guidance is documented in `SECURITY.md`.
Version tags trigger a GitHub-native release workflow that builds
Expand Down
42 changes: 42 additions & 0 deletions docs/doctoring/actions-consolidation-20260904.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# GitHub Actions ownership consolidation

## Scope

This audit compares `newsdom-api` `develop` at
`e06b1f3fb10903569124af011da213951e6e2473` with the central required-workflow
source at `ContextualWisdomLab/.github` main
`769691526f8c73cf714de8fe8ba51ae6cfa2901a`. Product code is outside scope.

## Ownership decision

The organization ruleset injects these seven required workflows from the
central repository:

- `codeql-pr.yml`
- `noema-review.yml`
- `opencode-review.yml`
- `pr-review-merge-scheduler.yml`
- `security-scan.yml`
- `strix.yml`
- `sast-semgrep.yml`

`security-scan.yml` already owns PR dependency review and Scorecard work.
Accordingly, the local CodeQL and Scorecard PR triggers are removed while their
default-branch and scheduled backstops remain. The local dependency-review and
duplicate quality-gate workflows are removed. The remaining `tests` workflow
preserves the stricter all-extras install and the same 100% branch-coverage
command, so no product test is dropped.

Repository-local workflow files fall from 10 to 8. An ordinary source-code PR
falls from seven local workflow runs to three: tests, ClusterFuzzLite, and the
container build. Documentation-only PRs run only tests locally. ClusterFuzzLite
and container builds ignore documentation-only changes.

PR validation workflows use a fixed workflow-name, repository, and PR-number
group and cancel only an older run for the same PR. Image, Pages, and release
workflows serialize by repository and PR or ref with cancellation disabled.
There were no local sleep or queue-sweep steps to retain or remove.

The repository ruleset keeps strict required checks and the `pytest` GitHub
Actions context. The four deleted local contexts are replaced by the central
required-workflow ruleset rather than being bypassed by local copies.
18 changes: 12 additions & 6 deletions tests/test_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@ def test_pull_request_template_exists():
assert Path(".github/pull_request_template.md").exists()


def test_security_workflows_exist():
assert Path(".github/workflows/scorecards.yml").exists()
assert Path(".github/workflows/codeql.yml").exists()
assert Path(".github/workflows/dependency-review.yml").exists()
def test_central_required_pr_workflows_are_not_duplicated_locally():
for workflow_name in [
"dependency-review.yml",
"quality-gate.yml",
]:
assert not Path(".github/workflows", workflow_name).exists()

assert Path(".github/workflows/codeql.yml").exists()
assert Path(".github/workflows/scorecards.yml").exists()

def test_quality_gate_workflow_exists():
assert Path(".github/workflows/quality-gate.yml").exists()
text = Path("README.md").read_text(encoding="utf-8")
normalized_text = " ".join(text.split())
assert "769691526f8c73cf714de8fe8ba51ae6cfa2901a" in text
assert "`pytest` is the sole repository-local required check" in normalized_text
22 changes: 4 additions & 18 deletions tests/test_repository_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,19 @@ def test_codeowners_exists_and_covers_repository() -> None:
assert "@seonghobae" in rules["manual/"]


def test_codeql_scans_python_and_actions_with_required_check_name() -> None:
def test_codeql_backstop_scans_python_and_actions() -> None:
workflow = yaml.safe_load(
Path(".github/workflows/codeql.yml").read_text(encoding="utf-8")
)
analyze_job = workflow["jobs"]["analyze"]
assert analyze_job["name"] == "codeql (python, actions)"

init_step = next(
step
for step in analyze_job["steps"]
if step.get("uses", "").startswith("github/codeql-action/init@")
)
languages = init_step["with"]["languages"]
if isinstance(languages, str):
normalized_languages = {
language.strip().lower()
for language in languages.split(",")
if language.strip()
}
else:
normalized_languages = {
str(language).strip().lower()
for language in languages
if str(language).strip()
}

assert normalized_languages == {"python", "actions"}

assert analyze_job["name"] == "codeql (python, actions)"
assert init_step["with"]["languages"] == "python, actions"


def test_gitignore_declares_site_only_once() -> None:
Expand Down
12 changes: 0 additions & 12 deletions tests/test_truth_source_alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,6 @@ def test_gh_pages_workflow_targets_supported_branches_only() -> None:
assert set(branches) == {"main", "develop"}


def test_security_gate_docs_use_current_codeql_check_name() -> None:
paths = [
Path("docs/plans/2026-04-08-security-gates.md"),
Path("docs/plans/2026-04-08-security-gates-design.md"),
]

for path in paths:
text = path.read_text(encoding="utf-8")
assert "codeql (python, actions)" in text
assert "codeql (python)" not in text


def test_adr_follow_up_drops_stale_issue_references() -> None:
text = Path("docs/adr/0001-openssf-best-practices-badge.md").read_text(
encoding="utf-8"
Expand Down
Loading
Loading