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
15 changes: 15 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: 'Setup Node.js environment'
description: 'Configure Node.js 22 with npm cache and install dependencies'

runs:
using: composite
steps:
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci
shell: bash
52 changes: 39 additions & 13 deletions .github/workflows/unit-tests.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Unit Tests
name: CI

on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
push:
branches:
- main
Expand All @@ -14,13 +16,16 @@ jobs:
changes:
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
# On scheduled runs there is no diff to compare — always run tests.
# On push/PR, only run when relevant files changed.
code: ${{ github.event_name == 'schedule' || steps.filter.outputs.code == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Detect relevant file changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
if: github.event_name != 'schedule'
id: filter
with:
filters: |
Expand All @@ -36,17 +41,8 @@ jobs:
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup

- name: Build generated files needed for tests
run: npm run build:milestones && npm run build:js
Expand All @@ -65,6 +61,23 @@ jobs:
verbose: true
fail_ci_if_error: false

test-e2e:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium

- name: Build generated files
run: npm run build

- name: Run E2E tests
run: npm run test:e2e

unit-test-status:
needs: [changes, test]
if: always()
Expand All @@ -77,3 +90,16 @@ jobs:
exit 1
fi
echo "Unit tests passed or were skipped (no relevant files changed)"

e2e-test-status:
needs: [changes, test-e2e]
if: always()
runs-on: ubuntu-latest
steps:
- name: Report status
run: |
if [[ "${{ needs.test-e2e.result }}" == "failure" ]]; then
echo "E2E tests failed"
exit 1
fi
echo "E2E tests passed or were skipped (no relevant files changed)"
29 changes: 4 additions & 25 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,11 @@ jobs:
name: github-pages
url: https://nitrocode.github.io/token-deathclock/
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup

- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Regenerate milestones-data.js from YAML
run: npm run build:milestones

- name: Regenerate changelog-data.js from CHANGELOG.md
run: npm run build:changelog

- name: Regenerate project-stats-data.js from YAML
run: npm run build:project-stats

- name: Rebuild script.js from src/js/ source files
run: npm run build:js

- name: Rebuild styles.css from styles/ source files
run: npm run build:css
- name: Build all generated files
run: npm run build

- name: Deploy to GitHub Pages (gh-pages branch)
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
Expand Down
71 changes: 0 additions & 71 deletions .github/workflows/e2e-tests.yml

This file was deleted.

17 changes: 4 additions & 13 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,8 @@ jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup

- name: Build all generated files
run: npm run build
Expand All @@ -35,8 +26,8 @@ jobs:
destination_dir: previews/pr-${{ github.event.number }}
# Preserve existing previews and production files
keep_files: true
# Exclude non-site files
exclude_assets: '.github,node_modules,tests,scripts,package-lock.json,package.json,milestones.yaml'
# Exclude non-site files (keep in sync with deploy.yml)
exclude_assets: '.github,node_modules,tests,scripts,package-lock.json,package.json,milestones.yaml,project-stats.yaml'

- name: Post or update preview URL comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/weekly-stats-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Weekly Project Stats Check

# Opens a reminder issue when the recorded pr_count in project-stats.yaml
# is lower than the actual number of merged PRs in the repository.

on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
check-stats:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Check project-stats.yaml against actual merged PR count
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');

// Read the recorded pr_count from project-stats.yaml
const yaml = fs.readFileSync('project-stats.yaml', 'utf8');
const match = yaml.match(/^pr_count:\s*(\d+)/m);
if (!match) {
throw new Error('Invalid or missing pr_count in project-stats.yaml');
}
const recordedCount = parseInt(match[1], 10);
if (!Number.isInteger(recordedCount)) {
throw new Error('Invalid or missing pr_count in project-stats.yaml');
}

// Count actual merged PRs via the GitHub API
let page = 1;
let totalMerged = 0;
while (true) {
const { data } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
per_page: 100,
page,
});
if (data.length === 0) break;
totalMerged += data.filter(pr => pr.merged_at !== null).length;
if (data.length < 100) break;
page++;
}

console.log(`Recorded pr_count: ${recordedCount}, actual merged PRs: ${totalMerged}`);

if (recordedCount >= totalMerged) {
console.log('project-stats.yaml is up to date — no action needed.');
return;
}

const title = `chore: update project-stats.yaml (recorded ${recordedCount}, actual ${totalMerged} merged PRs)`;
const body = [
'## 📊 Project Stats Are Out of Date',
'',
`**Recorded \`pr_count\`:** ${recordedCount}`,
`**Actual merged PRs:** ${totalMerged}`,
`**Difference:** ${totalMerged - recordedCount} untracked PR(s)`,
'',
'After the next agent session, update `project-stats.yaml`:',
'',
'```yaml',
`pr_count: ${totalMerged}`,
'```',
'',
'Then run `npm run build:project-stats` to regenerate `project-stats-data.js`, or let the deploy workflow handle it.',
].join('\n');

// Avoid duplicate open issues (paginate to cover all open items, exclude PRs)
const openIssues = await github.paginate(github.rest.issues.listForRepo, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
});
const duplicate = openIssues.find(i =>
i.title.startsWith('chore: update project-stats.yaml') && !i.pull_request
);
if (duplicate) {
console.log(`Reminder issue already open: #${duplicate.number} — skipping.`);
return;
}

const { data: issue } = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
});
console.log(`Created reminder issue: #${issue.number}`);
Loading
Loading