diff --git a/.github/scripts/generate-lighthouse-badges.cjs b/.github/scripts/generate-lighthouse-badges.cjs new file mode 100644 index 0000000..b66f4a3 --- /dev/null +++ b/.github/scripts/generate-lighthouse-badges.cjs @@ -0,0 +1,122 @@ +const fs = require('fs'); +const path = require('path'); +const https = require('https'); + +// Path to LHCI manifest and output directory +const manifestPath = path.join(__dirname, '../../.lighthouseci/manifest.json'); +const outputDir = path.join(__dirname, '../../badges'); + +// Ensure output directory exists +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); +} + +/** + * Downloads a badge from Shields.io + * @param {string} label + * @param {string} value + * @param {string} color + * @returns {Promise} + */ +function downloadBadge(label, value, color) { + const url = `https://img.shields.io/badge/${encodeURIComponent(label)}-${encodeURIComponent(value)}-${color}`; + const filename = `${label.toLowerCase().replace(/ /g, '-')}.svg`; + const filePath = path.join(outputDir, filename); + + return new Promise((resolve, reject) => { + const req = https.get(url, { headers: { 'User-Agent': 'NodeJS/LighthouseBadgeGenerator' }, timeout: 10000 }, (res) => { + if (res.statusCode !== 200) { + res.resume(); + reject(new Error(`Failed to download ${label} badge: Status code ${res.statusCode}`)); + return; + } + const fileStream = fs.createWriteStream(filePath); + res.on('error', reject); + fileStream.on('error', reject); + res.pipe(fileStream); + fileStream.on('finish', () => { + fileStream.close(); + console.log(`Successfully downloaded: ${filename} (${value})`); + resolve(); + }); + }); + req.on('error', reject); + req.on('timeout', () => req.destroy(new Error(`Timed out downloading ${label} badge`))); + }); +} + +async function main() { + try { + if (!fs.existsSync(manifestPath)) { + console.error(`Error: Manifest file not found at ${manifestPath}`); + process.exit(1); + } + + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + if (!Array.isArray(manifest) || manifest.length === 0) { + console.error('Error: Manifest is empty or invalid.'); + process.exit(1); + } + + // Find the representative run (median run) or fallback to first run + const run = manifest.find(r => r.isRepresentativeRun) || manifest[0]; + const summary = run.summary; + + if (!summary) { + console.error('Error: No summary found in the representative run.'); + process.exit(1); + } + + // Categories to generate badges for + const categories = { + 'Performance': summary.performance, + 'Accessibility': summary.accessibility, + 'Best Practices': summary['best-practices'], + 'SEO': summary.seo, + 'PWA': summary.pwa + }; + + let totalScore = 0; + let count = 0; + + for (const [name, score] of Object.entries(categories)) { + if (score === undefined || score === null) { + console.log(`Skipping category ${name} (no score available)`); + continue; + } + + const percentage = Math.round(score * 100); + totalScore += percentage; + count++; + + // Determine color according to Lighthouse standards + let color = 'ff3333'; // Red (0-49) + if (percentage >= 90) { + color = '00cc66'; // Green (90-100) + } else if (percentage >= 50) { + color = 'ffaa33'; // Orange (50-89) + } + + await downloadBadge(`Lighthouse ${name}`, `${percentage}%`, color); + } + + // Generate single aggregate score badge + if (count > 0) { + const average = Math.round(totalScore / count); + let color = 'ff3333'; + if (average >= 90) { + color = '00cc66'; + } else if (average >= 50) { + color = 'ffaa33'; + } + await downloadBadge('Lighthouse', `${average}%`, color); + } + + console.log('Lighthouse badges generated successfully!'); + } catch (error) { + console.error('Error generating badges:', error); + process.exit(1); + } +} + +main(); diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml new file mode 100644 index 0000000..f0015e9 --- /dev/null +++ b/.github/workflows/lighthouse.yml @@ -0,0 +1,57 @@ +name: Lighthouse CI Audit + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +concurrency: + group: lighthouse-${{ github.ref }} + cancel-in-progress: true + +jobs: + lighthouse: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build application + run: npm run build + + - name: Run Lighthouse CI + uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 + with: + configPath: './lighthouserc.json' + temporaryPublicStorage: true + + - name: Generate Lighthouse Badges + run: node .github/scripts/generate-lighthouse-badges.cjs + + - name: Commit and Push Badges + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + env: + REF_NAME: ${{ github.ref_name }} + run: | + git config --local user.email "github-actions[bot]`@users.noreply.github.com`" + git config --local user.name "github-actions[bot]" + git add badges/ + git commit -m "chore: update lighthouse badges [skip ci]" || echo "No changes to commit" + git push origin HEAD:"$REF_NAME" + diff --git a/.gitignore b/.gitignore index 216a31b..1ecbf67 100644 --- a/.gitignore +++ b/.gitignore @@ -380,6 +380,7 @@ logs/ coverage/ playwright-report/ test-results/ +.lighthouseci/ # =============================== # IDE / Editor diff --git a/README.md b/README.md index 7cb9cea..ca05680 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,15 @@ +
+ Lighthouse Score   + Lighthouse Performance   + Lighthouse Accessibility   + Lighthouse Best Practices   + Lighthouse SEO   + Lighthouse PWA +
+ --- **OrgExplorer** transforms GitHub organizations into interactive, visual intelligence dashboards. Explore repository relationships, compare two or more organizations, contributor networks, activity trends, risk metrics, and organizational health—all without leaving your browser. @@ -294,7 +303,24 @@ Dashboard renders visual intelligence Open http://localhost:5173 in your browser. -3. **Risk Assessment** +3. **Docker Deployment (Optional)** + + If you want to host OrgExplorer locally or in your own containerized infrastructure: + + * **Using Docker Compose (Recommended)**: + ```bash + docker compose up --build -d + ``` + Open http://localhost:8080 in your browser. + + * **Using Docker CLI**: + ```bash + docker build -t orgexplorer . + docker run -d -p 8080:80 orgexplorer + ``` + Open http://localhost:8080 in your browser. + +4. **Risk Assessment** - Open bus factor panel - Detect low contributor redundancy - Review critical repositories diff --git a/badges/lighthouse-accessibility.svg b/badges/lighthouse-accessibility.svg new file mode 100644 index 0000000..1d5d14e --- /dev/null +++ b/badges/lighthouse-accessibility.svg @@ -0,0 +1 @@ +Lighthouse Accessibility: 90%Lighthouse Accessibility90% \ No newline at end of file diff --git a/badges/lighthouse-best-practices.svg b/badges/lighthouse-best-practices.svg new file mode 100644 index 0000000..7082396 --- /dev/null +++ b/badges/lighthouse-best-practices.svg @@ -0,0 +1 @@ +Lighthouse Best Practices: 88%Lighthouse Best Practices88% \ No newline at end of file diff --git a/badges/lighthouse-performance.svg b/badges/lighthouse-performance.svg new file mode 100644 index 0000000..987200a --- /dev/null +++ b/badges/lighthouse-performance.svg @@ -0,0 +1 @@ +Lighthouse Performance: 95%Lighthouse Performance95% \ No newline at end of file diff --git a/badges/lighthouse-pwa.svg b/badges/lighthouse-pwa.svg new file mode 100644 index 0000000..4cdb537 --- /dev/null +++ b/badges/lighthouse-pwa.svg @@ -0,0 +1 @@ +Lighthouse PWA: 50%Lighthouse PWA50% \ No newline at end of file diff --git a/badges/lighthouse-seo.svg b/badges/lighthouse-seo.svg new file mode 100644 index 0000000..843c72b --- /dev/null +++ b/badges/lighthouse-seo.svg @@ -0,0 +1 @@ +Lighthouse SEO: 100%Lighthouse SEO100% \ No newline at end of file diff --git a/badges/lighthouse.svg b/badges/lighthouse.svg new file mode 100644 index 0000000..1f4a227 --- /dev/null +++ b/badges/lighthouse.svg @@ -0,0 +1 @@ +Lighthouse: 85%Lighthouse85% \ No newline at end of file diff --git a/lighthouserc.json b/lighthouserc.json new file mode 100644 index 0000000..0d5a179 --- /dev/null +++ b/lighthouserc.json @@ -0,0 +1,19 @@ +{ + "ci": { + "collect": { + "staticDistDir": "./dist", + "numberOfRuns": 3 + }, + "upload": { + "target": "temporary-public-storage" + }, + "assert": { + "assertions": { + "categories:performance": ["warn", { "minScore": 0.75 }], + "categories:accessibility": ["error", { "minScore": 0.8 }], + "categories:best-practices": ["error", { "minScore": 0.85 }], + "categories:seo": ["error", { "minScore": 0.85 }] + } + } + } +}