目录

GitHub Printer

Turn your sparse contribution graph into balanced, pixel‑perfect art — ethically, transparently, and within GitHub’s rules.

Many GitHub users aren’t professional developers. They still have an account to share small tools, notes, or experiments once in a while. The result? Their contribution graph looks empty most of the year.
GitHub Printer exists to help you curate that graph: generate a balanced pixel‑art banner and “print” it across a chosen year using lightweight marker commits.

Please read GitHub’s Terms of Service and Community Guidelines.
This tool is for legitimate self‑publishing and curation. Do not abuse it (spam, deception, misleading activity, or automated flooding across repos). You are responsible for your account and its compliance.

My own graph during a short period looked like this (for inspiration):


Table of Contents


How the contribution graph works

GitHub shades each day’s square by activity level (commit contributions, PRs, issues, etc.). Visually, you see four intensities (we’ll call them quartiles or levels L1..L4; the background “none” is separate).
If you want a balanced banner (e.g., a 7×52 header strip), it helps to keep the same number of pixels of each of the 4 levels.

This repo ships a helper that converts text -> balanced pixel image so that each color level appears equally often. That image becomes a target layout for your year.


What this project does

  • pixelart.py — builds a balanced 7×52 banner from text with:
    • two subtle shadow layers,
    • a uniform checkerboard background assembled from all deficit colors,
    • a final correction pass to make counts match exactly (same number of pixels for levels 1–4).
  • printer.py — fills your contribution graph for a selected year by creating commits in one target repository you own. It:
    • reads your daily contribution caps,
    • computes how many commits to add per day to match the image,
    • uses the Git Database API + a small marker file (.github/.contrib-marker.txt) to create the commits,
    • respects rate limits and handles empty repos safely.

⚠️ Be considerate: The tool operates in your repo, on the default branch, and only creates tiny marker commits. Do not run it across many repos or organizations. Keep the rate within limits.


Installation

Create your OAuth App (credentials)

printer.py uses OAuth (user‑to‑server). You’ll create a GitHub OAuth App in your account to obtain a Client ID and Client Secret (this is not a “GitHub App” installation; we specifically use OAuth endpoints).

  1. Go to GitHub -> Settings -> Developer settings -> OAuth Apps -> New OAuth App.
  2. Fill in:
    • Application name: github-printer (or anything)
    • Homepage URL: https://example.com (any URL you own; not used by the script)
    • Authorization callback URL: http://localhost:8765/callback (matches the script default; you may change it)
  3. After creation, note your Client ID and Client Secret.
  4. The script will request scopes: repo, read:user, user:email.

Alternative: you can use a classic PAT via GITHUB_PAT with repo scope. OAuth is recommended for clarity and easy revocation.

Environment variables

Create a small .env or export these in your shell (PowerShell examples included):

# Required for OAuth flow
export GITHUB_APP_CLIENT_ID="Iv1..."
export GITHUB_APP_CLIENT_SECRET="your-secret"
export OAUTH_REDIRECT_URL="http://localhost:8765/callback"

# Optional: use a classic PAT instead of OAuth
# export GITHUB_PAT="ghp_..."

# Optional: where to store tokens exchanged by OAuth
export GITHUB_TOKEN_STORE="./github_tokens.json"

# Optional: default repo name (owner is inferred from your account)
export TARGET_REPO="github-printer"

PowerShell:

# OAuth
[Environment]::SetEnvironmentVariable("GITHUB_APP_CLIENT_ID","Iv1...","User")
[Environment]::SetEnvironmentVariable("GITHUB_APP_CLIENT_SECRET","your-secret","User")
[Environment]::SetEnvironmentVariable("OAUTH_REDIRECT_URL","http://localhost:8765/callback","User")
# PAT (optional)
# [Environment]::SetEnvironmentVariable("GITHUB_PAT","ghp_...","User")

Install Python deps:

pip install pillow requests

Quick start

  1. Generate a balanced banner (7×52) from text:

    python pixelart.py --text "HELLO" --shadowdx 0 --out ./out_banner7
    # outputs: banner_7x52.png and a x50 preview
  2. Authorize (OAuth) — get the URL and finish the code exchange:

    python printer.py auth-url
    # open the printed URL, authorize, copy the "code" here:
    python printer.py exchange-code --code YOUR_CODE

(or set GITHUB_PAT instead of OAuth)

  1. Dry run a paint plan for a year (see how many commits would be made):

    python printer.py paint --year 2025 --image ./out_banner7/banner_7x52.png --dry-run
  2. Execute the paint (creates/uses a repo you own; default is TARGET_REPO or github-printer):

    python printer.py paint --year 2025 --image ./out_banner7/banner_7x52.png --repo my-contrib-art

