Skip to content
Merged
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
20 changes: 14 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ All notable changes to RepoReady are documented here. The project follows [Seman

## [Unreleased]

### Changed

- Moved the live application to the nested portfolio route at `https://aminhanifm.github.io/projects/repo-ready/`.
- Kept the legacy repository Pages URL as a redirect.
- Replaced em dashes throughout project copy and generated Markdown reports.

## [1.0.0] - 2026-07-20

### Added
Expand All @@ -24,6 +18,20 @@ All notable changes to RepoReady are documented here. The project follows [Seman
- Unit, integration, and Playwright browser test suites with no real GitHub requests in CI.
- GitHub Actions workflows for continuous integration and Pages deployment.
- Open-source governance and community health files.
- Complete Open Graph, Twitter card, JSON-LD, favicon, and social-preview metadata.
- End-to-end usage guidance for analyzing and exporting a repository report.

### Fixed

- Replaced repository-size-only empty detection with positive README and tree evidence, preventing populated size-zero repositories from producing false empty reports.
- Invalidated previously cached reports created by the incorrect empty-repository logic.
- Updated the bundled CSV Health fixture to its canonical nested live URL.

### Changed

- Moved the live application to `https://aminhanifm.github.io/projects/repo-ready/`.
- Kept the legacy repository Pages URL as a redirect.
- Replaced em dashes throughout project copy and generated Markdown reports.

[Unreleased]: https://github.com/aminhanifm/RepoReady/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/aminhanifm/RepoReady/releases/tag/v1.0.0
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ npm run dev

Open the local address printed by Vite. No environment variables or GitHub credentials are required.

## Usage

1. Open the [live RepoReady application](https://aminhanifm.github.io/projects/repo-ready/).
2. Enter a public repository as `owner/repository` or paste its complete GitHub URL.
3. Select **Analyze** to collect the repository's public evidence.
4. Review the readiness score, scan coverage, category results, evidence, and recommendations.
5. Export the complete report as Markdown or JSON when you need a stable record.

## Architecture

RepoReady is a static React application with a deliberately one-way data flow:
Expand Down Expand Up @@ -84,7 +92,7 @@ Every rule, point value, evidence heuristic, limitation, and formula is specifie

## GitHub API and rate limits

RepoReady uses unauthenticated requests to the public GitHub REST API. A normal non-empty scan makes up to six requests: repository metadata, community profile, README, recursive tree, latest public release, and latest default-branch check runs.
RepoReady uses unauthenticated requests to the public GitHub REST API. A scan makes up to six requests: repository metadata, community profile, README, recursive tree, latest public release, and latest default-branch check runs. These evidence requests are attempted even when GitHub reports a repository size of zero because size alone does not prove that a repository is empty.

The interface displays GitHub's observed request limit, remaining allowance, and reset time. A `404` is reported as “repository not found or not publicly accessible.” Rate limits, malformed responses, network failures, server errors, missing endpoints, and truncated trees are surfaced explicitly. Affected checks remain unknown when the repository itself was successfully identified.

Expand Down
2 changes: 1 addition & 1 deletion SCORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Recommendations come only from failed checks, are ordered by points descending,

## Repository conditions and limitations

Archived, forked, and empty repositories are analyzed but clearly flagged. These conditions do not change points because they describe repository context rather than readiness evidence. Empty repositories typically have many observable failures; missing or blocked endpoints remain unknown.
Archived, forked, and empty repositories are analyzed but clearly flagged. These conditions do not change points because they describe repository context rather than readiness evidence. GitHub's repository size is not authoritative evidence of emptiness. RepoReady marks a repository empty only when size is zero, the README is observably absent, and the tree is observably empty or GitHub explicitly reports that no default-branch commit exists. Inconclusive requests remain unknown and never create an empty classification.

Unauthenticated GitHub REST API limits apply. RepoReady displays the latest observed `X-RateLimit-Remaining`, `X-RateLimit-Limit`, and `X-RateLimit-Reset` values. When the limit is exhausted, no speculative retry is performed. Completed reports are cached in `sessionStorage` for about ten minutes using normalized repository name plus ruleset version. Starting a different analysis aborts the previous request.

Expand Down
47 changes: 42 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,54 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#111a17" />
<link
rel="icon"
type="image/png"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta
name="description"
content="RepoReady creates deterministic, evidence-based readiness reports for public GitHub repositories."
/>
<link rel="canonical" href="https://aminhanifm.github.io/projects/repo-ready/" />
<meta property="og:title" content="RepoReady | Evidence-based repository readiness" />
<meta
property="og:description"
content="Analyze a public GitHub repository with deterministic checks, explainable evidence, and prioritized improvements."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://aminhanifm.github.io/projects/repo-ready/" />
<meta
property="og:image"
content="https://aminhanifm.github.io/projects/repo-ready/repo-ready-social.png"
/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="RepoReady evidence-based repository report preview" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="RepoReady | Evidence-based repository readiness" />
<meta
name="twitter:description"
content="Analyze a public GitHub repository with deterministic checks, explainable evidence, and prioritized improvements."
/>
<meta
name="twitter:image"
content="https://aminhanifm.github.io/projects/repo-ready/repo-ready-social.png"
/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": ["SoftwareApplication", "SoftwareSourceCode"],
"name": "RepoReady",
"description": "Deterministic, evidence-based readiness reports for public GitHub repositories.",
"url": "https://aminhanifm.github.io/projects/repo-ready/",
"codeRepository": "https://github.com/aminhanifm/RepoReady",
"license": "https://opensource.org/licenses/MIT",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Any modern web browser",
"author": {
"@type": "Person",
"name": "Amin Hanif",
"url": "https://github.com/aminhanifm"
}
}
</script>
<title>RepoReady | Know what your repository communicates</title>
</head>
<body>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "node scripts/run-e2e.mjs",
"screenshot": "node scripts/capture-screenshot.mjs"
"screenshot": "node scripts/capture-screenshot.mjs",
"social-image": "node scripts/capture-social-image.mjs"
},
"dependencies": {
"lucide-react": "^0.468.0",
Expand Down
19 changes: 19 additions & 0 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/repo-ready-social.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
134 changes: 134 additions & 0 deletions scripts/capture-social-image.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { mkdir } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";

const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)));
const outputPath = join(projectRoot, "public", "repo-ready-social.png");

