Skip to content
Open
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
62 changes: 62 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name-template: "v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"

categories:
- title: "New Features"
labels:
- "feature"
- "enhancement"
- title: "Bug Fixes"
labels:
- "fix"
- "bugfix"
- "bug"
- title: "iRacing"
labels:
- "iracing"
- title: "Dashboard"
labels:
- "dashboard"
- "ui"
- title: "Observability"
labels:
- "observability"
- "logging"
- "metrics"
- title: "Security"
labels:
- "security"
- title: "Dependencies"
labels:
- "dependencies"
- title: "CI/CD"
labels:
- "ci"
- "github-actions"

change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&'

version-resolver:
major:
labels:
- "major"
- "breaking"
minor:
labels:
- "feature"
- "enhancement"
patch:
labels:
- "fix"
- "bugfix"
- "bug"
- "dependencies"
default: patch

template: |
## Changes

$CHANGES

**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
27 changes: 27 additions & 0 deletions .github/workflows/cloudflare-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Deploy Dashboards to Cloudflare Pages

on:
push:
branches: [main]
paths:
- "src/SimSteward.Dashboard/**"
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Deploy to Cloudflare Pages
# cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy src/SimSteward.Dashboard --project-name=sim-steward-dash
28 changes: 28 additions & 0 deletions .github/workflows/contextstream-index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: ContextStream Index Sync

on:
push:
branches: [main]
paths-ignore:
- "*.md"
- ".github/**"
- "docs/**"

jobs:
index:
runs-on: windows-latest

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Install ContextStream CLI
run: npm install -g contextstream-mcp

- name: Run ContextStream ingest
env:
CONTEXTSTREAM_API_URL: https://api.contextstream.io
CONTEXTSTREAM_PROJECT_ID: ${{ secrets.CONTEXTSTREAM_PROJECT_ID }}
CONTEXTSTREAM_WORKSPACE_ID: ${{ secrets.CONTEXTSTREAM_WORKSPACE_ID }}
CONTEXTSTREAM_API_KEY: ${{ secrets.CONTEXTSTREAM_API_KEY }}
run: pwsh -NoProfile -File scripts/contextstream-ingest.ps1 -Force
78 changes: 78 additions & 0 deletions .github/workflows/dashboard-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Dashboard Validation

on:
push:
paths:
- "src/SimSteward.Dashboard/**"
pull_request:
paths:
- "src/SimSteward.Dashboard/**"

