{"id":15008167,"url":"https://github.com/python/blurb","last_synced_at":"2025-04-09T21:18:21.092Z","repository":{"id":228822450,"uuid":"775007678","full_name":"python/blurb","owner":"python","description":"Command-line tool to manage CPython Misc/NEWS.d entries","archived":false,"fork":false,"pushed_at":"2025-01-15T12:48:00.000Z","size":187,"stargazers_count":13,"open_issues_count":9,"forks_count":7,"subscribers_count":47,"default_branch":"main","last_synced_at":"2025-04-09T21:18:16.884Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/python.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://www.python.org/psf/donations/python-dev/","github":["python"]}},"created_at":"2024-03-20T15:41:45.000Z","updated_at":"2025-01-21T12:32:37.000Z","dependencies_parsed_at":"2024-07-17T09:39:08.926Z","dependency_job_id":"0ba2446c-fa8e-4d82-b316-f24a5c24f9d2","html_url":"https://github.com/python/blurb","commit_stats":{"total_commits":86,"total_committers":23,"mean_commits":3.739130434782609,"dds":0.5581395348837209,"last_synced_commit":"a0829f7a6ab4b6ac2013552124cea740821cb72a"},"previous_names":["hugovk/blurb"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fblurb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fblurb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fblurb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fblurb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/python","download_url":"https://codeload.github.com/python/blurb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248111973,"owners_count":21049578,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:15:28.077Z","updated_at":"2025-04-09T21:18:21.064Z","avatar_url":"https://github.com/python.png","language":"Python","funding_links":["https://www.python.org/psf/donations/python-dev/","https://github.com/sponsors/python"],"categories":[],"sub_categories":[],"readme":"# blurb\n\n[![PyPI version](https://img.shields.io/pypi/v/blurb.svg?logo=pypi\u0026logoColor=FFE873)](https://pypi.org/project/blurb)\n[![GitHub Actions](https://github.com/python/blurb/actions/workflows/test.yml/badge.svg)](https://github.com/python/blurb/actions)\n[![Codecov](https://codecov.io/gh/python/blurb/branch/main/graph/badge.svg)](https://codecov.io/gh/python/blurb)\n[![Python discussions](https://img.shields.io/badge/Discourse-join_chat-brightgreen.svg)](https://discuss.python.org/)\n\n## Overview\n\n**blurb** is a tool designed to rid CPython core development\nof the scourge of `Misc/NEWS` conflicts.\n\nThe core concept: split `Misc/NEWS` into many\nseparate files that, when concatenated back together\nin sorted order, reconstitute the original `Misc/NEWS` file.\nAfter that, `Misc/NEWS` could be deleted from the CPython\nrepo and thereafter rendered on demand (e.g. when building\na release).  When committing a change to CPython, the commit\nprocess will write out a new file that sorts into the correct place,\nusing a filename unlikely to have a merge conflict.\n\n**blurb** is a single command with a number of subcommands.\nIt's designed  to be run inside a valid CPython (Git) repo,\nand automatically uses the correct file paths.\n\nYou can install **blurb** from PyPI using `pip`.  Alternatively,\nsimply add `blurb` to a directory on your path.\n\n\n## Files used by blurb\n\n**blurb** uses a new directory tree called `Misc/NEWS.d`.\nEverything it does is in there, except for possibly\nmodifying `Misc/NEWS`.\n\nUnder `Misc/NEWS.d` you'll find the following:\n\n* A single file for all news entries per previous revision,\n  named for the exact version number, with the extension `.rst`.\n  Example: `Misc/NEWS.d/3.6.0b2.rst`.\n\n* The `next` directory, which contains subdirectories representing\n  the various `Misc/NEWS` categories.  Inside these subdirectories\n  are more `.rst` files with long, uninteresting, computer-generated\n  names.  Example:\n  `Misc/NEWS.d/next/Library/2017-05-04-12-24-06.gh-issue-25458.Yl4gI2.rst`\n\n\n## blurb subcommands\n\nLike many modern utilities, **blurb** has only one executable\n(called `blurb`), but provides a diverse set of functionality\nthrough subcommands.  The subcommand is the first argument specified\non the command-line.\n\nIf you're a CPython contributor, you probably don't need to use\nanything except `blurb add` — and you don't even need to specify\nthe `add` part.\n(If no subcommand is specified, **blurb** assumes you meant `blurb add`.)\nThe other commands are only expected to be useful for CPython release\nmanagers.\n\n\n\n### blurb help\n\n**blurb** is self-documenting through the `blurb help` subcommand.\nRun without any further arguments, it prints a list of all subcommands,\nwith a one-line summary of the functionality of each.  Run with a\nthird argument, it prints help on that subcommand (e.g. `blurb help release`).\n\n\n### blurb add\n\n`blurb add` adds a new `Misc/NEWS` entry for you.\nIt opens a text editor on a template; you edit the\nfile, save, and exit.  **blurb** then stores the file\nin the correct place, and stages it in Git for you.\n\nThe template for the `blurb add` message looks like this:\n\n    #\n    # Please enter the relevant GitHub issue number here:\n    #\n    .. gh-issue:\n\n    #\n    # Uncomment one of these \"section:\" lines to specify which section\n    # this entry should go in in Misc/NEWS.\n    #\n    #.. section: Security\n    #.. section: Core and Builtins\n    #.. section: Library\n    #.. section: Documentation\n    #.. section: Tests\n    #.. section: Build\n    #.. section: Windows\n    #.. section: macOS\n    #.. section: IDLE\n    #.. section: Tools/Demos\n    #.. section: C API\n\n    # Write your Misc/NEWS entry below.  It should be a simple ReST paragraph.\n    # Don't start with \"- Issue #\u003cn\u003e: \" or \"- gh-issue\u003cn\u003e: \" or that sort of stuff.\n    ###########################################################################\n\nHere's how you interact with the file:\n\n* Add the GitHub issue number for this commit to the\n  end of the `.. gh-issue:` line.\n\n* Uncomment the line with the relevant `Misc/NEWS` section for this entry.\n  For example, if this should go in the `Library` section, uncomment\n  the line reading `#.. section: Library`.  To uncomment, just delete\n  the `#` at the front of the line.\n\n* Finally, go to the end of the file, and enter your `NEWS` entry.\n  This should be a single paragraph of English text using\n  simple reST markup.\n\nWhen `blurb add` gets a valid entry, it writes it to a file\nwith the following format:\n\n    Misc/NEWS.d/next/\u003csection\u003e/\u003cdatetime\u003e.gh-issue-\u003cissue_number\u003e.\u003cnonce\u003e.rst\n\nFor example, a file added by `blurb add` might look like this::\n\n    Misc/NEWS.d/next/Library/2017-05-04-12-24-06.gh-issue-25458.Yl4gI2.rst\n\n`\u003csection\u003e` is the section provided in the commit message.\n\n`\u003cdatetime\u003e` is the current UTC time, formatted as\n`YYYY-MM-DD-hh-mm-ss`.\n\n`\u003cnonce\u003e` is a hopefully-unique string of characters meant to\nprevent filename collisions.  **blurb** creates this by computing\nthe MD5 hash of the text, converting it to base64 (using the\n\"urlsafe\" alphabet), and taking the first 6 characters of that.\n\n\nThis filename ensures several things:\n\n* All entries in `Misc/NEWS` will be sorted by time.\n\n* It is unthinkably unlikely that there'll be a conflict\n  between the filenames generated for two developers committing,\n  even if they commit in at the exact same second.\n\n\nFinally, `blurb add` stages the file in git for you.\n\n\n### blurb merge\n\n`blurb merge` recombines all the files in the\n`Misc/NEWS.d` tree back into a single `NEWS` file.\n\n`blurb merge` accepts only a single command-line argument:\nthe file to write to.  By default, it writes to\n`Misc/NEWS` (relative to the root of your CPython checkout).\n\nSplitting and recombining the existing `Misc/NEWS` file\ndoesn't recreate the previous `Misc/NEWS` exactly.  This\nis because `Misc/NEWS` never used a consistent ordering\nfor the \"sections\" inside each release, whereas `blurb merge`\nhas a hard-coded preferred ordering for the sections.  Also,\n**blurb** aggressively reflows paragraphs to \u003c 78 columns,\nwheras the original hand-edited file occasionally had lines \u003e\n80 columns.  Finally, **blurb** strictly uses `gh-issue-\u003cn\u003e:` to\nspecify issue numbers at the beginnings of entries, wheras\nthe legacy approach to `Misc/NEWS` required using `Issue #\u003cn\u003e:`.\n\n\n### blurb release\n\n`blurb release` is used by the release manager as part of\nthe CPython release process.  It takes exactly one argument,\nthe name of the version being released.\n\nHere's what it does under the hood:\n\n* Combines all recently-added NEWS entries from\n  the `Misc/NEWS.d/next` directory into `Misc/NEWS.d/\u003cversion\u003e.rst`.\n* Runs `blurb merge` to produce an updated `Misc/NEWS` file.\n\nOne hidden feature: if the version specified is `.`, `blurb release`\nuses the name of the directory CPython is checked out to.\n(When making a release I generally name the directory after the\nversion I'm releasing, and using this shortcut saves me some typing.)\n\n\n\n## The \"next\" directory\n\nYou may have noticed that `blurb add` adds news entries to\na directory called `next`, and `blurb release` combines those\nnews entries into a single file named with the version.  Why\nis that?\n\nFirst, it makes naming the next version a late-binding decision.\nIf we are currently working on 3.6.5rc1, but there's a zero-day\nexploit and we need to release an emergency 3.6.5 final, we don't\nhave to fix up a bunch of metadata.\n\nSecond, it means that if you cherry-pick a commit forward or\nbackwards, you automatically pick up the `NEWS` entry too.  You\ndon't need to touch anything up — the system will already do\nthe right thing.  If `NEWS` entries were already written to the\nfinal version directory, you'd have to move those around as\npart of the cherry-picking process.\n\n## Copyright\n\n**blurb** is Copyright 2015-2018 by Larry Hastings.\nLicensed to the PSF under a contributor agreement.\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpython%2Fblurb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpython%2Fblurb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpython%2Fblurb/lists"}