Skip to content

Contributing

jamesconwaydev edited this page Nov 6, 2026 · 2 revisions

Want to fix a bug, tighten a check or make the site nicer? Good. The rules for contributing live in CONTRIBUTING.md, and this page doesn't repeat them. What it adds is the map: which bot does what, and where your change fits.

Just want a repo listed? You don't need any of this. Use the Submit a repo form.

The short version of CONTRIBUTING.md

  • Fork, branch from main, make your change with tests, and open a pull request.

  • Run the checks before you push:

    python3 -m unittest discover -s scripts -t .
    npm --prefix site test
    npx prettier --check .
  • Use Conventional Commits for the PR title, such as fix(submit): reject repos with no README, in 72 characters or fewer. PRs are squash-merged, so the title becomes the commit.

  • Every PR needs an approving review and passing checks.

  • Planning something big? Start a thread in Ideas first.

  • Never edit data/entries.json by hand in a PR. The bot owns it.

Everything you contribute is released under CC0.

What's where

Path What it holds
data/entries.json Every entry: repo, category, description, status. The single source of truth.
data/schema.json The JSON Schema that entries.json must pass.
data/categories.json The 13 categories.
data/osi-licenses.json The OSI-approved license IDs the license check accepts.
scripts/ The bots, in Python 3.12 with the standard library only. Tests are in scripts/tests/.
scripts/devstream/ Shared modules: trust, quality, license_detect, description, issue_form, likes and the GitHub client.
scripts/blocklist.txt The spam wording the description filter rejects.
site/ The Astro site, with a little vanilla JavaScript for sorting and filtering.
fuzz/ Fuzz targets for the parsers.
scheduler/ The Cloudflare Worker that starts the timed workflows.

There's also a stats branch that only the bot writes to. It holds the Like counts (likes.json), the entry-to-Discussion map (discussions.json), daily snapshots, burst flags, health-check state and the trust cache. Keeping all that off main means the hourly Likes sync never touches the protected branch.

How the bots fit together

Every bot is a GitHub Actions workflow in .github/workflows/ that runs a script in scripts/. They write as a GitHub App, devstreamhq-bot.

Submit form ─> submit.yml ─> bot PR ─> pr-checks.yml + auto-approve.yml ─> merge
                                                  (submit.yml then opens the entry's Discussion)

scheduler/ (every 15 min) ─> sweep.yml, likes.yml, on-merge.yml, rot.yml, stars.yml
             likes.yml (hourly) ─> stats branch ─> deploy.yml ─> the site
             on-merge.yml (hourly) ─> any missing Discussions
             rot.yml (weekly)  ─> bot PR for warnings, archives, removals
Report form ─> reports.yml ─> label, hide PR, or a one-repo rot.yml run

GitHub's own scheduler skips runs when it's busy, so a small Cloudflare Worker in scheduler/ starts the timed workflows. It runs every 15 minutes and picks what to start from the time.

Workflow Runs What it does Script
submit.yml A submission issue is opened Checks the account and the repo against the Quality Bar. On a pass, opens a PR adding the entry and turns on auto-merge. On a fail, comments the reasons and closes the issue. scripts/submit.py
pr-checks.yml Every PR to main validate (entries against the schema), test (the unit tests), lint (Prettier) and site (the site's tests and build). scripts/validate_data.py
auto-approve.yml A bot PR is opened Approves the bot's own PRs, only when they change nothing but data/entries.json.
on-merge.yml Hourly at :30, as a backstop Opens a Discussion for any entry that doesn't have one yet and records it on the stats branch. Submit and the health check open threads themselves right after they merge. scripts/on_merge.py
likes.yml Hourly at :15 Counts trusted 👍 reactions, ranks entries, runs the burst and drip checks, writes the stats branch. scripts/sync_likes.py
deploy.yml After merges, Likes syncs and site changes Builds the site from main plus stats, and deploys it.
rot.yml Mondays at 06:45 UTC, or one repo on demand The health check. Opens a PR for any status change, then comments on the affected Discussions. scripts/rot.py
reports.yml A report issue is opened Labels the report, queues a health check, or opens a PR hiding the entry. See Reporting an Entry. scripts/reports.py
sweep.yml Every 15 minutes, and after each submission or report run Re-runs submissions and reports that no run finished, re-runs an overdue health check, restarts a Likes or Discussions run that hasn't started in 2 hours, closes the health check's abandoned PRs, and clears abandoned bot branches. scripts/sweep.py
stars.yml Daily Opens one issue when the repo reaches 100 stars. scripts/stars.py
codeql.yml, scorecard.yml, cflite_pr.yml PRs, pushes and a schedule Code scanning, the OpenSSF Scorecard and fuzzing.
release.yml A v* tag is pushed Tests and builds the site, then publishes a GitHub release with the source and site tarballs.

House rules for workflow code

If you touch .github/workflows/, the review looks for these:

  • Actions are pinned to a full commit SHA, with the version in a comment.
  • Permissions start at {} and each job asks only for what it needs.
  • Text from issues never goes into ${{ }} inside run:. Pass it through env: or a file. Issue text is written by strangers.
  • Deciding and writing are separate jobs. The job that reads an issue and decides runs with a read-only token. Only a later job gets the App token.
  • No pull_request_target checkout of PR code.

The bot's public comments use fixed strings and the normalised owner/name, never text copied from the issue. Keep it that way, so nobody can make the bot say something or ping someone.

Where to start

Issues labelled good first issue are small and well-scoped. The Roadmap shows what's planned for each milestone. Questions go to Discussions Q&A.

Found a security problem while you were in there? Don't open a PR or an issue. See Security.

Clone this wiki locally