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
86 changes: 46 additions & 40 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ on:
branches:
- master
- main
pull_request_target:
branches:
- master
- main

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
Expand All @@ -23,6 +22,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Node.js
Expand All @@ -49,6 +49,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Node.js
Expand Down Expand Up @@ -82,6 +83,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Node.js
Expand All @@ -94,39 +96,40 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Run npm audit
run: npm audit --audit-level=high
continue-on-error: true

- name: Check for known vulnerabilities
run: |
# Run npm audit and capture output
npm audit --audit-level=moderate --json > audit-results.json || true

# Check if there are any vulnerabilities
if [ -s audit-results.json ]; then
echo "Security vulnerabilities found:"
cat audit-results.json

# Count vulnerabilities
HIGH_VULNS=$(cat audit-results.json | jq -r '.metadata.vulnerabilities.high // 0')
CRITICAL_VULNS=$(cat audit-results.json | jq -r '.metadata.vulnerabilities.critical // 0')

echo "Critical vulnerabilities: $CRITICAL_VULNS"
echo "High vulnerabilities: $HIGH_VULNS"
audit_status=0
npm audit --audit-level=critical --json > audit-results.json || audit_status=$?
cat audit-results.json

# Missing counts or error responses must not be treated as a clean audit.
if ! jq -e -s '
length == 1 and (.[0] |
type == "object" and .error == null and
(.metadata.vulnerabilities |
type == "object" and
([.info, .low, .moderate, .high, .critical] |
all(.[]; type == "number" and . >= 0 and . == floor))))
' audit-results.json > /dev/null; then
echo "::error::npm audit returned an invalid or error report (exit status $audit_status)."
exit 1
fi

# Fail if critical vulnerabilities are found
if [ "$CRITICAL_VULNS" -gt 0 ]; then
echo "❌ Critical vulnerabilities found. Please fix them before merging."
exit 1
fi
HIGH_VULNS=$(jq -r '.metadata.vulnerabilities.high' audit-results.json)
CRITICAL_VULNS=$(jq -r '.metadata.vulnerabilities.critical' audit-results.json)
echo "Critical vulnerabilities: $CRITICAL_VULNS"
echo "High vulnerabilities: $HIGH_VULNS"

# Warn about high vulnerabilities but don't fail
if [ "$HIGH_VULNS" -gt 0 ]; then
echo "⚠️ High vulnerabilities found. Consider fixing them."
fi
else
echo "✅ No security vulnerabilities found."
if [ "$HIGH_VULNS" -gt 0 ]; then
echo "::warning::High vulnerabilities found. Consider fixing them."
fi
if [ "$CRITICAL_VULNS" -gt 0 ]; then
echo "::error::Critical vulnerabilities found. Please fix them before merging."
exit 1
fi
if [ "$audit_status" -ne 0 ]; then
echo "::error::npm audit failed with exit status $audit_status."
exit 1
fi

dependency-review:
Expand All @@ -137,6 +140,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Dependency Review
uses: actions/dependency-review-action@v4
Expand All @@ -156,6 +161,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Node.js
Expand Down Expand Up @@ -192,12 +198,12 @@ jobs:
echo "Dependency Review: ${{ needs.dependency-review.result }}"
echo "Build Dry Run: ${{ needs.build-dry-run.result }}"

# Check if any required job failed
if [[ "${{ needs.lint-and-type-check.result }}" == "failure" ||
"${{ needs.test.result }}" == "failure" ||
"${{ needs.security.result }}" == "failure" ||
"${{ needs.build-dry-run.result }}" == "failure" ]]; then
echo "❌ One or more required checks failed"
# Require every blocking job to complete successfully
if [[ "${{ needs.lint-and-type-check.result }}" != "success" ||
"${{ needs.test.result }}" != "success" ||
"${{ needs.security.result }}" != "success" ||
"${{ needs.build-dry-run.result }}" != "success" ]]; then
echo "❌ One or more required checks did not succeed"
exit 1
else
echo "✅ All critical checks passed!"
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
run: npm install

- name: Upload images to CDN and generate JSON file
id: capture
run: |
if [ "${{ github.event.inputs.force_update }}" = "true" ]; then
npm run dev -- --force
Expand All @@ -49,16 +50,18 @@ jobs:
IMAGEKIT_PRIVATE_KEY: ${{ secrets.IMAGEKIT_PRIVATE_KEY }}

- name: Commit new screenshots
if: ${{ !cancelled() && (steps.capture.outcome == 'success' || steps.capture.outcome == 'failure') }}
uses: stefanzweifel/git-auto-commit-action@v5
with:
file_pattern: "screenshots screenshots.json"
file_pattern: "screenshots screenshots.json screenshots.metadata.json"
branch: ${{ github.head_ref }}
commit_message: Commit new screenshots
commit_user_name: Analysis Tools Bot
commit_user_email: bot@analysis-tools.dev
commit_author: Analysis Tools Bot <bot@analysis-tools.dev>

- name: Redeploy website
if: ${{ success() && steps.capture.outcome == 'success' }}
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.REPO_ACCESS_TOKEN }}
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,25 @@ Screenshots are created via a Github actions pipeline and some glue code.
```
npm run dev
```

### Freshness and retries

`screenshots.json` keeps its existing public format. The sidecar
`screenshots.metadata.json` records capture and publication timestamps (Unix
milliseconds) and the CDN path for each tool URL. A screenshot is fresh for five
days after capture only if its upload succeeded and its recorded CDN path matches
the manifest. Local file timestamps do not count: a checkout must not make an old
screenshot look new. Missing metadata, including legacy entries, means stale.
The first run after this change will therefore refresh legacy screenshots.

`npm run dev -- --force` ignores freshness. Failed captures or uploads keep the
last successful mapping but clear its freshness metadata so the next run retries.
The command saves successful partial progress, reports capture and upload failure
counts, and exits nonzero if either count is nonzero. CI commits that progress,
including the sidecar, even when capture fails; the job stays failed and does not
trigger a website redeploy.

`npm run dev -- --dry-run` lists the work without capturing, uploading, or changing
local files, the manifest, or metadata. It still downloads the tool lists but does
not require ImageKit credentials. Combine it with `--force` to preview a full
refresh.
Loading
Loading