{
  "slug": "open-source-release",
  "url": "https://checklists.org/open-source-release",
  "markdown": "https://checklists.org/open-source-release.md",
  "title": "Releasing an Open Source Project",
  "description": "Everything to do before, during, and after publishing a project as open source.",
  "category": "software",
  "tags": [
    "software",
    "open-source"
  ],
  "version": "1.0",
  "updated": "2026-07-07",
  "sources": [
    {
      "name": "GitHub — Open Source Guides",
      "url": "https://opensource.guide/"
    },
    {
      "name": "Choose a License",
      "url": "https://choosealicense.com/"
    },
    {
      "name": "Keep a Changelog",
      "url": "https://keepachangelog.com/"
    }
  ],
  "intro": "For making a repository public for the first time — a new project or a formerly private one. Ordered so that nothing sensitive is exposed before the scan, and nothing is announced before it's usable.",
  "itemCount": 23,
  "essentialCount": 8,
  "sections": [
    {
      "title": "Before anything is public",
      "items": [
        {
          "id": "17vty3r",
          "text": "Scan the entire git history for secrets, not just the current tree",
          "note": "Use gitleaks or trufflehog. A key deleted in commit 50 is still sitting in commit 49, and bots scan newly public repos within minutes of creation.",
          "essential": true
        },
        {
          "id": "oalidq",
          "text": "Rotate every credential the scan finds",
          "note": "Treat found as compromised. Rewriting history is not enough — you cannot prove no one has a clone.",
          "essential": true
        },
        {
          "id": "ee317x",
          "text": "Purge private data from history",
          "note": "Internal hostnames, customer data, colleagues' emails, embarrassing commit messages. `git filter-repo` does this; note it rewrites every commit hash, so do it before anyone else has cloned.",
          "when": "previously-private-repo",
          "essential": true
        },
        {
          "id": "1ng4ls0",
          "text": "Choose a license and add a LICENSE file",
          "note": "No license means all rights reserved — nobody can legally use the code. MIT and Apache-2.0 for permissive (Apache adds an explicit patent grant), GPL for copyleft.",
          "essential": true
        },
        {
          "id": "nfle81",
          "text": "Verify your dependencies' licenses are compatible with yours",
          "note": "You cannot ship GPL code inside an MIT project. Check transitive dependencies too — license checkers exist for every ecosystem.",
          "essential": true
        },
        {
          "id": "1eaftsx",
          "text": "Confirm you have the right to publish",
          "note": "Many employment contracts claim IP created on company time or equipment. Get written approval or use your employer's open source release process — retroactive permission is much harder.",
          "when": "employed",
          "essential": true
        }
      ]
    },
    {
      "title": "Make it usable",
      "items": [
        {
          "id": "yswt5e",
          "text": "Write a README with what it does, how to install it, and a working example — all in the first screenful",
          "note": "A visitor decides in about thirty seconds. A copy-pasteable example above the fold beats three paragraphs of philosophy."
        },
        {
          "id": "z7peok",
          "text": "Verify the install instructions on a clean machine or container",
          "note": "Your dev machine has invisible dependencies the README doesn't mention. A fresh container is the only honest test."
        },
        {
          "id": "1xzqmqp",
          "text": "Add a CONTRIBUTING guide: how to run the tests, what PRs are welcome",
          "note": "The second half matters most — saying what you *won't* merge saves contributors wasted evenings and you awkward rejections."
        },
        {
          "id": "tvx4vs",
          "text": "Add a SECURITY.md with a private reporting channel",
          "note": "You want vulnerability reports in your inbox, not in a public issue with a proof of concept attached."
        },
        {
          "id": "gsuahx",
          "text": "Adopt a code of conduct",
          "note": "The Contributor Covenant is the de facto default; adopting it takes five minutes and deciding disputes without one takes much longer.",
          "when": "expecting-contributors"
        },
        {
          "id": "28uxzo",
          "text": "Set up CI running the tests on every pull request",
          "note": "Contributors can't run your private test setup. Public CI is what lets you review a stranger's PR without pulling it down first."
        },
        {
          "id": "18bvxvf",
          "text": "Enable branch protection on the main branch",
          "note": "Require the CI check to pass; block force-pushes. Cheap insurance the day a second maintainer arrives."
        }
      ]
    },
    {
      "title": "Version and publish",
      "items": [
        {
          "id": "uyfnc0",
          "text": "Adopt semantic versioning and start at 0.1.0",
          "note": "0.x tells users the API may still change; 1.0.0 is a compatibility promise — don't make it before you mean it."
        },
        {
          "id": "1fceju6",
          "text": "Start a CHANGELOG in Keep a Changelog format",
          "note": "Written for humans deciding whether to upgrade, not a dump of commit messages."
        },
        {
          "id": "q5ys7j",
          "text": "Tag the release in git with a version tag matching the package version",
          "note": "`v0.1.0` — tags are what let users and tooling map an installed version back to source."
        },
        {
          "id": "tf501p",
          "text": "Check the package name is available on the registry",
          "note": "Check npm/PyPI/crates.io before the name is in your README, docs, and announcement. Avoid names one typo away from popular packages.",
          "when": "publishing-package"
        },
        {
          "id": "1acopfp",
          "text": "Build the package and inspect exactly what's inside",
          "note": "`npm pack --dry-run` or `python -m build`, then list the contents. Packages routinely ship `.env` files, test fixtures, and junk — use an explicit include list, not excludes.",
          "when": "publishing-package",
          "essential": true
        },
        {
          "id": "1305tmx",
          "text": "Enable 2FA on the registry account before publishing",
          "note": "A hijacked publisher account is a supply-chain attack on everyone who installs your package.",
          "when": "publishing-package",
          "essential": true
        }
      ]
    },
    {
      "title": "Announce",
      "items": [
        {
          "id": "1madow1",
          "text": "Write the announcement around the problem it solves, not the implementation",
          "note": "\"I kept needing X, so I built...\" travels; \"a Rust library that uses...\" doesn't, except to people who already wanted it."
        },
        {
          "id": "zi6wfb",
          "text": "Post where your users already are, then stay for the first few hours",
          "note": "Show HN, the relevant subreddit, the ecosystem's forum or Discord. Answering early questions in the thread is half the launch."
        },
        {
          "id": "1u4jvij",
          "text": "Respond to the first issues and PRs within a day",
          "note": "The first week sets the community's expectations permanently. A fast \"thanks, looking at it\" costs nothing and keeps contributors."
        },
        {
          "id": "pytfjk",
          "text": "Add repository topics and a one-line description",
          "note": "It's how GitHub search and topic pages find you after the announcement traffic fades."
        }
      ]
    }
  ]
}