feat: Enterprise DSS architecture implementation
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Complete implementation of enterprise design system validation: Phase 1 - @dss/rules npm package: - CLI with validate and init commands - 16 rules across 5 categories (colors, spacing, typography, components, a11y) - dss-ignore support (inline and next-line) - Break-glass [dss-skip] for emergency merges - CI workflow templates (Gitea, GitHub, GitLab) Phase 2 - Metrics dashboard: - FastAPI metrics API with SQLite storage - Portfolio-wide metrics aggregation - Project drill-down with file:line:column violations - Trend charts and history tracking Phase 3 - Local analysis cache: - LocalAnalysisCache for offline-capable validation - Mode detection (LOCAL/REMOTE/CI) - Stale cache warnings with recommendations Phase 4 - Project onboarding: - dss-init command for project setup - Creates ds.config.json, .dss/ folder structure - Updates .gitignore and package.json scripts - Optional CI workflow setup Architecture decisions: - No commit-back: CI uploads to dashboard, not git - Three-tier: Dashboard (read-only) → CI (authoritative) → Local (advisory) - Pull-based rules via npm for version control 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
23
packages/dss-rules/templates/ds.config.json
Normal file
23
packages/dss-rules/templates/ds.config.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://dss.overbits.luz.uy/schemas/ds.config.schema.json",
|
||||
"name": "{{PROJECT_NAME}}",
|
||||
"version": "1.0.0",
|
||||
"rules": {
|
||||
"package": "@dss/rules",
|
||||
"version": "^1.0.0"
|
||||
},
|
||||
"analysis": {
|
||||
"include": ["src/**/*.{ts,tsx,js,jsx,css,scss}"],
|
||||
"exclude": ["**/node_modules/**", "**/*.test.*", "**/*.spec.*"],
|
||||
"output": ".dss/analysis_graph.json"
|
||||
},
|
||||
"metrics": {
|
||||
"upload": true,
|
||||
"dashboardUrl": "https://dss.overbits.luz.uy/api/metrics"
|
||||
},
|
||||
"ci": {
|
||||
"blocking": true,
|
||||
"skipPattern": "[dss-skip]",
|
||||
"baselineBranch": "main"
|
||||
}
|
||||
}
|
||||
1
packages/dss-rules/templates/dss-folder/.gitkeep
Normal file
1
packages/dss-rules/templates/dss-folder/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This folder is created by DSS initialization
|
||||
122
packages/dss-rules/templates/gitea-workflow.yml
Normal file
122
packages/dss-rules/templates/gitea-workflow.yml
Normal file
@@ -0,0 +1,122 @@
|
||||
name: DSS Design System Validation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['*']
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
env:
|
||||
DSS_MODE: ci
|
||||
DSS_DASHBOARD_URL: ${{ vars.DSS_DASHBOARD_URL || 'https://dss.overbits.luz.uy/api/metrics' }}
|
||||
|
||||
jobs:
|
||||
dss-validate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Check for [dss-skip] in commit message
|
||||
id: skip-check
|
||||
run: |
|
||||
COMMIT_MSG=$(git log -1 --pretty=%B)
|
||||
if echo "$COMMIT_MSG" | grep -q "\[dss-skip\]"; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "::warning::DSS validation skipped via [dss-skip] flag"
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run DSS Rules Validation
|
||||
if: steps.skip-check.outputs.skip != 'true'
|
||||
id: validate
|
||||
run: |
|
||||
# Run validation and capture output
|
||||
npx dss-rules --ci --json src/ > dss-report.json 2>&1 || true
|
||||
|
||||
# Check results
|
||||
ERRORS=$(jq '.totalErrors' dss-report.json)
|
||||
WARNINGS=$(jq '.totalWarnings' dss-report.json)
|
||||
|
||||
echo "errors=$ERRORS" >> $GITHUB_OUTPUT
|
||||
echo "warnings=$WARNINGS" >> $GITHUB_OUTPUT
|
||||
|
||||
# Print summary
|
||||
echo "## DSS Validation Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Errors: $ERRORS" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Warnings: $WARNINGS" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "$ERRORS" -gt 0 ]; then
|
||||
echo "::error::DSS validation failed with $ERRORS errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check for version drift
|
||||
if: steps.skip-check.outputs.skip != 'true'
|
||||
run: |
|
||||
CURRENT_VERSION=$(npm list @dss/rules --json 2>/dev/null | jq -r '.dependencies["@dss/rules"].version // "unknown"')
|
||||
LATEST_VERSION=$(npm view @dss/rules version 2>/dev/null || echo "unknown")
|
||||
|
||||
if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ] && [ "$LATEST_VERSION" != "unknown" ]; then
|
||||
echo "::warning::@dss/rules version drift detected: using $CURRENT_VERSION, latest is $LATEST_VERSION"
|
||||
fi
|
||||
|
||||
- name: Upload metrics to dashboard
|
||||
if: steps.skip-check.outputs.skip != 'true' && always()
|
||||
run: |
|
||||
if [ -f dss-report.json ]; then
|
||||
# Extract metrics for upload
|
||||
jq '{
|
||||
project: "${{ github.repository }}",
|
||||
branch: "${{ github.ref_name }}",
|
||||
commit: "${{ github.sha }}",
|
||||
timestamp: now | todate,
|
||||
metrics: {
|
||||
totalFiles: .totalFiles,
|
||||
passedFiles: .passedFiles,
|
||||
failedFiles: .failedFiles,
|
||||
totalErrors: .totalErrors,
|
||||
totalWarnings: .totalWarnings,
|
||||
rulesVersion: .rulesVersion
|
||||
},
|
||||
fileResults: [.fileResults[] | {
|
||||
file: .file,
|
||||
errors: (.errors | length),
|
||||
warnings: (.warnings | length),
|
||||
violations: [.errors[], .warnings[] | {
|
||||
rule: .rule,
|
||||
line: .line,
|
||||
column: .column
|
||||
}]
|
||||
}]
|
||||
}' dss-report.json > metrics-payload.json
|
||||
|
||||
# Upload to dashboard (non-blocking)
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${{ secrets.DSS_API_TOKEN }}" \
|
||||
-d @metrics-payload.json \
|
||||
"$DSS_DASHBOARD_URL/upload" \
|
||||
--fail-with-body || echo "::warning::Failed to upload metrics to dashboard"
|
||||
fi
|
||||
|
||||
- name: Upload validation report artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dss-validation-report
|
||||
path: dss-report.json
|
||||
retention-days: 30
|
||||
152
packages/dss-rules/templates/github-workflow.yml
Normal file
152
packages/dss-rules/templates/github-workflow.yml
Normal file
@@ -0,0 +1,152 @@
|
||||
# DSS Design System Validation - GitHub Actions
|
||||
# Generated by @dss/rules init
|
||||
#
|
||||
# This workflow validates design system compliance and uploads metrics
|
||||
# to the DSS dashboard for portfolio-wide visibility.
|
||||
#
|
||||
# Required Secrets:
|
||||
# DSS_DASHBOARD_URL: URL to DSS metrics API (e.g., https://dss.example.com)
|
||||
# DSS_API_TOKEN: Authentication token for metrics upload
|
||||
|
||||
name: DSS Validate
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, develop]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
env:
|
||||
NODE_VERSION: '20'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Design System Validation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Full history for baseline comparison
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# Check for break-glass [dss-skip] in commit message
|
||||
- name: Check for [dss-skip]
|
||||
id: skip-check
|
||||
run: |
|
||||
COMMIT_MSG=$(git log -1 --pretty=%B)
|
||||
if echo "$COMMIT_MSG" | grep -q '\[dss-skip\]'; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "::warning::DSS validation skipped via [dss-skip] commit message"
|
||||
echo "::warning::Commit: $(git log -1 --pretty='%h %s')"
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check @dss/rules version drift
|
||||
- name: Check rules version
|
||||
if: steps.skip-check.outputs.skip != 'true'
|
||||
run: |
|
||||
INSTALLED=$(npm list @dss/rules --json 2>/dev/null | jq -r '.dependencies["@dss/rules"].version // "not-installed"')
|
||||
LATEST=$(npm view @dss/rules version 2>/dev/null || echo "unknown")
|
||||
|
||||
echo "Installed @dss/rules: $INSTALLED"
|
||||
echo "Latest @dss/rules: $LATEST"
|
||||
|
||||
if [ "$INSTALLED" != "$LATEST" ] && [ "$LATEST" != "unknown" ]; then
|
||||
echo "::warning::@dss/rules is outdated ($INSTALLED vs $LATEST). Consider updating."
|
||||
fi
|
||||
|
||||
# Run DSS validation
|
||||
- name: Run DSS validation
|
||||
if: steps.skip-check.outputs.skip != 'true'
|
||||
id: validate
|
||||
run: |
|
||||
# Run validation with CI mode (strict, JSON output)
|
||||
npm run dss:validate:ci || echo "validation_failed=true" >> $GITHUB_OUTPUT
|
||||
|
||||
# Extract summary for PR comment
|
||||
if [ -f .dss/results.json ]; then
|
||||
ERRORS=$(jq -r '.metrics.totalErrors // 0' .dss/results.json)
|
||||
WARNINGS=$(jq -r '.metrics.totalWarnings // 0' .dss/results.json)
|
||||
SCORE=$(jq -r '.metrics.adoptionScore // 0' .dss/results.json)
|
||||
|
||||
echo "errors=$ERRORS" >> $GITHUB_OUTPUT
|
||||
echo "warnings=$WARNINGS" >> $GITHUB_OUTPUT
|
||||
echo "score=$SCORE" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Upload metrics to DSS dashboard
|
||||
- name: Upload metrics to dashboard
|
||||
if: steps.skip-check.outputs.skip != 'true' && always()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
if [ ! -f .dss/results.json ]; then
|
||||
echo "No results file found, skipping upload"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Add git metadata to results
|
||||
jq --arg branch "${{ github.ref_name }}" \
|
||||
--arg commit "${{ github.sha }}" \
|
||||
--arg repo "${{ github.repository }}" \
|
||||
'. + {branch: $branch, commit: $commit, project: $repo}' \
|
||||
.dss/results.json > .dss/upload.json
|
||||
|
||||
curl -X POST "${DSS_DASHBOARD_URL}/api/metrics/upload" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${DSS_API_TOKEN}" \
|
||||
-d @.dss/upload.json \
|
||||
--fail --silent --show-error
|
||||
env:
|
||||
DSS_DASHBOARD_URL: ${{ secrets.DSS_DASHBOARD_URL }}
|
||||
DSS_API_TOKEN: ${{ secrets.DSS_API_TOKEN }}
|
||||
|
||||
# Comment on PR with results
|
||||
- name: Comment on PR
|
||||
if: github.event_name == 'pull_request' && steps.skip-check.outputs.skip != 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const errors = '${{ steps.validate.outputs.errors }}' || '0';
|
||||
const warnings = '${{ steps.validate.outputs.warnings }}' || '0';
|
||||
const score = '${{ steps.validate.outputs.score }}' || 'N/A';
|
||||
|
||||
const status = errors === '0' ? '✅' : '❌';
|
||||
const body = `## ${status} DSS Validation Results
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Adoption Score | ${score}% |
|
||||
| Errors | ${errors} |
|
||||
| Warnings | ${warnings} |
|
||||
|
||||
${errors !== '0' ? '⚠️ Please fix design system violations before merging.' : '🎉 All design system checks passed!'}
|
||||
|
||||
---
|
||||
*Powered by @dss/rules*`;
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: body
|
||||
});
|
||||
|
||||
# Fail if validation errors (authoritative enforcement)
|
||||
- name: Check validation result
|
||||
if: steps.skip-check.outputs.skip != 'true'
|
||||
run: |
|
||||
if [ "${{ steps.validate.outputs.validation_failed }}" = "true" ]; then
|
||||
echo "::error::DSS validation failed with errors. Please fix violations."
|
||||
exit 1
|
||||
fi
|
||||
9
packages/dss-rules/templates/gitignore-additions.txt
Normal file
9
packages/dss-rules/templates/gitignore-additions.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# DSS Design System (generated files - do not commit)
|
||||
.dss/analysis_graph.json
|
||||
.dss/cache/
|
||||
.dss/metrics.json
|
||||
.dss/baseline.json
|
||||
|
||||
# Keep config and schema
|
||||
!.dss/ds.config.json
|
||||
!.dss/.gitkeep
|
||||
126
packages/dss-rules/templates/gitlab-ci.yml
Normal file
126
packages/dss-rules/templates/gitlab-ci.yml
Normal file
@@ -0,0 +1,126 @@
|
||||
# DSS Design System Validation - GitLab CI
|
||||
# Generated by @dss/rules init
|
||||
#
|
||||
# This workflow validates design system compliance and uploads metrics
|
||||
# to the DSS dashboard for portfolio-wide visibility.
|
||||
#
|
||||
# Required Variables:
|
||||
# DSS_DASHBOARD_URL: URL to DSS metrics API (e.g., https://dss.example.com)
|
||||
# DSS_API_TOKEN: Authentication token for metrics upload
|
||||
|
||||
stages:
|
||||
- validate
|
||||
|
||||
variables:
|
||||
NODE_VERSION: "20"
|
||||
|
||||
.node-cache:
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
paths:
|
||||
- node_modules/
|
||||
- .npm/
|
||||
|
||||
dss-validate:
|
||||
stage: validate
|
||||
image: node:${NODE_VERSION}
|
||||
extends: .node-cache
|
||||
script:
|
||||
# Install dependencies
|
||||
- npm ci --cache .npm --prefer-offline
|
||||
|
||||
# Check for break-glass [dss-skip] in commit message
|
||||
- |
|
||||
COMMIT_MSG=$(git log -1 --pretty=%B)
|
||||
if echo "$COMMIT_MSG" | grep -q '\[dss-skip\]'; then
|
||||
echo "⚠️ DSS validation skipped via [dss-skip] commit message"
|
||||
echo "Commit: $(git log -1 --pretty='%h %s')"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check @dss/rules version drift
|
||||
- |
|
||||
INSTALLED=$(npm list @dss/rules --json 2>/dev/null | jq -r '.dependencies["@dss/rules"].version // "not-installed"')
|
||||
LATEST=$(npm view @dss/rules version 2>/dev/null || echo "unknown")
|
||||
echo "Installed @dss/rules: $INSTALLED"
|
||||
echo "Latest @dss/rules: $LATEST"
|
||||
if [ "$INSTALLED" != "$LATEST" ] && [ "$LATEST" != "unknown" ]; then
|
||||
echo "⚠️ @dss/rules is outdated ($INSTALLED vs $LATEST). Consider updating."
|
||||
fi
|
||||
|
||||
# Run DSS validation
|
||||
- npm run dss:validate:ci || VALIDATION_FAILED=true
|
||||
|
||||
# Upload metrics to dashboard
|
||||
- |
|
||||
if [ -f .dss/results.json ]; then
|
||||
jq --arg branch "$CI_COMMIT_REF_NAME" \
|
||||
--arg commit "$CI_COMMIT_SHA" \
|
||||
--arg repo "$CI_PROJECT_PATH" \
|
||||
'. + {branch: $branch, commit: $commit, project: $repo}' \
|
||||
.dss/results.json > .dss/upload.json
|
||||
|
||||
curl -X POST "${DSS_DASHBOARD_URL}/api/metrics/upload" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${DSS_API_TOKEN}" \
|
||||
-d @.dss/upload.json \
|
||||
--fail --silent --show-error || echo "⚠️ Failed to upload metrics (non-blocking)"
|
||||
fi
|
||||
|
||||
# Fail if validation errors
|
||||
- |
|
||||
if [ "$VALIDATION_FAILED" = "true" ]; then
|
||||
echo "❌ DSS validation failed with errors. Please fix violations."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- .dss/results.json
|
||||
expire_in: 1 week
|
||||
reports:
|
||||
codequality: .dss/results.json
|
||||
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "develop"
|
||||
- if: $CI_MERGE_REQUEST_IID
|
||||
|
||||
# Optional: MR comment with results (requires GITLAB_TOKEN with API access)
|
||||
dss-mr-comment:
|
||||
stage: validate
|
||||
image: curlimages/curl:latest
|
||||
needs:
|
||||
- job: dss-validate
|
||||
artifacts: true
|
||||
script:
|
||||
- |
|
||||
if [ ! -f .dss/results.json ]; then
|
||||
echo "No results file, skipping MR comment"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ERRORS=$(jq -r '.metrics.totalErrors // 0' .dss/results.json)
|
||||
WARNINGS=$(jq -r '.metrics.totalWarnings // 0' .dss/results.json)
|
||||
SCORE=$(jq -r '.metrics.adoptionScore // 0' .dss/results.json)
|
||||
|
||||
if [ "$ERRORS" = "0" ]; then
|
||||
STATUS="✅"
|
||||
MESSAGE="🎉 All design system checks passed!"
|
||||
else
|
||||
STATUS="❌"
|
||||
MESSAGE="⚠️ Please fix design system violations before merging."
|
||||
fi
|
||||
|
||||
BODY="## $STATUS DSS Validation Results\n\n| Metric | Value |\n|--------|-------|\n| Adoption Score | ${SCORE}% |\n| Errors | $ERRORS |\n| Warnings | $WARNINGS |\n\n$MESSAGE\n\n---\n*Powered by @dss/rules*"
|
||||
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"body\": \"$BODY\"}" \
|
||||
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes" \
|
||||
|| echo "⚠️ Failed to post MR comment (non-blocking)"
|
||||
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_IID && $GITLAB_TOKEN
|
||||
allow_failure: true
|
||||
Reference in New Issue
Block a user