An automated CI/CD pipeline that discovers repository metadata and idempotently syncs a unified project showcase to your GitHub Profile README.
Developers often build great projects but forget to update their central GitHub Profile README. This leads to "portfolio drift" — a state where your public showcase is constantly outdated compared to your actual repository activity. Manually updating a central README every time you ship a new feature or change a repository's tech stack is tedious and error-prone.
RepoDeck shifts the documentation mental model.
Instead of treating your Profile README as a manually edited document, RepoDeck treats the individual repository as the Single Source of Truth (SSOT). By placing a .github/project.md file inside each participating repository, the metadata lives directly alongside the code it describes. Your Profile README is no longer a source of truth; it becomes an automated, generated projection of your distributed repository data.
- Automated Discovery: Dynamically scans your GitHub account for repositories containing RepoDeck metadata.
- Strict Validation: Parses and validates all project frontmatter at runtime using Zod.
- Idempotent Updates: Computes local state against the remote GitHub file SHA, committing only if the rendered string has actually changed.
- Non-Destructive Injection: Uses HTML boundary markers to update only the project showcase section of your Profile README, leaving the rest of your content intact.
- Strongly Typed: Written entirely in TypeScript with rigorous type boundaries.
RepoDeck executes a strict, sequential pipeline:
[GitHub Repos] -> (Find .github/project.md) -> [Discovery Service]
|
[Zod Validation]
|
[Markdown Render]
|
[Profile README] <- (Idempotent Commit) <- [GitHub Content API]
RepoDeck is designed to be run as a personal CI/CD pipeline rather than a globally installed dependency.
- Fork or clone this repository to your GitHub account.
- Ensure you have Node.js 22.x installed locally.
- Run
npm cito install dependencies.
Core pipeline behaviors are managed via policy constants. These can be adjusted in the codebase or environment based on your needs:
PROJECT_METADATA_PATH: The relative path RepoDeck looks for (default:.github/project.md).INCLUDE_PRIVATE: Boolean flag to scan private repositories (requires appropriate token scopes).INCLUDE_ARCHIVED: Boolean flag to include archived, read-only repositories.COMMIT_MESSAGE: The automated Git commit message (default:docs(repodeck): sync project showcase).
Create a .env file in the root of the project:
GITHUB_TOKEN=github_pat_11A...
Note: The .env file is heavily .gitignored to prevent accidental credential leaks.
When running in CI/CD, the workflow requires a Repository Secret named REPODECK_PAT.
Required Token Permissions: You must generate a Fine-grained Personal Access Token scoped to "All repositories" (or selected repositories) with the following permission:
Contents: Read and write(Required to fetch.github/project.mdand push updates to your profile README). Metadata: Read-only is granted automatically.
To include a repository in your automated showcase, create a .github/project.md file in that repository with the following YAML frontmatter:
---
title: RepoDeck
description: Automated CI/CD pipeline for syncing project showcases.
category: Automation
status: Active
featured: true
tech:
- TypeScript
- Node.js
- GitHub Actions
repo: [https://github.com/kaushik0010/repodeck](https://github.com/kaushik0010/repodeck)
live: [https://github.com/kaushik0010](https://github.com/kaushik0010)
---
Optional Markdown body content goes here. It is not currently rendered in the profile but serves as valuable context for AI agents and future tooling.
title(String, Required): The display name of the project.description(String, Optional): A concise summary of what the project does.category(String, Optional): The domain or type of project (e.g., Automation, Web, CLI).status(String, Optional): Current project state (e.g., Active, Archived, WIP).featured(Boolean, Optional): Flags the project for prominent display. Defaults tofalse.tech(Array of Strings, Optional): Technologies used.repo(URL, Optional): Link to the source code.live(URL, Optional): Link to the deployed application.
To tell RepoDeck where to inject the generated showcase, add the following HTML comments to your <username>/<username> Profile README:
<!-- REPODECK:START -->
<!-- REPODECK:END -->Why markers? Instead of overwriting your entire README, RepoDeck uses standard Regex matching to find these boundaries. This allows you to maintain custom introductions, stats cards, and social links above or below your project list without interference.
RepoDeck is structured by domain responsibility to ensure high cohesion and low coupling:
github/: Isolates all external network boundaries. Contains the Octokit client initialization and raw API calls for fetching content and discovering repositories.markdown/: The pure text-processing engine. Handles parsing YAML, validating against the Zod schema, rendering the final Markdown string, and executing the boundary injection.services/: The core business logic. Orchestrates the pipeline by calling network modules, passing data to text processors, and managing application state.config.ts: The centralized global configuration policy.index.ts: The application entry point that wires the services together into a single execution command.
- Single Source of Truth: Metadata belongs with the code it describes, not in a disconnected markdown file.
- Idempotency: The pipeline can run 100 times a minute safely. It verifies remote SHAs and halts execution if the generated output matches the live profile.
- Fail Fast: Zod schemas instantly reject invalid data types, preventing malformed Markdown from ever being committed to your profile.
- Separation of Concerns: Network fetching, data validation, and text rendering are strictly isolated modules.
- Pure Rendering: The markdown generator is a pure function—given the same array of project objects, it always returns the exact same string.
- Least Privilege: The application requests only the precise permissions required to read specific files and write to one destination.
- Deterministic Output:
npm ciand pinned Node versions ensure the runner behaves exactly the same on every execution.
- SHA-Based Optimistic Concurrency: RepoDeck does not blindly force-push code. It uses GitHub's file SHA system to guarantee it is only overwriting the file state it originally fetched, preventing race conditions.
- No Secrets Committed: Tokens are managed entirely via GitHub's encrypted Actions Secrets and Libsodium sealed boxes.
- Minimal Runner Permissions: The
.github/workflows/repodeck.ymlfile explicitly drops default token scopes (usingpermissions: contents: readfor the runner), protecting against supply-chain attacks during the build step.
To work on RepoDeck locally:
npm install— Installs project dependencies using the standard lockfile.npm run dev— Executes the pipeline locally usingtsx(strips types for rapid testing).npm run typecheck— Runs a strict TypeScript compilation check (tsc --noEmit).npm run build— Compiles the TypeScript source into standard JavaScript in thedist/directory.npm start— Executes the compiled production build fromdist/index.js.
Contributions are welcome. If you find a bug or have a feature request, please open an issue first to discuss it. Ensure that any code changes pass npm run typecheck and adhere to the established architectural boundaries before submitting a pull request.