jobs:
validate:
runs-on: ubuntu-latest

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Validate HTML files
run: |
errors=0
for f in src/SimSteward.Dashboard/*.html; do
echo "Checking $f..."

# Check for unclosed script/style tags
open_script=$(grep -c '<script' "$f" || true)
close_script=$(grep -c '</script>' "$f" || true)
if [ "$open_script" -ne "$close_script" ]; then
echo "::error file=$f::Mismatched <script> tags (open=$open_script, close=$close_script)"
errors=$((errors + 1))
fi

# Check for Sentry DSN not accidentally removed
if grep -q 'Sentry.init' "$f"; then
if ! grep -q 'dsn:' "$f"; then
echo "::error file=$f::Sentry.init found but no DSN configured"
errors=$((errors + 1))
fi
fi

# Check for console.log left in production (warning only)
count=$(grep -c 'console\.log' "$f" || true)
if [ "$count" -gt 5 ]; then
echo "::warning file=$f::$count console.log statements found - consider cleanup"
fi
done

if [ "$errors" -gt 0 ]; then
echo "::error::$errors validation error(s) found"
exit 1
fi
echo "All dashboard HTML files passed validation."

lighthouse:
runs-on: ubuntu-latest

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

# actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "20"

- name: Install Lighthouse CI
run: npm install -g @lhci/cli

- name: Run Lighthouse on dashboards
run: |
for f in src/SimSteward.Dashboard/*.html; do
echo "Running Lighthouse on $f..."
lhci autorun --collect.staticDistDir=src/SimSteward.Dashboard \
--collect.url="file://$(pwd)/$f" \
--assert.preset=lighthouse:no-pwa \
--assert.assertions.categories:accessibility=warn \
--assert.assertions.categories:best-practices=warn || true
done
33 changes: 33 additions & 0 deletions .github/workflows/nuget-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: NuGet Vulnerability Audit

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 7 * * 1"

jobs:
audit:
runs-on: windows-latest

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Restore packages
run: dotnet restore

- name: Check for vulnerable packages
run: dotnet list package --vulnerable --include-transitive 2>&1 | tee audit-output.txt

- name: Fail on vulnerabilities
shell: pwsh
run: |
$output = Get-Content audit-output.txt -Raw
if ($output -match "has the following vulnerable packages") {
Write-Error "Vulnerable NuGet packages detected. See output above."
exit 1
}
Write-Host "No known vulnerabilities found."
24 changes: 24 additions & 0 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Release Drafter

on:
push:
branches: [main]
pull_request:
types: [opened, reopened, synchronize, labeled]

permissions:
contents: read
pull-requests: write

jobs:
update-release-draft:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write

steps:
# release-drafter/release-drafter@v6
- uses: release-drafter/release-drafter@6a93d829887aa2e0748befe2e808c66c0ec6e4c7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 changes: 10 additions & 5 deletions .github/workflows/secrets-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ jobs:
secretlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
# pnpm/action-setup@v4
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320
# actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "20"
cache: pnpm
Expand All @@ -22,9 +25,11 @@ jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
# gitleaks/gitleaks-action@v2
- uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 changes: 52 additions & 0 deletions .github/workflows/sentry-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Sentry Release

on:
push:
tags: ["v*"]

jobs:
sentry-release:
runs-on: ubuntu-latest

steps:
# actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Create Sentry release
# getsentry/action-release@v3
uses: getsentry/action-release@dab6548b3c03c4717878099e43782cf5be654289
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: sim-steward
with:
projects: simhub-plugin web-dashboards
version: ${{ steps.version.outputs.version }}
set_commits: auto

- name: Register simhub-plugin deploy
if: env.SENTRY_AUTH_TOKEN != ''
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
curl -s -X POST \
"https://sentry.io/api/0/organizations/sim-steward/releases/${{ steps.version.outputs.version }}/deploys/" \
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"environment":"production","name":"simhub-plugin"}'

- name: Register web-dashboards deploy
if: env.SENTRY_AUTH_TOKEN != ''
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
curl -s -X POST \
"https://sentry.io/api/0/organizations/sim-steward/releases/${{ steps.version.outputs.version }}/deploys/" \
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"environment":"production","name":"web-dashboards"}'
54 changes: 54 additions & 0 deletions .github/workflows/statsig-gate-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: StatsIG Gate Check

on:
workflow_call:
inputs:
gate-name:
description: "StatsIG gate to check before proceeding"
required: true
type: string
environment:
description: "Target environment"
required: false
type: string
default: "production"
outputs:
gate-open:
description: "Whether the gate is open"
value: ${{ jobs.check-gate.outputs.gate-open }}

jobs:
check-gate:
runs-on: ubuntu-latest
outputs:
gate-open: ${{ steps.gate.outputs.open }}

steps:
- name: Check StatsIG gate
id: gate
env:
STATSIG_SERVER_KEY: ${{ secrets.STATSIG_SERVER_KEY }}
run: |
if [ -z "$STATSIG_SERVER_KEY" ]; then
echo "::warning::STATSIG_SERVER_KEY not set - defaulting gate to open"
echo "open=true" >> "$GITHUB_OUTPUT"
exit 0
fi

response=$(curl -s -X POST "https://statsigapi.net/v1/check_gate" \
-H "statsig-api-key: $STATSIG_SERVER_KEY" \
-H "Content-Type: application/json" \
-d "{
\"user\": { \"userID\": \"github-actions\", \"custom\": { \"environment\": \"${{ inputs.environment }}\" } },
\"gateName\": \"${{ inputs.gate-name }}\"
}")

gate_value=$(echo "$response" | jq -r '.value // false')
echo "Gate '${{ inputs.gate-name }}' = $gate_value"
echo "open=$gate_value" >> "$GITHUB_OUTPUT"

Comment on lines +31 to +49

Check failure

Code scanning / Semgrep OSS

Semgrep Finding: yaml.github-actions.security.run-shell-injection.run-shell-injection Error

Using variable interpolation ${...} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".
- name: Gate closed - block deploy
if: steps.gate.outputs.open != 'true'
run: |
echo "::error::StatsIG gate '${{ inputs.gate-name }}' is closed for environment '${{ inputs.environment }}'. Deploy blocked."
exit 1
Comment on lines +52 to +54

Check failure

Code scanning / Semgrep OSS

Semgrep Finding: yaml.github-actions.security.run-shell-injection.run-shell-injection Error

Using variable interpolation ${...} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".
Loading
Loading