The tool creates the repo if missing, initializes the default branch if empty, and commits to .github/.contrib-marker.txt on the default branch.


CLI reference

printer.py subcommands:

  • auth-url
    Print the OAuth authorization URL (repo read:user user:email).
  • exchange-code --code <CODE>
    Exchange the OAuth code for a user token and store it locally.
  • contribute [--repo owner/name|name] [--message MSG] [--count N] [--date YYYY-MM-DD]
    Make N marker commits for the given date (UTC). Creates the repo/branch if needed.
  • max-daily --from YYYY-MM-DD --to YYYY-MM-DD [--json] [--include-private]
    Show your per‑day commit‑contributions in the range (aggregated across repos).
  • paint --year YYYY --image path.png [--repo ...] [--message ...] [--dry-run] [--include-private]
    Compute the plan from the image and print it across the year.

pixelart.py arguments:

  • --text "YOUR TEXT" — banner text (emojis supported)
  • --shadowdx N — horizontal shadow offset (default 0)
  • --font PATH — optional custom TTF
  • --phase N — shifts the global checkerboard phase
  • --out DIR — output directory

Examples

Simple daily contributions (one day):

python printer.py contribute --count 5 --date 2025-01-15 --message "chore: marker"

Inspect max daily contributions in a month:

python printer.py max-daily --from 2025-01-01 --to 2025-01-31 --json

Paint with a custom repo and message:

python printer.py paint --year 2025 --image ./out_banner7/banner_7x52.png \
  --repo my-user/github-printer-art --message "chore: paint logo"

Generate text banner with emoji and slight phase shift:

python pixelart.py --text "🚀HELLO" --shadowdx 1 --phase 2 --out ./out_banner7

Design notes (how it paints)

Pixel art balancing (pixelart.py)

  • Canvas: 7×52 (fits the common 1‑year strip nicely).
  • Colors (levels):
    1=#033A16, 2=#196C2E, 3=#2EA043, 4=#56D364, NEUTRAL #151B23
  • Target per color: TGT = H*W/4 (i.e., exactly the same count for 1..4).
  • Pipeline:
    1. Text (adaptive height 6->5->4) with two shadows (level 1).
    2. Uniform checkerboard fill built from all deficit colors.
    3. Final correction pass (closest to center, excluding text & shadow) to hit the exact totals.

Printing plan (printer.py)

  • Reads your daily contribution totals and computes additions needed to match the pixel’s level for that day.
  • Uses the Git Database API to write a tiny marker line into .github/.contrib-marker.txt per commit.
  • Handles empty repos by creating the first commit via the Contents API.
  • Respects API rate limits (shows status; backs off when needed).

Troubleshooting

  • 403 Resource not accessible by integration (GET /user/emails)
    If your OAuth scopes or PAT don’t permit reading emails, the script will fall back to a noreply address ({id}+{login}@users.noreply.github.com).

  • 409 Git Repository is empty
    The script auto‑initializes the default branch by creating the marker file through the Contents API, then continues with Git DB commits.

  • Rate limits / Abuse detection
    The script detects both hard and secondary limits, waits for reset, and prints a budget estimate before painting.

  • Missing permissions to create a repo
    Ensure your token has repo scope and that you own the target user repo (or have org permissions).

  • Image shape
    Any RGBA image works, but 7×52 is recommended to mirror the year strip. The mapper walks the year in a repeatable pattern and assigns pixels consistently.


FAQ

Q: Is this allowed by GitHub?
A: GitHub doesn’t ban personal tooling that creates commits in your own repo. However, deceptive or spammy behavior is not OK. Use responsibly and follow the ToS/Guidelines.

Q: OAuth App vs GitHub App?
A: This script uses OAuth App (endpoints: /login/oauth/...). In GitHub settings, create an OAuth App, not an “App installation.” A classic PAT also works via GITHUB_PAT.

Q: What scopes are required?
A: repo, read:user, user:email.

Q: Will this affect my real projects?
A: Painting happens in a single target repo (created if needed) by editing a marker file. It won’t touch other repos.

Q: Can I limit how many commits it adds per day?
A: The plan respects your existing per‑day contributions and computes the delta to reach the pixel’s level, with internal caps to avoid unnatural spikes.

Q: Can I pause and resume?
A: Yes. You can run paint multiple times; it recalculates based on the current state of your contributions.


License

MIT (see LICENSE).

关于
3.7 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802032778号