|
| 1 | +# blurb |
| 2 | + |
| 3 | +[](https://pypi.org/project/blurb) |
| 4 | +[](https://github.com/python/blurb/actions) |
| 5 | +[](https://codecov.io/gh/python/blurb) |
| 6 | +[](https://discuss.python.org/) |
| 7 | + |
| 8 | +## Overview |
| 9 | + |
| 10 | +**blurb** is a tool designed to rid CPython core development |
| 11 | +of the scourge of `Misc/NEWS` conflicts. |
| 12 | + |
| 13 | +The core concept: split `Misc/NEWS` into many |
| 14 | +separate files that, when concatenated back together |
| 15 | +in sorted order, reconstitute the original `Misc/NEWS` file. |
| 16 | +After that, `Misc/NEWS` could be deleted from the CPython |
| 17 | +repo and thereafter rendered on demand (e.g. when building |
| 18 | +a release). When committing a change to CPython, the commit |
| 19 | +process will write out a new file that sorts into the correct place, |
| 20 | +using a filename unlikely to have a merge conflict. |
| 21 | + |
| 22 | +**blurb** is a single command with a number of subcommands. |
| 23 | +It's designed to be run inside a valid CPython (Git) repo, |
| 24 | +and automatically uses the correct file paths. |
| 25 | + |
| 26 | +You can install **blurb** from PyPI using `pip`. Alternatively, |
| 27 | +simply add `blurb` to a directory on your path. |
| 28 | + |
| 29 | +**blurb**'s only dependency is Python 3.8+. |
| 30 | + |
| 31 | + |
| 32 | +## Files used by blurb |
| 33 | + |
| 34 | +**blurb** uses a new directory tree called `Misc/NEWS.d`. |
| 35 | +Everything it does is in there, except for possibly |
| 36 | +modifying `Misc/NEWS`. |
| 37 | + |
| 38 | +Under `Misc/NEWS.d` you'll find the following: |
| 39 | + |
| 40 | +* A single file for all news entries per previous revision, |
| 41 | + named for the exact version number, with the extension `.rst`. |
| 42 | + Example: `Misc/NEWS.d/3.6.0b2.rst`. |
| 43 | + |
| 44 | +* The `next` directory, which contains subdirectories representing |
| 45 | + the various `Misc/NEWS` categories. Inside these subdirectories |
| 46 | + are more `.rst` files with long, uninteresting, computer-generated |
| 47 | + names. Example: |
| 48 | + `Misc/NEWS.d/next/Library/2017-05-04-12-24-06.gh-issue-25458.Yl4gI2.rst` |
| 49 | + |
| 50 | + |
| 51 | +## blurb subcommands |
| 52 | + |
| 53 | +Like many modern utilities, **blurb** has only one executable |
| 54 | +(called `blurb`), but provides a diverse set of functionality |
| 55 | +through subcommands. The subcommand is the first argument specified |
| 56 | +on the command-line. |
| 57 | + |
| 58 | +If you're a CPython contributor, you probably don't need to use |
| 59 | +anything except `blurb add` — and you don't even need to specify |
| 60 | +the `add` part. |
| 61 | +(If no subcommand is specified, **blurb** assumes you meant `blurb add`.) |
| 62 | +The other commands are only expected to be useful for CPython release |
| 63 | +managers. |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | +### blurb help |
| 68 | + |
| 69 | +**blurb** is self-documenting through the `blurb help` subcommand. |
| 70 | +Run without any further arguments, it prints a list of all subcommands, |
| 71 | +with a one-line summary of the functionality of each. Run with a |
| 72 | +third argument, it prints help on that subcommand (e.g. `blurb help release`). |
| 73 | + |
| 74 | + |
| 75 | +### blurb add |
| 76 | + |
| 77 | +`blurb add` adds a new `Misc/NEWS` entry for you. |
| 78 | +It opens a text editor on a template; you edit the |
| 79 | +file, save, and exit. **blurb** then stores the file |
| 80 | +in the correct place, and stages it in Git for you. |
| 81 | + |
| 82 | +The template for the `blurb add` message looks like this: |
| 83 | + |
| 84 | + # |
| 85 | + # Please enter the relevant GitHub issue number here: |
| 86 | + # |
| 87 | + .. gh-issue: |
| 88 | + |
| 89 | + # |
| 90 | + # Uncomment one of these "section:" lines to specify which section |
| 91 | + # this entry should go in in Misc/NEWS. |
| 92 | + # |
| 93 | + #.. section: Security |
| 94 | + #.. section: Core and Builtins |
| 95 | + #.. section: Library |
| 96 | + #.. section: Documentation |
| 97 | + #.. section: Tests |
| 98 | + #.. section: Build |
| 99 | + #.. section: Windows |
| 100 | + #.. section: macOS |
| 101 | + #.. section: IDLE |
| 102 | + #.. section: Tools/Demos |
| 103 | + #.. section: C API |
| 104 | + |
| 105 | + # Write your Misc/NEWS entry below. It should be a simple ReST paragraph. |
| 106 | + # Don't start with "- Issue #<n>: " or "- gh-issue<n>: " or that sort of stuff. |
| 107 | + ########################################################################### |
| 108 | + |
| 109 | +Here's how you interact with the file: |
| 110 | + |
| 111 | +* Add the GitHub issue number for this commit to the |
| 112 | + end of the `.. gh-issue:` line. |
| 113 | + |
| 114 | +* Uncomment the line with the relevant `Misc/NEWS` section for this entry. |
| 115 | + For example, if this should go in the `Library` section, uncomment |
| 116 | + the line reading `#.. section: Library`. To uncomment, just delete |
| 117 | + the `#` at the front of the line. |
| 118 | + |
| 119 | +* Finally, go to the end of the file, and enter your `NEWS` entry. |
| 120 | + This should be a single paragraph of English text using |
| 121 | + simple reST markup. |
| 122 | + |
| 123 | +When `blurb add` gets a valid entry, it writes it to a file |
| 124 | +with the following format: |
| 125 | + |
| 126 | + Misc/NEWS.d/next/<section>/<datetime>.gh-issue-<issue_number>.<nonce>.rst |
| 127 | + |
| 128 | +For example, a file added by `blurb add` might look like this:: |
| 129 | + |
| 130 | + Misc/NEWS.d/next/Library/2017-05-04-12-24-06.gh-issue-25458.Yl4gI2.rst |
| 131 | + |
| 132 | +`<section>` is the section provided in the commit message. |
| 133 | + |
| 134 | +`<datetime>` is the current UTC time, formatted as |
| 135 | +`YYYY-MM-DD-hh-mm-ss`. |
| 136 | + |
| 137 | +`<nonce>` is a hopefully-unique string of characters meant to |
| 138 | +prevent filename collisions. **blurb** creates this by computing |
| 139 | +the MD5 hash of the text, converting it to base64 (using the |
| 140 | +"urlsafe" alphabet), and taking the first 6 characters of that. |
| 141 | + |
| 142 | + |
| 143 | +This filename ensures several things: |
| 144 | + |
| 145 | +* All entries in `Misc/NEWS` will be sorted by time. |
| 146 | + |
| 147 | +* It is unthinkably unlikely that there'll be a conflict |
| 148 | + between the filenames generated for two developers committing, |
| 149 | + even if they commit in at the exact same second. |
| 150 | + |
| 151 | + |
| 152 | +Finally, `blurb add` stages the file in git for you. |
| 153 | + |
| 154 | + |
| 155 | +### blurb merge |
| 156 | + |
| 157 | +`blurb merge` recombines all the files in the |
| 158 | +`Misc/NEWS.d` tree back into a single `NEWS` file. |
| 159 | + |
| 160 | +`blurb merge` accepts only a single command-line argument: |
| 161 | +the file to write to. By default, it writes to |
| 162 | +`Misc/NEWS` (relative to the root of your CPython checkout). |
| 163 | + |
| 164 | +Splitting and recombining the existing `Misc/NEWS` file |
| 165 | +doesn't recreate the previous `Misc/NEWS` exactly. This |
| 166 | +is because `Misc/NEWS` never used a consistent ordering |
| 167 | +for the "sections" inside each release, whereas `blurb merge` |
| 168 | +has a hard-coded preferred ordering for the sections. Also, |
| 169 | +**blurb** aggressively reflows paragraphs to < 78 columns, |
| 170 | +wheras the original hand-edited file occasionally had lines > |
| 171 | +80 columns. Finally, **blurb** strictly uses `gh-issue-<n>:` to |
| 172 | +specify issue numbers at the beginnings of entries, wheras |
| 173 | +the legacy approach to `Misc/NEWS` required using `Issue #<n>:`. |
| 174 | + |
| 175 | + |
| 176 | +### blurb release |
| 177 | + |
| 178 | +`blurb release` is used by the release manager as part of |
| 179 | +the CPython release process. It takes exactly one argument, |
| 180 | +the name of the version being released. |
| 181 | + |
| 182 | +Here's what it does under the hood: |
| 183 | + |
| 184 | +* Combines all recently-added NEWS entries from |
| 185 | + the `Misc/NEWS.d/next` directory into `Misc/NEWS.d/<version>.rst`. |
| 186 | +* Runs `blurb merge` to produce an updated `Misc/NEWS` file. |
| 187 | + |
| 188 | +One hidden feature: if the version specified is `.`, `blurb release` |
| 189 | +uses the name of the directory CPython is checked out to. |
| 190 | +(When making a release I generally name the directory after the |
| 191 | +version I'm releasing, and using this shortcut saves me some typing.) |
| 192 | + |
| 193 | + |
| 194 | +### blurb split |
| 195 | + |
| 196 | +`blurb split` only needs to be run once per-branch, ever. |
| 197 | +It reads in `Misc/NEWS` |
| 198 | +and splits it into individual `.rst` files. |
| 199 | +The text files are stored as follows:: |
| 200 | + |
| 201 | + Misc/NEWS.d/<version>.rst |
| 202 | + |
| 203 | +`<version>` is the version number of Python where the |
| 204 | +change was committed. Pre-release versions are denoted |
| 205 | +with an abbreviation: `a` for alphas, `b` for betas, |
| 206 | +and `rc` for release candidates. |
| 207 | + |
| 208 | +The individual `<version>.rst` files actually (usually) |
| 209 | +contain multiple entries. Each entry is delimited by a |
| 210 | +single line containing `..` by itself. |
| 211 | + |
| 212 | +The assumption is, at the point we convert over to *blurb*, |
| 213 | +we'll run `blurb split` on each active branch, |
| 214 | +remove `Misc/NEWS` from the repo entirely, |
| 215 | +never run `blurb split` ever again, |
| 216 | +and ride off into the sunset, confident that the world is now |
| 217 | +a better place. |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | +## The "next" directory |
| 222 | + |
| 223 | +You may have noticed that `blurb add` adds news entries to |
| 224 | +a directory called `next`, and `blurb release` combines those |
| 225 | +news entries into a single file named with the version. Why |
| 226 | +is that? |
| 227 | + |
| 228 | +First, it makes naming the next version a late-binding decision. |
| 229 | +If we are currently working on 3.6.5rc1, but there's a zero-day |
| 230 | +exploit and we need to release an emergency 3.6.5 final, we don't |
| 231 | +have to fix up a bunch of metadata. |
| 232 | + |
| 233 | +Second, it means that if you cherry-pick a commit forward or |
| 234 | +backwards, you automatically pick up the `NEWS` entry too. You |
| 235 | +don't need to touch anything up — the system will already do |
| 236 | +the right thing. If `NEWS` entries were already written to the |
| 237 | +final version directory, you'd have to move those around as |
| 238 | +part of the cherry-picking process. |
| 239 | + |
| 240 | +## Changelog |
| 241 | + |
| 242 | +### 1.1.0 |
| 243 | + |
| 244 | +- Support GitHub Issues in addition to b.p.o (bugs.python.org). |
| 245 | + If "gh-issue" is in the metadata, then the filename will contain |
| 246 | + "gh-issue-<number>" instead of "bpo-". |
| 247 | + |
| 248 | +### 1.0.7 |
| 249 | + |
| 250 | +- When word wrapping, don't break on long words or hyphens. |
| 251 | +- Use the `-f` flag when adding **blurb** files to a Git |
| 252 | + commit. This forces them to be added, even when the files |
| 253 | + might normally be ignored based on a `.gitignore` directive. |
| 254 | +- Explicitly support the `-help` command-line option. |
| 255 | +- Fix Travis CI integration. |
| 256 | + |
| 257 | +## Copyright |
| 258 | + |
| 259 | +**blurb** is Copyright 2015-2018 by Larry Hastings. |
| 260 | +Licensed to the PSF under a contributor agreement. |
0 commit comments