From bd9760e124d9facb40a2cb86fdb18fdff2abae1f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 3 Jul 2019 10:18:02 -0700 Subject: add release documentation to CONTRIBUTING.md (#1171) ## Motivation Currently, the process for releasing a new version of a Tokio crate is somewhat complex, and is not well-documented. To make it easier for contributors to release minor versions more frequently, there should be documentation describing this process. ## Solution This branch adds a section to `CONTRIBUTING.md` describing how to release a new version of a Tokio crate. The steps are based on those described by @carllerche in an offline conversation. I've also added a quick shell script to actually publish new crate versions. This should make it harder to make mistakes when publishing. Signed-off-by: Eliza Weisman --- CONTRIBUTING.md | 60 +++++++++++++++++++++++++++++ bin/publish | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100755 bin/publish diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da207a2b..8bd28043 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -381,3 +381,63 @@ _Adapted from the [Node.js contributing guide][node]_. [node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md [hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment [documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html + +## Releasing + +Since the Tokio project consists of a number of crates, many of which depend on +each other, releasing new versions to crates.io can involve some complexities. +When releasing a new version of a crate, follow these steps: + +1. **Ensure that the release crate has no path dependencies.** When the HEAD + version of a Tokio crate requires unreleased changes in another Tokio crate, + the crates.io dependency on the second crate will be replaced with a path + dependency. Crates with path dependencies cannot be published, so before + publishing the dependent crate, any path dependencies must also be published. + This should be done through a form of depth-first tree traversal: + + 1. Starting with the first path dependency in the crate to be released, + inspect the `Cargo.toml` for the dependency. If the dependency has any + path dependencies of its own, repeat this step with the first such + dependency. + 2. Begin the release process for the path dependency. + 3. Once the path dependency has been published to crates.io, update the + dependent crate to depend on the crates.io version. + 4. When all path dependencies have been published, the dependent crate may + be published. + + To verify that a crate is ready to publish, run: + + ```bash + bin/publish --dry-run + ``` + +2. **Update Cargo metadata.** After releasing any path dependencies, update the + `version` field in `Cargo.toml` to the new version, and the `documentation` + field to the docs.rs URL of the new version. +3. **Update other documentation links.** Update the `#![doc(html_root_url)]` + attribute in the crate's `lib.rs` and the "Documentation" link in the crate's + `README.md` to point to the docs.rs URL of the new version. +4. **Update the changelog for the crate.** Each crate in the Tokio repository + has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that + crate since the last release should be added to the changelog. Change + descriptions may be taken from the Git history, but should be edited to + ensure a consistent format, based on [Keep A Changelog][keep-a-changelog]. + Other entries in that crate's changelog may also be used for reference. +5. **Perform a final audit for breaking changes.** Compare the HEAD version of + crate with the Git tag for the most recent release version. If there are any + breaking API changes, determine if those changes can be made without breaking + existing APIs. If so, resolve those issues. Otherwise, if it is necessary to + make a breaking release, update the version numbers to reflect this. +6. **Open a pull request with your changes.** Once that pull request has been + approved by a maintainer and the pull request has been merged, continue to + the next step. +7. **Release the crate.** Run the following command: + + ```bash + bin/publish + ``` + + Your editor and prompt you to edit a message for the tag. Copy the changelog + entry for that release version into your editor and close the window. + +[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md diff --git a/bin/publish b/bin/publish new file mode 100755 index 00000000..df0669f4 --- /dev/null +++ b/bin/publish @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +set -e +USAGE="Publish a new release of a tokio crate + +USAGE: + $(basename "$0") [OPTIONS] [CRATE] [VERSION] + +OPTIONS: + -v, --verbose Use verbose Cargo output + -d, --dry-run Perform a dry run (do not publish or tag the release) + -h, --help Show this help text and exit" + +DRY_RUN="" +VERBOSE="" + +err() { + echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2; +} + +status() { + WIDTH=12 + printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2" +} + +verify() { + status "Verifying" "if $CRATE v$VERSION can be released" + ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p') + + if [ "$ACTUAL" != "$VERSION" ]; then + err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL" + exit 1 + fi + + if git tag -l | grep -Fxq "$TAG" ; then + err "git tag \`$TAG\` already exists" + exit 1 + fi + + PATH_DEPS=$(grep -F "path = \"" Cargo.toml | sed -e 's/^/ /') + if [ -n "$PATH_DEPS" ]; then + err "crate \`$CRATE\` contained path dependencies:\n$PATH_DEPS" + echo "path dependencies must be removed prior to release" + exit 1 + fi +} + +release() { + status "Releasing" "$CRATE v$VERSION" + cargo package $VERBOSE + cargo publish $VERBOSE $DRY_RUN + + status "Tagging" "$TAG" + if [ -n "$DRY_RUN" ]; then + echo "# git tag $TAG && git push --tags" + else + git tag "$TAG" && git push --tags + fi +} + +while [[ $# -gt 0 ]] +do + +case "$1" in + -v|--verbose) + VERBOSE="--verbose" + set +x + shift + ;; + -d|--dry-run) + DRY_RUN="--dry-run" + shift + ;; + -*) + err "unknown flag \"$1\"" + echo "$USAGE" + exit 1 + ;; + *) # crate or version + if [ -z "$CRATE" ]; then + CRATE="$1" + elif [ -z "$VERSION" ]; then + VERSION="$1" + else + err "unknown positional argument \"$1\"" + echo "$USAGE" + exit 1 + fi + shift + ;; +esac +done +# set -- "${POSITIONAL[@]}" + +if [ -z "$VERSION" ]; then + err "no version specified!" + HELP=1 +fi + +if [ -n "$CRATE" ]; then + TAG="$CRATE-$VERSION" +else + err "no crate specified!" + HELP=1 +fi + +if [ -n "$HELP" ]; then + echo "$USAGE" + exit 1 +fi + +if [ -d "$CRATE" ]; then + (cd "$CRATE" && verify && release ) +else + err "no such crate \"$CRATE\"" + exit 1 +fi + -- cgit v1.2.3