{"id":27966120,"url":"https://github.com/bugnificent/unlighthouse-ci","last_synced_at":"2026-01-24T02:07:16.039Z","repository":{"id":290520657,"uuid":"974457713","full_name":"bugnificent/unlighthouse-ci","owner":"bugnificent","description":"Dynamic(Prod.)website accessibility and performance check using Unlighthouse for easily scan every page in one file.","archived":false,"fork":false,"pushed_at":"2025-04-29T17:57:51.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-09T09:35:57.544Z","etag":null,"topics":["accessibility","ci-cd","lighthouse-ci","unlighthouse"],"latest_commit_sha":null,"homepage":"https://netlify.accessibility.yusufasik.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bugnificent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-28T20:07:55.000Z","updated_at":"2025-04-29T17:57:55.000Z","dependencies_parsed_at":"2025-05-07T20:16:51.180Z","dependency_job_id":"e95c0ace-cb69-4da1-a60a-f502739cc89d","html_url":"https://github.com/bugnificent/unlighthouse-ci","commit_stats":null,"previous_names":["bugnificent/unlighthouse-ci"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bugnificent/unlighthouse-ci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugnificent%2Funlighthouse-ci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugnificent%2Funlighthouse-ci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugnificent%2Funlighthouse-ci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugnificent%2Funlighthouse-ci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bugnificent","download_url":"https://codeload.github.com/bugnificent/unlighthouse-ci/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugnificent%2Funlighthouse-ci/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28707436,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T23:51:44.727Z","status":"online","status_checked_at":"2026-01-24T02:00:06.909Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessibility","ci-cd","lighthouse-ci","unlighthouse"],"created_at":"2025-05-07T20:16:48.972Z","updated_at":"2026-01-24T02:07:16.023Z","avatar_url":"https://github.com/bugnificent.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ⛵ Unlighthouse CI | 🛡️ Dastardly - Automated Website Accessibility, Performance \u0026 Security Scanner\n\n![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bugnificent/unlighthouse-ci/unlighthouse.yml)\n[![Netlify Status](https://api.netlify.com/api/v1/badges/e8580d81-450f-431a-adf1-9eef8a8c904b/deploy-status)](https://app.netlify.com/sites/bugnificent/deploys)\n\nAutomated website scanning using Unlighthouse and Dastardly to check accessibility, performance, and security metrics, with static report generation and Netlify deployment. Jenkins support for Lighthouse accessibility audits is also included.\n## ✨ Features\n\n- 🔍 Comprehensive website scanning with Unlighthouse\n- 📊 Static HTML report generation\n- 🚀 Automatic deployment to Netlify\n- ⚡ Performance metrics (Lighthouse scores)\n- ♿ Accessibility auditing\n- 🔐 DAST scanning with Dastardly by PortSwigger\n- 🧪 Lighthouse auditing via Jenkins\n- 🔄 CI/CD integration via GitHub Actions\n\n## 🔧 Prerequisites\n\n- 🟢 Node.js (v14+)\n- 🌐 Netlify account\n- 📁 GitHub repository\n\n## ⚙️ Setup\n\n1. **Install dependencies**:\n   ```bash\n   npm install -g @unlighthouse/cli puppeteer netlify-cli\n   ```\n\n2. **Run Unlighthouse manually (optional for testing)**:\n   ```bash\n   unlighthouse-ci --site yusufasik.com --debug --build-static --throttle --no-cache --samples 3\n   ```\n\n   ### 🏷️ CLI Flag Explanations\n   - 🐞 `--debug`: Enables verbose logging for easier troubleshooting and visibility during development.\n   - 🏗️ `--build-static`: Generates a static HTML report in the `.unlighthouse/` directory.\n   - 🐢 `--throttle`: Simulates slower network/CPU to mimic real-world conditions.\n   - 🧹 `--no-cache`: Forces a fresh scan by skipping cache usage.\n   - 🔁 `--samples 3`: Runs 3 scans and averages the results for consistent metrics.\n\n3. **🔌 Connect Netlify**:\n   - 🆕 Create a new site on Netlify\n   - 🔐 Obtain your `NETLIFY_AUTH_TOKEN` and `NETLIFY_SITE_ID`\n   - 🔒 Store them in your GitHub repository under `Settings \u003e Secrets and variables \u003e Actions`\n   - ![LeeroyGIF](https://github.com/user-attachments/assets/6c4c6ddb-d1a7-4d43-a05c-be8d4e97119e)\n\n### And of course, Jenkins. (optional for advanced reporting)\n\n## 🛠️ GitHub Actions CI/CD\n\nThis project includes a preconfigured GitHub Actions workflow to run Unlighthouse and Dastardly together, and deploy the reports to Netlify automatically.\n\n## Unlighthouse Configuration (`unlighthouse.config.ts`) Explanation\n\n## 👀 Overview\nThis configuration file customizes how Unlighthouse (a Lighthouse scanner) interacts with your website during analysis.\n\n## Configuration Breakdown\n```typescript\n\nimport { defineConfig } from '@unlighthouse/core'\n\nexport default defineConfig({\n  site: 'https://yusufasik.com',\n  scanner: {\n    // Increase max routes if needed\n    maxRoutes: 50,\n  },\n  puppeteerPageSetup: async (page) =\u003e {\n    // Enable console logging from browser context\n    page.on('console', msg =\u003e console.log('[Browser]', msg.text()));\n\n    console.log('[Node] Starting page analysis...');\n    \n    // Set viewport to desktop size\n    await page.setViewport({ width: 1440, height: 900 });\n\n    // Start network idle monitoring\n    await page.evaluate(() =\u003e {\n      window.__unlighthouseScrollState = {\n        networkRequests: 0,\n        networkIdle: false\n      };\n      PerformanceObserver.setResourceTimingBufferSize(500);\n    });\n\n    // Initial screenshot for debugging (disable in production)\n    await page.screenshot({ path: 'unlighthouse-scroll-start.png' });\n\n    // Main scroll handler with enhanced waiting\n    await page.evaluate(async () =\u003e {\n      console.log('[Browser] Starting scroll simulation');\n      \n      const scrollConfig = {\n        distance: 100,       // pixels per scroll\n        delay: 200,          // base delay after scroll (ms)\n        maxWait: 3000,       // maximum wait time for network idle (ms)\n        threshold: 0.1       // height threshold to consider page fully scrolled\n      };\n\n      let totalHeight = 0;\n      const scrollHeight = document.body.scrollHeight;\n      let lastPosition = 0;\n      let unchangedCount = 0;\n\n      while (totalHeight \u003c scrollHeight * (1 - scrollConfig.threshold)) {\n        const beforeScroll = window.scrollY;\n        window.scrollBy(0, scrollConfig.distance);\n        totalHeight += scrollConfig.distance;\n        \n        // Adaptive waiting - longer if page is still loading\n        await new Promise(resolve =\u003e {\n          const checkStability = async () =\u003e {\n            const currentPosition = window.scrollY;\n            const isStable = (currentPosition === lastPosition);\n            \n            lastPosition = currentPosition;\n            \n            if (isStable) {\n              unchangedCount++;\n              if (unchangedCount \u003e 2) {\n                console.log(`[Browser] Detected stable position at ${currentPosition}px`);\n                return resolve();\n              }\n            } else {\n              unchangedCount = 0;\n            }\n\n            // Base delay + extra time if network is busy\n            const delay = scrollConfig.delay + \n              (window.__unlighthouseScrollState?.networkIdle ? 0 : 100);\n            \n            setTimeout(checkStability, delay);\n          };\n          \n          checkStability();\n        });\n      }\n      \n      console.log(`[Browser] Reached scroll end (${totalHeight}px)`);\n    });\n\n    // Final screenshot (disable in production)\n    await page.screenshot({ path: 'unlighthouse-scroll-end.png' });\n    \n    console.log('[Node] Page analysis completed');\n  },\n})\n```\n\n### 📂 `.github/workflows/unlighthouse.yml`\n```yaml\nname: Unlighthouse CI Report\n\non:\n  workflow_dispatch:\n\njobs:\n  UnlighthouseCI:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Install Dependencies\n        run: npm install -g @unlighthouse/cli puppeteer netlify-cli\n\n      - name: Unlighthouse assertions and client\n        run: unlighthouse-ci --site yusufasik.com --debug --build-static --throttle --no-cache --samples 3\n\n      - name: Deploy\n        uses: netlify/actions/cli@master\n        with:\n         args: deploy --dir=.unlighthouse --prod --message=\"New Release Deploy from GitHub Actions\"\n        env:\n          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}\n          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}\n```\n\n### 📂 `.github/workflows/dastardly.yml`\n```yaml\nname: Dastardly Report\n\non:\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  checks: write\n  id-token: write\n\njobs:\n  dastardly:\n    name: Dastardly Scan\n    runs-on: ubuntu-latest\n    env:\n      SITE_URL: https://yusufasik.com\n    steps:\n      - uses: actions/checkout@v4\n      - name: Dastardly Scan\n        continue-on-error: true\n        uses: PortSwigger/dastardly-github-action@main\n        with:\n          target-url: ${{ env.SITE_URL }}\n          output-filename: dastardly-report.xml\n          \n      - name: Upload security scan results\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: dastardly-reports\n          path: dastardly-report.xml\n```\n\n## 🤖 Jenkins Integration (Optional)\n\nIf you are using Jenkins, the following pipeline allows you to generate, publish, and archive Lighthouse accessibility reports:\n\n### 📂 `Jenkinsfile`\n```groovy\npipeline {\n     agent any\n\n     environment {\n       BUILD_NUM =\"${BUILD_NUMBER}\"\n       SITE = 'yusufasik.com'\n     }\n\n     stages {\n\n           stage('Generate Lighthouse Accessibility Report') {\n              steps {\n              // Generate Lighthouse Report\n               sh 'npx lighthouse ${env.SITE} --output=html --output-path=lighthouse-accessibility-report-${BUILD_NUMBER}.html --chrome-flags=\"--headless --no-sandbox --disable-gpu --disable-dev-shm-usage\"'\n              }\n         }\n\n           stage('Publish Lighthouse Accessibility Report') {\n                steps {\n                        publishHTML(target: [\n                          allowMissing: false,\n                          alwaysLinkToLastBuild: true,\n                          keepAll: true,\n                          reportDir: '.',\n                          reportFiles: 'lighthouse-accessibility-report-${BUILD_NUMBER}.html',\n                          reportName: 'Accessibility Report Build #${BUILD_NUMBER}'\n                        ])\n                }\n         }\n\n           stage('Archive Reports') {\n            steps {\n                // Lighthouse copy for zip\n                sh 'zip -r lighthouse-report-${BUILD_NUMBER}.zip lighthouse-accessibility-report-${BUILD_NUMBER}.html'\n\n                archiveArtifacts artifacts: 'lighthouse-report-${BUILD_NUMBER}.zip', allowEmptyArchive: false, onlyIfSuccessful: true\n            }\n        }\n    }\n       post {\n         always {\n\n             emailext (\n                 to: 'contact@yusufasik.com',\n                 subject: \"Jenkins Build ${currentBuild.currentResult}: Job ${env.JOB_NAME} #${env.BUILD_NUMBER}\",\n                 body: '${DEFAULT_CONTENT}',\n                 attachLog: true,\n                 attachmentsPattern: 'lighthouse-report-${BUILD_NUMBER}.zip'\n             )\n         }\n     }\n}\n```\n\n## 📤 Output\n\n- 🧾 Static site with performance and accessibility reports in `.unlighthouse/`\n- 🧪 DAST results stored as `dastardly-report.xml`\n- 🗂️ Lighthouse accessibility reports published via Jenkins HTML Publisher plugin and sended via email extension.\n- 🌍 Automatically deployed to: [https://netlify.accessibility.yusufasik.com](https://netlify.accessibility.yusufasik.com)\n\n## 📚 License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n---\n\n🤝 Feel free to contribute or open issues for enhancements!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugnificent%2Funlighthouse-ci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbugnificent%2Funlighthouse-ci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugnificent%2Funlighthouse-ci/lists"}