Files
dashy/.github/workflows/ci.yml
2026-06-04 12:48:53 +01:00

330 lines
8.8 KiB
YAML

# CI checks to run when a PR is opened, or manually via workflow_dispatch
name: 🚦 CI
on:
pull_request:
branches: ['master', 'develop']
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: 🔎 Detect Changes
runs-on: ubuntu-latest
outputs:
lockfile: ${{ steps.filter.outputs.lockfile }}
workflows: ${{ steps.filter.outputs.workflows }}
locales: ${{ steps.filter.outputs.locales }}
translations: ${{ steps.filter.outputs.translations }}
src: ${{ steps.filter.outputs.src }}
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Filter Paths
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
lockfile:
- 'yarn.lock'
workflows:
- '.github/workflows/**'
locales:
- 'src/assets/locales/**'
- 'src/**/*.vue'
- 'src/**/*.js'
- 'tests/locales/**'
translations:
- 'src/assets/locales/**'
src:
- 'src/**'
- 'package.json'
- 'yarn.lock'
- 'eslint.config.mjs'
- 'tsconfig.json'
lint:
name: 🛡️ Lint
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'yarn'
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Run ESLint
run: yarn lint
typecheck:
name: 🦴 Typecheck
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'yarn'
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Run vue-tsc
run: yarn typecheck
test:
name: 🧪 Test
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'yarn'
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Run Tests
run: yarn test 2>&1 | tee test-report.txt
- name: Upload Test Report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: test-report.txt
if-no-files-found: ignore
locales:
name: 🌐 Locale Check
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.locales == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
- name: Check Locales
run: yarn validate-locales 2>&1 | tee locale-report.txt
- name: Upload Locale Report
if: always()
uses: actions/upload-artifact@v4
with:
name: locale-report
path: locale-report.txt
if-no-files-found: ignore
spellcheck:
name: ✏️ Spellcheck
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.translations == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Spellcheck en.json
uses: crate-ci/typos@v1
with:
files: src/assets/locales/en.json
build:
name: 🏗️ Build Check
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'yarn'
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Build Project
run: yarn build
- name: Verify Build Output
run: |
if [ ! -d "dist" ]; then
echo "❌ Build failed: dist directory not created"
exit 1
fi
if [ ! -f "dist/index.html" ]; then
echo "❌ Build failed: index.html not found"
exit 1
fi
echo "✅ Build successful"
docker-smoke:
name: 🐳 Docker Smoke Test
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Build & Test Docker Image
run: sh tests/docker-smoke-test.sh
timeout-minutes: 10
dependency-review:
name: 🔒 Dependency Audit
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.lockfile == 'true' && github.event_name == 'pull_request'
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Review Dependencies
uses: actions/dependency-review-action@v5
with:
fail-on-severity: moderate
secret-scan:
name: 🔑 Secret Scanning
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Scan PR Diff for Secrets
uses: trufflesecurity/trufflehog@v3.95.3
with:
base: ${{ github.event.pull_request.base.sha }}
head: ${{ github.event.pull_request.head.sha }}
extra_args: --only-verified
workflow-audit:
name: 🛠️ Workflow Audit
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.workflows == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Run Actionlint
uses: raven-actions/actionlint@v2
with:
fail-on-error: true
- name: Run Zizmor
uses: zizmorcore/zizmor-action@v0.5.6
with:
inputs: .github/workflows/
advanced-security: false
annotations: true
# Renders markdown summary of all checks at the end
summary:
name: 📋 Summary
runs-on: ubuntu-latest
if: always()
continue-on-error: true
needs:
- lint
- typecheck
- test
- locales
- spellcheck
- build
- docker-smoke
- dependency-review
- secret-scan
- workflow-audit
steps:
- name: Download Reports
uses: actions/download-artifact@v4
continue-on-error: true
with:
path: reports
merge-multiple: true
- name: Render Summary
env:
NEEDS: ${{ toJSON(needs) }}
run: |
label() {
case "$1" in
lint) echo "🛡️ Lint" ;;
typecheck) echo "🦴 Typecheck" ;;
test) echo "🧪 Test" ;;
locales) echo "🌐 Locale Check" ;;
spellcheck) echo "✏️ Spellcheck" ;;
build) echo "🏗️ Build" ;;
docker-smoke) echo "🐳 Docker Smoke" ;;
dependency-review) echo "🔒 Dependency Audit" ;;
secret-scan) echo "🔑 Secret Scan" ;;
workflow-audit) echo "🛠️ Workflow Audit" ;;
*) echo "$1" ;;
esac
}
status() {
case "$1" in
success) echo "✅ Passed" ;;
skipped) echo "⏭️ Skipped" ;;
cancelled) echo "🚫 Cancelled" ;;
failure) [ "$2" = docker-smoke ] && echo "⚠️ Warnings" || echo "❌ Failed" ;;
*) echo "❔ $1" ;;
esac
}
details() {
[ -f "$2" ] || return 0
printf '\n<details><summary>%s</summary>\n\n```\n' "$1" >> "$GITHUB_STEP_SUMMARY"
cat "$2" >> "$GITHUB_STEP_SUMMARY"
printf '\n```\n</details>\n' >> "$GITHUB_STEP_SUMMARY"
}
{
echo "## CI Summary"
echo ""
echo "| Check | Status |"
echo "|-------|--------|"
} >> "$GITHUB_STEP_SUMMARY"
for job in $(echo "$NEEDS" | jq -r 'keys[]'); do
result=$(echo "$NEEDS" | jq -r --arg j "$job" '.[$j].result')
echo "| $(label "$job") | $(status "$result" "$job") |" >> "$GITHUB_STEP_SUMMARY"
done
details "🧪 Test Report" reports/test-report.txt
details "🌐 Locale Report" reports/locale-report.txt