|
1 | 1 | # Footprint |
2 | 2 |
|
3 | | -Footprint is a GitHub Action and tool that discovers a user’s public open-source contributions across GitHub (beyond just PRs) and generates a portfolio-ready “footprint” card + report. It's designed to help developers showcase their impact, especially across repositories they don't own. |
4 | | - |
5 | | -## Features |
6 | | - |
7 | | -- **Deep Discovery**: Finds contributions across GitHub, focusing on impact beyond just code ownership. |
8 | | - - **Contributions**: PRs opened, Code Reviews (PR comments/ reviews), Issues opened, and Issue comments. |
9 | | - - **Highlighting**: Top external contributions (ranked by impact) + owned repo highlights (ranked by stars). |
10 | | -- **Impact & Quality**: |
11 | | - - **Merged Bonus**: Merged Pull Requests receive a **1.5x bonus** to prioritize landing code. |
12 | | - - **Repo-Level Popularity Multiplier**: Scores are scaled using `1 + log10(1 + stars + 2*forks)` to reward impact in widely-adopted projects. This multiplier is applied **once per repository** and capped at 4.0, preventing star-heavy repos from dominating via sheer volume of low-impact events. |
13 | | - - **Diminishing Returns**: Comment/Issue scores decay per repository using a `1.0 / (1.0 + 0.5 * count)` formula. This ensures that while consistent engagement is valued, repetitive low-effort activity has reduced impact compared to diverse, high-value contributions. |
14 | | - - **Multi-Metric Separation**: Strictly separates PRs Opened, PR Reviews, PR Comments, Issues Opened, and Issue Comments to prevent conflation. |
15 | | - - **Minimal Card Expansion**: In minimal layouts, if one section (Owned or External) is empty, the other expands to show up to 6 items. To utilize space effectively, these items "shift to the right" into a **2x3 grid**, occupying the remaining horizontal space. |
16 | | -- **Card Variants**: |
17 | | - - **Standard**: All core statistics. |
18 | | -  |
19 | | - - **Minimal**: Non-zero statistics only. |
20 | | -  |
21 | | - - **Extended**: Rich dashboard including top projects and contribution highlights. |
22 | | -  |
23 | | - - **Extended Minimal**: Minified dashboard view. |
24 | | -  |
25 | | - |
26 | | -- **Artifact Generation**: |
27 | | - - `summary.md`: Human-readable impact summary. |
28 | | - - `report.json`: Machine-readable scoring data. |
29 | | - - `card.svg` (+ variants): Interactive SVG visualizations. |
30 | | - |
31 | | -## SVG Embedding |
32 | | - |
33 | | -To showcase your footprint on your GitHub profile, embed the generated cards using Markdown or HTML: |
| 3 | +[](https://github.com/marketplace/actions/footprint) [](https://github.com/arayofcode/footprint/actions/workflows/ci.yml) [](LICENSE) |
| 4 | + |
| 5 | +**Footprint is an OSS contributions impact scoring engine** that discovers your public GitHub contributions, scores them by real impact, and generates portfolio-ready artifacts — SVG cards, a structured JSON report, and a Markdown summary. |
| 6 | + |
| 7 | +It goes beyond raw commit or PR counts. Contributions are weighted by type, boosted if merged, scaled by repository popularity, and decayed for repetitive low-effort activity. The card is a projection of that model. |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## How Scoring Works |
| 14 | + |
| 15 | +Every contribution earns a base score: |
| 16 | + |
| 17 | +| Contribution | Base Score | |
| 18 | +| ------------------ | ---------- | |
| 19 | +| Pull Request | 10.0 | |
| 20 | +| Issue | 5.0 | |
| 21 | +| Code Review | 3.0 | |
| 22 | +| Issue Comment | 2.0 | |
| 23 | +| Discussion | 2.0 | |
| 24 | +| Discussion Comment | 2.0 | |
| 25 | +| Review Comment | 1.0 | |
| 26 | + |
| 27 | +Three modifiers are applied: |
| 28 | + |
| 29 | +- **Merged PR Bonus** — merged PRs receive a `1.5×` multiplier on their base score, applied before popularity. |
| 30 | +- **Repo Popularity Multiplier** — each repo's score is scaled by `1 + log10(1 + stars + 2×forks)`, capped at `4.0×`. Forks are weighted 2× as a higher-intent adoption signal. The log scale prevents star-heavy repos from overwhelming everything else. |
| 31 | +- **Diminishing Returns** — comment-type contributions (issue comments, review comments, PR comments, discussion comments) decay per repo using `1.0 / (1.0 + 0.5 × count)`. The first comment scores at 1.0×, the second at 0.66×, the third at 0.5×, and so on. Consistent engagement is valued; pure volume is not. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## Card Variants |
| 36 | + |
| 37 | +| Variant | Stats | Sections | Hides zeros | |
| 38 | +| ---------------- | ------------- | ---------------------------------- | ----------- | |
| 39 | +| Standard | All | — | No | |
| 40 | +| Minimal | Non-zero only | — | Yes | |
| 41 | +| Extended | All | Owned projects + top contributions | No | |
| 42 | +| Extended Minimal | Non-zero only | Owned projects + top contributions | Yes | |
| 43 | + |
| 44 | +| Standard | Minimal | |
| 45 | +| ------------------------------------------ | ---------------------------------------- | |
| 46 | +|  |  | |
| 47 | + |
| 48 | +| Extended | Extended Minimal | |
| 49 | +| ------------------------------------------ | ---------------------------------------------------------- | |
| 50 | +|  |  | |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## GitHub Action Usage |
| 55 | + |
| 56 | +Add this workflow to any repository — typically your `username/username` profile repo: |
| 57 | + |
| 58 | +```yaml |
| 59 | +# .github/workflows/footprint.yml |
| 60 | +name: Generate Footprint |
| 61 | + |
| 62 | +on: |
| 63 | + schedule: |
| 64 | + - cron: "0 0 * * 0" # Weekly on Sunday |
| 65 | + workflow_dispatch: |
| 66 | + |
| 67 | +permissions: |
| 68 | + contents: write # Required to push artifacts to output branch |
| 69 | + |
| 70 | +jobs: |
| 71 | + generate: |
| 72 | + runs-on: ubuntu-latest |
| 73 | + steps: |
| 74 | + - name: Checkout |
| 75 | + uses: actions/checkout@v4 |
| 76 | + |
| 77 | + - name: Generate Footprint |
| 78 | + uses: arayofcode/footprint@v1 |
| 79 | + env: |
| 80 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 81 | +``` |
| 82 | +
|
| 83 | +The default `GITHUB_TOKEN` is sufficient. No PAT required. |
| 84 | + |
| 85 | +### Inputs |
| 86 | + |
| 87 | +| Input | Default | Description | |
| 88 | +| --------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | |
| 89 | +| `gh_token` | `${{ github.token }}` | GitHub token for API access | |
| 90 | +| `username` | `GITHUB_ACTOR` | GitHub username to profile (defaults to the repo owner) | |
| 91 | +| `output_branch` | `footprint-output` | Branch where generated artifacts are committed | |
| 92 | +| `output_dir` | `dist` | Local output directory inside the container | |
| 93 | +| `min_stars` | `0` | Minimum star count for a project to appear in card sections. Does not affect aggregate stats — all owned projects are always counted | |
| 94 | +| `card` | `true` | Generate SVG card variants | |
| 95 | +| `timeout` | `300` | Timeout for GitHub API operations in seconds. Raise this for prolific contributors | |
| 96 | + |
| 97 | +### Outputs |
| 98 | + |
| 99 | +| Output | Description | |
| 100 | +| ---------------------- | -------------------------------------------------- | |
| 101 | +| `total_contributions` | Count of discovered external contributions | |
| 102 | +| `owned_projects_count` | Count of owned projects meeting the star threshold | |
| 103 | +| `total_score` | Aggregate weighted impact score | |
| 104 | + |
| 105 | +### Generated Artifacts |
| 106 | + |
| 107 | +Artifacts are committed to `output_branch` after each run: |
| 108 | + |
| 109 | +| File | Description | |
| 110 | +| --------------------------- | ---------------------------------------------------------------------- | |
| 111 | +| `card.svg` | Standard card — all stats | |
| 112 | +| `card-minimal.svg` | Minimal card — non-zero stats only | |
| 113 | +| `card-extended.svg` | Extended card — stats + repo sections | |
| 114 | +| `card-extended-minimal.svg` | Extended minimal — non-zero stats + sections | |
| 115 | +| `report.json` | Full structured scoring data (schema versioned) | |
| 116 | +| `summary.md` | Human-readable impact summary, also written to the Actions job summary | |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## Embedding in Your Profile README |
34 | 121 |
|
35 | 122 | ### Markdown |
| 123 | + |
36 | 124 | ```markdown |
37 | | - |
| 125 | + |
38 | 126 | ``` |
39 | 127 |
|
40 | | -### HTML (for links) |
| 128 | +### HTML (with link) |
| 129 | + |
41 | 130 | ```html |
42 | 131 | <a href="https://github.com/<username>/<repo>"> |
43 | | - <img src="https://raw.githubusercontent.com/<username>/<repo>/output/card.svg" alt="Footprint" width="800" /> |
| 132 | + <img |
| 133 | + src="https://raw.githubusercontent.com/<username>/<repo>/footprint-output/card-extended.svg" |
| 134 | + alt="Footprint" |
| 135 | + width="800" |
| 136 | + /> |
44 | 137 | </a> |
45 | 138 | ``` |
46 | 139 |
|
47 | 140 | > [!TIP] |
48 | | -> Use the `output` branch (or similar) to host these artifacts for clean embedding. |
| 141 | +> Replace `<username>/<repo>` with the repository where you added the workflow (e.g. `arayofcode/arayofcode`). The branch is `footprint-output` by default. |
49 | 142 |
|
50 | | -## Usage |
| 143 | +--- |
51 | 144 |
|
52 | | -Run locally: |
| 145 | +## Local Usage |
53 | 146 |
|
54 | 147 | ```bash |
55 | | -go run ./cmd/footprint |
| 148 | +export GITHUB_TOKEN=your_token |
| 149 | +go run ./cmd/footprint -username <github_username> |
56 | 150 | ``` |
57 | 151 |
|
58 | 152 | Optional flags: |
59 | 153 |
|
60 | | -```bash |
61 | | --username <github_username> |
62 | | --min-stars <n> |
63 | | --output <dir> |
64 | | -``` |
65 | | - |
66 | | -Environment variables: |
| 154 | +| Flag | Default | Description | |
| 155 | +| ------------ | -------------- | -------------------------------- | |
| 156 | +| `-username` | `GITHUB_ACTOR` | GitHub username | |
| 157 | +| `-min-stars` | `0` | Minimum stars for owned projects | |
| 158 | +| `-output` | `dist` | Output directory | |
| 159 | +| `-timeout` | `300s` | API timeout | |
| 160 | +| `-card` | `true` | Generate SVG cards | |
67 | 161 |
|
68 | | -```bash |
69 | | -- `GITHUB_TOKEN` (required) |
70 | | -- `GITHUB_ACTOR` (used when `-username` is not provided) |
| 162 | +--- |
71 | 163 |
|
72 | | -## GitHub Action Usage |
| 164 | +## Architecture |
73 | 165 |
|
74 | | -Add this to your `username/username` repository (or any repo) as a workflow: |
| 166 | +Footprint is a pipeline, not a badge service. The same scoring engine that populates `report.json` drives the card. Nothing is computed at render time. |
75 | 167 |
|
76 | | -```yaml |
77 | | -# .github/workflows/footprint.yml |
78 | | -name: Generate Footprint |
79 | | -on: |
80 | | - schedule: |
81 | | - - cron: "0 0 * * 0" # Weekly |
82 | | - workflow_dispatch: |
| 168 | +``` |
| 169 | +fetch → classify → score → aggregate → render → write |
| 170 | +``` |
83 | 171 |
|
84 | | -permissions: |
85 | | - contents: write # for pushing artifacts to output branch |
| 172 | +**Pipeline laws enforced in code:** |
86 | 173 |
|
87 | | -jobs: |
88 | | - generate: |
89 | | - runs-on: ubuntu-latest |
90 | | - steps: |
91 | | - - name: Checkout |
92 | | - uses: actions/checkout@v4 |
93 | | - - name: Generate Footprint |
94 | | - uses: arayofcode/footprint@v1 |
95 | | - env: |
96 | | - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
97 | | - with: |
98 | | - min_stars: 5 |
99 | | - card: true |
100 | | -``` |
| 174 | +- `logic/aggregator.go` owns all score multiplication. Renderers and fetchers never compute scores. |
| 175 | +- Renderers consume finalized output models only — no re-derivation. |
| 176 | +- `DecideLayout` is a pure function: same inputs always produce the same geometry. |
| 177 | +- Decay ordering is deterministic: events are sorted chronologically before decay is applied, with URL as a stable tiebreaker. |
101 | 178 |
|
102 | | -## Architecture Principles |
| 179 | +**Fetch sources** (all via GitHub GraphQL): |
103 | 180 |
|
104 | | -- **Semantic ViewModel**: The renderer receives data structures (`RowKind`, `Badges`) rather than raw URLs or domain inferences. ViewModels contain no geometry (X/Y) or layout-specific logic. |
105 | | -- **Centralized Layout**: All geometric math (X, Y, height, gaps) is pre-calculated in a pure `DecideLayout` function. `LayoutVM` exclusively owns all coordinates. |
106 | | -- **Zero Heuristics**: SVG generation logic is purely compositional and branching is based on explicit ViewModel flags. |
107 | | -- **Purely Deterministic**: Given the same ViewModel and assets, the SVG output is byte-perfect identical every time. |
| 181 | +- PRs authored (`author:<user> -user:<user> type:pr`) |
| 182 | +- PRs reviewed (`reviewer:<user> -user:<user> type:pr`) |
| 183 | +- Issues authored (`author:<user> -user:<user> type:issue`) |
| 184 | +- Issue comments (`user.issueComments`, paginated, private repos excluded) |
108 | 185 |
|
109 | | -## Roadmap & Areas for Improvement |
| 186 | +--- |
110 | 187 |
|
111 | | -- **New Data Sources**: Wiki edits, PR comments, Gist activity, and GitHub Discussions. |
112 | | -- **Detailed Analytics**: Refine scoring using metadata like `merged by`, `pushed to main`, or `tagged as help wanted`. |
113 | | -- **Reaction Indexing**: Incorporate emoji reactions for qualitative impact assessment. |
| 188 | +Contributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). |
0 commit comments