A purely client-side static web application that renders
RefactorFirst reports by fetching
.refactorfirst/refactor-first.json data directly from repositories. No server-side
code, no database, no build step — HTML, CSS and ES6 JavaScript modules served as
static files.
- Search over a curated listing of repositories (
repositories.txt) - Reports rendered with Mustache.js from raw platform content, with
main→masterbranch fallback — the same report the RefactorFirst report viewer produces: class/package maps (vizdom WASM SVGs with pan/zoom, plus Sigma 2D and 3D force-graph popups), relationship-removal priority tables, Chart.js disharmony bubble charts and class cycle summaries - Repository submission via a pre-filled issue on the hosting platform (no login, apps or tokens on this site): your platform account is captured as the issue author and validated server-side by the platform's CI
- Reports and submissions work for repositories hosted on the same platform as the deployment (GitHub, GitLab or Bitbucket)
- Works with a plain static file server:
python3 -m http.server 8000
- Project Layout
- Deploying to GitHub Pages (organization or personal account)
- Deploying to GitHub Enterprise Server
- Deploying to Bitbucket
- Deploying to GitLab
- How repository submission works
- Making Changes (Developer Guide)
- Testing
index.html # Single-page app shell (top menu + #app container)
repositories.txt # Listed repositories, one "user/repo" per line
js/ # ES6 modules: router, fetcher, renderer, search,
# repo-submission, error-handler,
# rate-limiter, cache-manager, utils, main
ci/process-submissions.sh # Shared submission validator used by GitHub
# Actions, GitLab CI and Bitbucket Pipelines
css/ # main.css + components.css
templates/ # Static page templates (about, faq, errors, ...)
# + user CI templates: user-refactorfirst-workflow.yml (GitHub),
# user-refactorfirst-gitlab-ci.yml, user-refactorfirst-bitbucket-pipeline.yml
# + workflow-sample-{github,gitlab,bitbucket}.html shown on the
# Getting Started page based on the detected hosting environment
.gitlab-ci.yml # Deploys this site to GitLab Pages
bitbucket-pipelines.yml # Validates this site's files on Bitbucket
assets/ # Fallback Mustache template, logo, Sentry config
tests/ # unit/ (Bun), integration/ (Bun), e2e/ (Playwright)
.github/workflows/ # add-repository.yml, redeploy.yml, test.yml
- Personal account: create a repository named
<username>.github.io. - Organization: create a repository named
<orgname>.github.ioin the org, or any project repository if you want a project page (https://<org>.github.io/<repo>/).
Everything in this directory is the site — push it to the default branch:
git init
git add .
git commit -m "RefactorFirst Pages site"
git remote add origin https://github.com/<owner>/<repo>.git
git push -u origin mainGo to Settings → Pages:
- Source: GitHub Actions (required for the scheduled redeployment workflow in
.github/workflows/redeploy.yml). - Alternatively choose Deploy from a branch (main,
/ (root)) if you don't need scheduled redeploys — the site is fully static.
The included redeploy.yml workflow redeploys every 10 minutes, but only when
repositories.txt changed in the last 15 minutes. The add-repository.yml workflow
reacts to newly opened submission issues, validates the submitter and commits new
entries to repositories.txt.
"Add Your Repo" submissions are pre-filled issues created in the listing
repository. Point the site at your repository via the meta tag in index.html:
<meta name="submission-target" content="<owner>/<repo>">No GitHub Apps, OAuth apps, client IDs or secrets are needed — identity is captured by GitHub as the issue author. See How repository submission works.
Add a CNAME file containing your domain (e.g. reports.example.com), configure
your DNS (CNAME record pointing to <owner>.github.io), and enable Enforce HTTPS
in Settings → Pages.
The application is fully static, so it works on any GitHub Enterprise Server (GHES) instance with Pages enabled.
A site admin must enable GitHub Pages for the instance
(Management Console → Pages → Enable), then create the repository
(<owner>.<pages-host> or a project repo) and push this project as described above.
Raw content and API calls default to github.com / raw.githubusercontent.com
/ api.github.com. For a self-hosted instance, update the URL builders:
js/fetcher.js— thegithubentry ofPLATFORM_BUILDERSshould build URLs likehttps://github.example.com/raw/<user>/<repo>/<branch>/.refactorfirst/refactor-first.json.js/repo-submission.js—repositoryInfoUrl()andbuildSubmissionIssueUrl()github branches must target your instance (https://github.example.com/...).ci/process-submissions.sh— setGH_API(and raw URL handling) to your instance endpoints (GH_HOSTis respected bygh-style tooling).index.html— extend the CSPconnect-srcdirective with your instance host and setsubmission-targetto your listing repository.
(Tip: keep these behind a single config module such as enterprise-config.json
if you need to support multiple deployments from one codebase.)
add-repository.yml and redeploy.yml use the built-in GITHUB_TOKEN;
ci/process-submissions.sh needs only curl and jq (preinstalled on
Actions runners). If your instance lacks internet access, ensure raw/API
endpoints are reachable from the browser — reports and submission pre-checks
are client-side, so end users' browsers (not the server) must be able to
reach your GHES host.
A Bitbucket deployment lists Bitbucket-hosted repositories: report fetching and
submission use bitbucket.org/.../raw/... and the Bitbucket REST API.
- Personal account: create a repository named
<username>.bitbucket.io. - Workspace/team: static sites are per-workspace:
<workspace>.bitbucket.io.
git init
git add .
git commit -m "RefactorFirst Pages site"
git remote add origin [email protected]:<workspace>/<workspace>.bitbucket.io.git
git push -u origin mainThe site goes live at https://<workspace>.bitbucket.io. Note that Bitbucket static
sites serve all paths from one index.html-style tree — since this app routes
client-side from a single index.html, request every path as /index.html-relative
links, or accept that deep links (e.g. /user/repo) return 404 unless Bitbucket
serves index.html for unknown paths (it does not by default — consider using the
query-style links or hosting deep routes via a redirect service).
The site is detected as bitbucket from the <workspace>.bitbucket.io
hostname; set submission-target in index.html to
<workspace>/<workspace>.bitbucket.io, enable the issue tracker on that
repository and extend the CSP connect-src with https://api.bitbucket.org
and https://bitbucket.org.
Bitbucket has no issue-triggered pipelines, so submissions are processed by the
custom process-submissions pipeline in bitbucket-pipelines.yml:
- In the repository go to Pipelines → Schedules and schedule
custom: process-submissions(e.g. every 10 minutes). - Create a workspace OAuth consumer with
issues:writeandrepositories:writescopes and store its credentials as the secured repository variablesBITBUCKET_CLIENT_ID/BITBUCKET_CLIENT_SECRET(server-side CI secrets only — the site itself never sees them).
The pipeline polls open issues titled Add repository: owner/repo, checks the
author has write/admin permission on the repository, verifies the report
file exists, commits repositories.txt and closes the issue with the outcome.
The manual sort-repos pipeline from the shipped bitbucket-pipelines.yml
also normalizes the listing on demand.
Users generating reports on Bitbucket: point them at
templates/user-refactorfirst-bitbucket-pipeline.yml— a copy-paste pipeline that runsmvn refactorfirst:jsonReportand commits.refactorfirst/refactor-first.jsonon every push tomain/master.
- Personal account: create a project named
<username>.gitlab.io. - Group: create a project named
<groupname>.gitlab.io, or any project for a project page athttps://<group>.gitlab.io/<project>/.
This repository already ships a ready-to-use .gitlab-ci.yml (validates the site and
deploys public/ via a pages job, including a 404.html copy of index.html for
client-side routing). It looks like this:
pages:
stage: deploy
script:
- mkdir -p public
# Publish everything except VCS metadata, tests and tooling
- |
for f in index.html repositories.txt css js templates assets; do
cp -r "$f" public/
done
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHgit init
git add .
git commit -m "RefactorFirst Pages site"
git remote add origin https://gitlab.com/<group>/<project>.git
git push -u origin mainGitLab Pages deploys from the pages job and serves
https://<group>.gitlab.io/<project>/.
- Client-side routing: GitLab Pages serves
404.htmlfor unknown paths; keep a copy ofindex.htmlaspublic/404.htmlin the pipeline (cp index.html public/404.html) so deep links like/user/repoload the app. - Submission processing: set
submission-targetinindex.htmlto your<group>/<project>, extend the CSPconnect-srcwith your GitLab base (https://gitlab.comor your self-managed host), and create a pipeline schedule (CI/CD → Schedules, e.g. every 10 minutes) — GitLab has no issue-triggered pipelines, so theprocess-submissionsjob in.gitlab-ci.ymlpolls open submission issues. For self-managed GitLab also add<meta name="platform-base-url" content="https://your-gitlab.example.com">. The job usesCI_JOB_TOKENby default; if your GitLab version/instance restricts its API scope, set a maskedGITLAB_TOKENCI variable with a project access token (apiscope) instead. - Listing redeploys: schedule another pipeline (or extend the same one) to
re-run
pageswhenrepositories.txtchanged. - Custom domains: set up under Settings → Pages with automatic Let's Encrypt
certificates. Note: the hostname-based environment detection only recognises
*.gitlab.io; on a custom domain passhostEnvironment: 'gitlab'tocreateApp()injs/main.js.
Users generating reports on GitLab: point them at
templates/user-refactorfirst-gitlab-ci.yml— a copy-paste pipeline that runsmvn refactorfirst:jsonReporton the default branch and commits.refactorfirst/refactor-first.jsonback using the built-inCI_JOB_TOKEN.
No OAuth app, client ID, token or secret is involved on the client side — forks need zero auth setup. The flow on every supported platform:
- The user fills in owner and repository on
/add-repo(no login on this site — identity is captured later, by the platform itself). - The app verifies client-side (unauthenticated) that the repository exists
and publishes
.refactorfirst/refactor-first.jsonon itsmain, default ormasterbranch, then opens a pre-filled issue (Add repository: owner/repo) in the listing project in a new tab. - The user — now on GitHub/GitLab/Bitbucket, logged in there — creates the issue. The platform-verified issue author is the captured submitter identity; it cannot be spoofed.
- The platform's CI (GitHub Actions
add-repository.yml, GitLab scheduledprocess-submissionspipeline, Bitbucket scheduledprocess-submissionspipeline; all drivingci/process-submissions.sh) validates:- the issue title matches the exact submission format,
- the issue author has write access to the submitted repository
(GitHub collaborator permission, GitLab Developer+ membership, Bitbucket
write/adminpermission), - the report file exists and the repository is not already listed.
- Valid submissions are committed to
repositories.txtand the issue receives a comment with the outcome and is closed; rejected submissions are commented with the reason and closed.
| GitHub | GitLab | Bitbucket | |
|---|---|---|---|
| Trigger | instant (issues: opened event) |
scheduled pipeline (10 min) | scheduled pipeline (10 min) |
| CI credentials | built-in GITHUB_TOKEN |
CI_JOB_TOKEN (or GITLAB_TOKEN project token) |
workspace OAuth consumer (secured variables) |
| Access check | collaborator permission |
member access_level >= 30 (Developer) |
permissions write/admin |
Limitations: only public repositories can be submitted (the report checks are unauthenticated), and each deployment serves exactly one platform — the one it is hosted on.
- Bun ≥ 1.0 (unit/integration tests) — or run it via
npx bun - Node.js ≥ 18 (Playwright E2E tests only)
- Python 3 (local static server)
bun install # install devDependencies (mustache, jsdom, playwright, eslint)python3 -m http.server 8000 # then open http://localhost:8000When making changes to the site locally, you may need to clear the cache to see your changes. Alternatively, you can disable caching in your browser.
This project follows strict TDD — write the failing test before production code:
- Write a failing test in
tests/unit/(pure module logic) ortests/integration/(DOM + routing flows). - Run
bun test tests/unit tests/integrationand watch it fail. - Write the minimal implementation in
js/to make it pass. - Refactor while keeping tests green.
bun test tests/unit tests/integration # unit + integration (jsdom)
bun test --watch tests/unit # watch mode
bun test --coverage tests/unit tests/integrationE2E tests run under Node.js/Playwright with a live local server:
npx playwright install # one-time: download browsers
npx playwright test # full E2E suite (chromium, firefox, webkit)
npx playwright test --ui # interactive modenpx eslint js/**/*.js tests/unit/**/*.js tests/integration/**/*.jsThe Getting Started page shows only the CI sample matching the hosting
environment, detected from the hostname (*.github.io → GitHub Actions,
*.gitlab.io → GitLab CI, *.bitbucket.io → Bitbucket Pipelines; anything else
defaults to GitHub). The samples live in
templates/workflow-sample-{github,gitlab,bitbucket}.html, and detection lives in
detectHostingEnvironment() in js/utils.js. To override detection (e.g. a custom
domain hosting the GitLab variant), pass hostEnvironment: 'gitlab' to
createApp() in js/main.js.
| Change | Files |
|---|---|
| URL routes | js/router.js (+ tests/unit/router-ext.test.js) |
| Raw fetching / branch fallback (platform-aware) | js/fetcher.js |
| Mustache rendering | js/renderer.js, assets/refactor-first-report.mustache (port of the RefactorFirst viewer template) |
| Interactive report widgets | js/report-view.js (+ CDN libs declared in index.html: Chart.js, sigma/graphology, graphlib-dot, svg-pan-zoom, 3d-force-graph, vizdom WASM) |
| Search / type-ahead | js/search.js |
| Submission flow | js/repo-submission.js, js/main.js (renderAddRepo) |
| Submission validation (CI) | ci/process-submissions.sh, .github/workflows/add-repository.yml, .gitlab-ci.yml, bitbucket-pipelines.yml |
| Error pages | js/error-handler.js, templates/error-*.html |
| Page content | templates/*.html |
| Styling | css/main.css, css/components.css |
| Listing data | repositories.txt (one user/repo per line) |
| Scheduled redeploy | .github/workflows/redeploy.yml |
.github/workflows/test.yml runs Bun unit/integration tests and the Playwright suite
on every push and pull request. Keep it green before merging.
- Unit (
tests/unit/): router, fetcher (incl. branch fallback, retry and per-platform URL construction), renderer, report-view (charts/graphs/popups), search, repo-submission (incl. report-file existence check and per-platform issue URLs), error-handler, rate-limiter, cache-manager, utils. - Integration (
tests/integration/): search flow, submission flow (missing report, unknown repo, per-platform issue redirect), report rendering. - E2E (
tests/e2e/): user journeys (incl. the submission → pre-filled issue hand-off), cross-browser smoke tests, mobile responsiveness (hamburger menu, single-column grid).
Coverage target: 80%+ on core modules. Current suite: 165 tests.