const browser = await chromium.launch(
process.platform === "win32" && !process.env.CI ? { channel: "chrome" } : {},
);

try {
const page = await browser.newPage({ viewport: { width: 1200, height: 630 } });
await page.setContent(`<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { box-sizing: border-box; }
html, body { width: 1200px; height: 630px; margin: 0; overflow: hidden; }
body {
position: relative;
display: grid;
grid-template-columns: 1.08fr 0.92fr;
gap: 56px;
align-items: center;
padding: 72px 76px;
color: #17201d;
background:
radial-gradient(circle at 88% 10%, rgba(220, 235, 226, 0.92), transparent 35%),
#f6f4ee;
font-family: Aptos, Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
body::after {
content: "";
position: absolute;
width: 420px;
height: 420px;
left: -250px;
bottom: -300px;
border-radius: 50%;
border: 82px solid rgba(231, 97, 62, 0.14);
}
.brand { display: flex; align-items: center; gap: 17px; font-size: 30px; font-weight: 800; letter-spacing: -1px; }
.mark {
width: 66px;
height: 66px;
display: grid;
place-items: center;
color: white;
border-radius: 19px;
background: #0e7555;
box-shadow: inset 0 -5px 0 rgba(0, 0, 0, 0.14);
}
.kicker {
width: fit-content;
margin: 46px 0 20px;
padding: 9px 13px;
color: #09573f;
border: 1px solid #bdd4c7;
border-radius: 999px;
background: rgba(255, 255, 255, 0.55);
font: 700 14px ui-monospace, "SFMono-Regular", Consolas, monospace;
letter-spacing: 1.4px;
text-transform: uppercase;
}
h1 { margin: 0; font-size: 63px; line-height: 0.99; letter-spacing: -4px; }
h1 em { color: #0e7555; font-family: Georgia, "Times New Roman", serif; font-weight: 400; }
.lead { max-width: 575px; margin: 24px 0 0; color: #52605b; font-size: 21px; line-height: 1.5; }
.report {
position: relative;
min-height: 430px;
padding: 34px;
color: #f7faf8;
border: 1px solid #2f3b36;
border-radius: 28px;
background: #17201d;
box-shadow: 0 30px 70px rgba(21, 31, 27, 0.19);
transform: rotate(1.2deg);
}
.report::before { content: "REPOREADY / REPORT"; color: #9ba8a2; font: 700 12px ui-monospace, monospace; letter-spacing: 1.4px; }
.score-row { display: flex; align-items: center; gap: 22px; margin: 30px 0 34px; }
.score {
width: 124px;
height: 124px;
display: grid;
place-items: center;
border-radius: 50%;
background: conic-gradient(#55c897 0 302deg, #38423e 302deg);
}
.score div { width: 92px; height: 92px; display: grid; place-items: center; border-radius: 50%; background: #17201d; }
.score strong { font-size: 44px; letter-spacing: -2px; }
.summary small { color: #83d6b2; font: 700 12px ui-monospace, monospace; letter-spacing: 1.2px; }
.summary h2 { max-width: 220px; margin: 8px 0 7px; font-size: 23px; line-height: 1.13; }
.summary span { color: #aebbb5; font-size: 15px; }
.row { display: grid; grid-template-columns: 1fr 138px 34px; gap: 14px; align-items: center; margin: 18px 0; color: #c9d0cd; font-size: 14px; }
.track { height: 8px; overflow: hidden; border-radius: 99px; background: #38423e; }
.track i { display: block; height: 100%; border-radius: inherit; background: #55c897; }
.stamp { position: absolute; right: 30px; bottom: 25px; color: #93a39c; font: 700 11px ui-monospace, monospace; letter-spacing: 1.2px; }
</style>
</head>
<body>
<main>
<div class="brand">
<span class="mark">
<svg width="42" height="42" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.9-1.9"/>
</svg>
</span>
RepoReady
</div>
<div class="kicker">Evidence, not opinion</div>
<h1>Know what your repository <em>communicates.</em></h1>
<p class="lead">Deterministic readiness reports for public GitHub repositories.</p>
</main>
<section class="report" aria-label="Example readiness report">
<div class="score-row">
<div class="score"><div><strong>84</strong></div></div>
<div class="summary"><small>READINESS</small><h2>Ready with minor improvements</h2><span>92% scan coverage</span></div>
</div>
<div class="row"><span>Documentation</span><span class="track"><i style="width:88%"></i></span><b>88</b></div>
<div class="row"><span>Engineering</span><span class="track"><i style="width:76%"></i></span><b>76</b></div>
<div class="row"><span>Community</span><span class="track"><i style="width:82%"></i></span><b>82</b></div>
<div class="stamp">EXPLAINABLE / ACTIONABLE</div>
</section>
</body>
</html>`);
await mkdir(dirname(outputPath), { recursive: true });
await page.screenshot({ path: outputPath, animations: "disabled" });
console.log(outputPath);
} finally {
await browser.close();
}
Loading
Loading