From 250460f88ac6ff0e07d068d8fd0a92b3b45539ac Mon Sep 17 00:00:00 2001 From: Dmitry Misharov Date: Tue, 25 Aug 2026 16:42:09 +0200 Subject: [PATCH 01/20] release-tools: rewrite stage-release in Python Replaces stage-release.sh, the release-aux/*-fn.sh helpers and the twelve fixup-*.pl scripts. Verified against the shell across 230 state and transition combinations; the 14 that differ are shell bugs, now tested. Assisted-by: Claude:claude-opus-5 --- HOWTO-publish-a-release.md | 2 +- HOWTO-stage-a-release.md | 39 +- release-tools/.gitignore | 3 + release-tools/README.md | 106 ++ release-tools/do-copyright-year | 82 - release-tools/pyproject.toml | 19 + release-tools/release-aux/README.md | 43 - release-tools/release-aux/fix-title.pl | 6 - .../release-aux/fixup-CHANGES-postrelease.pl | 22 - .../release-aux/fixup-CHANGES-release.pl | 12 - .../fixup-CHANGES.md-postrelease.pl | 28 - .../release-aux/fixup-CHANGES.md-release.pl | 13 - .../release-aux/fixup-NEWS-postrelease.pl | 22 - .../release-aux/fixup-NEWS-release.pl | 12 - .../release-aux/fixup-NEWS.md-postrelease.pl | 28 - .../release-aux/fixup-NEWS.md-release.pl | 16 - .../release-aux/fixup-README-postrelease.pl | 10 - .../release-aux/fixup-README-release.pl | 12 - .../fixup-openssl.spec-postrelease.pl | 13 - .../release-aux/fixup-openssl.spec-release.pl | 13 - .../openssl-announce-pre-release.tmpl | 49 - .../openssl-announce-release-premium.tmpl | 39 - .../openssl-announce-release-public.tmpl | 38 - release-tools/release-aux/release-data-fn.sh | 29 - release-tools/release-aux/release-state-fn.sh | 214 --- .../release-aux/release-version-fn.sh | 353 ----- release-tools/release-aux/string-fn.sh | 51 - release-tools/release-aux/test_suite.sh | 263 ---- release-tools/release-aux/upload-fn.sh | 97 -- release-tools/stage-release | 30 + release-tools/stage-release.sh | 1393 ----------------- release-tools/stagerelease/__init__.py | 15 + release-tools/stagerelease/__main__.py | 15 + release-tools/stagerelease/build.py | 53 + release-tools/stagerelease/cli.py | 280 ++++ release-tools/stagerelease/copyright_year.py | 106 ++ release-tools/stagerelease/errors.py | 28 + release-tools/stagerelease/fixups.py | 268 ++++ release-tools/stagerelease/git.py | 176 +++ release-tools/stagerelease/metadata.py | 37 + release-tools/stagerelease/report.py | 44 + release-tools/stagerelease/run.py | 108 ++ release-tools/stagerelease/stage.py | 386 +++++ release-tools/stagerelease/state.py | 152 ++ release-tools/stagerelease/tarball.py | 88 ++ release-tools/stagerelease/textutil.py | 40 + .../stagerelease/version/__init__.py | 57 + release-tools/stagerelease/version/base.py | 177 +++ release-tools/stagerelease/version/legacy.py | 180 +++ release-tools/stagerelease/version/modern.py | 143 ++ release-tools/tests/conftest.py | 176 +++ release-tools/tests/test_cli.py | 165 ++ release-tools/tests/test_copyright_year.py | 141 ++ release-tools/tests/test_fixups.py | 293 ++++ release-tools/tests/test_git.py | 181 +++ release-tools/tests/test_metadata.py | 89 ++ release-tools/tests/test_stage.py | 378 +++++ release-tools/tests/test_state.py | 212 +++ release-tools/tests/test_tarball.py | 127 ++ release-tools/tests/test_version_legacy.py | 197 +++ release-tools/tests/test_version_modern.py | 131 ++ 61 files changed, 4623 insertions(+), 2877 deletions(-) create mode 100644 release-tools/.gitignore create mode 100644 release-tools/README.md delete mode 100755 release-tools/do-copyright-year create mode 100644 release-tools/pyproject.toml delete mode 100644 release-tools/release-aux/README.md delete mode 100644 release-tools/release-aux/fix-title.pl delete mode 100644 release-tools/release-aux/fixup-CHANGES-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-CHANGES-release.pl delete mode 100644 release-tools/release-aux/fixup-CHANGES.md-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-CHANGES.md-release.pl delete mode 100644 release-tools/release-aux/fixup-NEWS-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-NEWS-release.pl delete mode 100644 release-tools/release-aux/fixup-NEWS.md-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-NEWS.md-release.pl delete mode 100644 release-tools/release-aux/fixup-README-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-README-release.pl delete mode 100644 release-tools/release-aux/fixup-openssl.spec-postrelease.pl delete mode 100644 release-tools/release-aux/fixup-openssl.spec-release.pl delete mode 100644 release-tools/release-aux/openssl-announce-pre-release.tmpl delete mode 100644 release-tools/release-aux/openssl-announce-release-premium.tmpl delete mode 100644 release-tools/release-aux/openssl-announce-release-public.tmpl delete mode 100644 release-tools/release-aux/release-data-fn.sh delete mode 100644 release-tools/release-aux/release-state-fn.sh delete mode 100644 release-tools/release-aux/release-version-fn.sh delete mode 100644 release-tools/release-aux/string-fn.sh delete mode 100755 release-tools/release-aux/test_suite.sh delete mode 100644 release-tools/release-aux/upload-fn.sh create mode 100755 release-tools/stage-release delete mode 100755 release-tools/stage-release.sh create mode 100644 release-tools/stagerelease/__init__.py create mode 100644 release-tools/stagerelease/__main__.py create mode 100644 release-tools/stagerelease/build.py create mode 100644 release-tools/stagerelease/cli.py create mode 100644 release-tools/stagerelease/copyright_year.py create mode 100644 release-tools/stagerelease/errors.py create mode 100644 release-tools/stagerelease/fixups.py create mode 100644 release-tools/stagerelease/git.py create mode 100644 release-tools/stagerelease/metadata.py create mode 100644 release-tools/stagerelease/report.py create mode 100644 release-tools/stagerelease/run.py create mode 100644 release-tools/stagerelease/stage.py create mode 100644 release-tools/stagerelease/state.py create mode 100644 release-tools/stagerelease/tarball.py create mode 100644 release-tools/stagerelease/textutil.py create mode 100644 release-tools/stagerelease/version/__init__.py create mode 100644 release-tools/stagerelease/version/base.py create mode 100644 release-tools/stagerelease/version/legacy.py create mode 100644 release-tools/stagerelease/version/modern.py create mode 100644 release-tools/tests/conftest.py create mode 100644 release-tools/tests/test_cli.py create mode 100644 release-tools/tests/test_copyright_year.py create mode 100644 release-tools/tests/test_fixups.py create mode 100644 release-tools/tests/test_git.py create mode 100644 release-tools/tests/test_metadata.py create mode 100644 release-tools/tests/test_stage.py create mode 100644 release-tools/tests/test_state.py create mode 100644 release-tools/tests/test_tarball.py create mode 100644 release-tools/tests/test_version_legacy.py create mode 100644 release-tools/tests/test_version_modern.py diff --git a/HOWTO-publish-a-release.md b/HOWTO-publish-a-release.md index b9588572..dc164142 100644 --- a/HOWTO-publish-a-release.md +++ b/HOWTO-publish-a-release.md @@ -80,7 +80,7 @@ that user with sudo: ## Update the source repositories Finish up by pushing your local changes to the appropriate source repo as -instructed by `$TOOLS/release-tools/stage-release.sh`, which was performed +instructed by `$TOOLS/release-tools/stage-release`, which was performed when [staging the releases](HOWTO-stage-a-release.md). You may want to sanity check the pushes by inserting the `-n` (dry-run) option. diff --git a/HOWTO-stage-a-release.md b/HOWTO-stage-a-release.md index 26699cde..3943128c 100644 --- a/HOWTO-stage-a-release.md +++ b/HOWTO-stage-a-release.md @@ -26,7 +26,7 @@ Updates pending! - [PGP / GnuPG key](#pgp-gnupg-key) - [Prepare your repository checkouts](#prepare-your-repository-checkouts) - [Staging tasks](#staging-tasks) - - [Generate the announcement text](#generating-the-tarball-and-announcement-text) + - [Generate the tarball](#generate-the-tarball) - [Remember the results](#remember-the-results) # Prerequisites @@ -106,41 +106,44 @@ team that has it. # Staging tasks -## Generate the announcement text +## Generate the tarball *The changes in this section should be made in your clone of the openssl source repo* -To generate and stage announcement text, there is a script -`$TOOLS/release-tools/stage-release.sh`. It's expected to be run -while standing in the worktree of an OpenSSL source repository, and the -expects the checked out branch to be the branch to stage the release from, -matching one of OpenSSL release branch patterns. +To stage a release, there is a script `$TOOLS/release-tools/stage-release`. +It's expected to be run while standing in the worktree of an OpenSSL source +repository, and expects the checked out branch to be the branch to stage the +release from, matching one of OpenSSL release branch patterns. It needs +Python 3.10 or later, and nothing else. -The stage-release script has a multitude of other options that are useful -for specific cases, and is also self-documented: +The stage-release script has a number of other options that are useful for +specific cases, and is also self-documented: - To get a quick usage reminder: - $TOOLS/release-tools/stage-release.sh --help + $TOOLS/release-tools/stage-release --help -- To get a man-page: +- To get the manual: - $TOOLS/release-tools/stage-release.sh --manual + $TOOLS/release-tools/stage-release --manual It is generally called like this: - $TOOLS/release-tools/stage-release.sh --reviewer=NAME \ - --local-user=BA5473A2B0587B07FB27CF2D216094DFD0CB81EF + $TOOLS/release-tools/stage-release --reviewer=NAME -This scripts will perform a number of preparatory tasks, such as updating +This script will perform a number of preparatory tasks, such as updating the copyright year, running `make update`, update release dates, and move -the branch to the next development version. This results not only in a -staged announcement text, but also in a set of commits. +the branch to the next development version. This results in a set of +commits, an annotated release tag, and the tarball with its checksums. + +Nothing is signed, pushed or uploaded by this script. The release tag is +annotated but not signed: the signing key lives on an HSM that the build +host cannot reach, so signing the tag and the tarball happens separately. After having run the stage-release script, verify that its results are sensible. Check the commits that were added, using for example `git log`. -Review the announcment file. Check the data left in the metadata .dat file. +Check the data left in the metadata .dat file. *Do not push* the local commits to the source repo at this stage. diff --git a/release-tools/.gitignore b/release-tools/.gitignore new file mode 100644 index 00000000..75c61823 --- /dev/null +++ b/release-tools/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.pyc +.pytest_cache/ diff --git a/release-tools/README.md b/release-tools/README.md new file mode 100644 index 00000000..5ea65c41 --- /dev/null +++ b/release-tools/README.md @@ -0,0 +1,106 @@ +Release tools +============= + +`stage-release` stages an OpenSSL release: it makes the release commits, tags +the release, and writes the tarball, checksums and a metadata file. + +Nothing here signs, pushes or uploads anything. The release tag is annotated +but **not** signed, because the signing key lives on an HSM that the build +host cannot reach — signing the tag and the tarball is a separate step, run +where that access exists. Shipping the artifacts is the caller's job. + +Requirements +------------ + +Python 3.10 or later, and nothing else: the tool uses only the standard +library, so it runs on release build hosts without installing anything. +`pytest` is needed to run the tests, but not to run the tool. + +`--reviewer` shells out to `addrev` from `review-tools/`, which needs the +`OpenSSL::Query` Perl module. Without `--reviewer` there is no such +dependency. + +Usage +----- + +Run it from inside an OpenSSL **source** worktree, with the branch you are +releasing from checked out: + +```sh +$TOOLS/release-tools/stage-release --reviewer=NAME +$TOOLS/release-tools/stage-release --help +$TOOLS/release-tools/stage-release --manual +``` + +It refuses to run unless the branch is `master` or a recognised release +branch, and unless the worktree is clean. With no `--alpha`, `--beta` or +`--final`, the next release is worked out from the state of the branch. + +Layout +------ + +``` +stage-release the executable; adds this directory to sys.path +stagerelease/ + cli.py argument handling and the closing message + stage.py the staging run, start to finish + state.py the release state machine (pure function) + version/ the two versioning schemes + base.py ReleaseState, and the Scheme interface + modern.py VERSION.dat, OpenSSL 3.0 and later + legacy.py opensslv.h, before OpenSSL 3.0 + fixups.py the per-file CHANGES/NEWS/README/spec edits + copyright_year.py the copyright year pass + tarball.py tarball construction and checksums + metadata.py the .dat file describing a staged release + build.py ./Configure and make + git.py the git operations a staging run needs + run.py running external commands + report.py progress output + textutil.py line handling and file I/O + errors.py ReleaseError +tests/ pytest suite +``` + +`build.py`, `git.py` and `run.py` exist so that `stage.py` can be tested +without configuring or building OpenSSL. Inject a stub `Build` and the whole +staging run — branch decisions, commit sequence, artifact naming — is +exercised in milliseconds; see `tests/test_stage.py`. + +Both filename conventions are handled throughout: pre-3.0 OpenSSL uses +`CHANGES`/`NEWS`, while 3.0 and later use `CHANGES.md`/`NEWS.md`. They are +not interchangeable. + +Tests +----- + +```sh +cd release-tools +uvx pytest # or: pytest, with pytest installed +``` + +The suite needs `git`, `make` and `gzip` on PATH, builds throwaway +repositories under the pytest tmp directory, and touches neither the network +nor any OpenSSL checkout. + +Two things worth knowing when changing this code: + +- `state.py` is a pure function of (scheme, state, method, date). Every + release transition is testable without a repository; keep it that way. +- The fixups in `fixups.py` rewrite published changelogs, so a mistake there + is expensive. `tests/test_fixups.py` asserts on exact output text rather + than on substrings, deliberately. + +History +------- + +This replaces `stage-release.sh`, the `release-aux/*-fn.sh` helpers and the +twelve `release-aux/fixup-*.pl` scripts. The port was verified against the +shell it replaced across 230 combinations of version state and requested +transition. All 216 OpenSSL 3.0+ cases matched exactly. Fourteen pre-3.0 +cases differ, and in every one the shell wrote a corrupt +`OPENSSL_VERSION_NUMBER`: its patch-letter decoder was `^(z)*(.)$`, a capture +group under `*`, which keeps only the final repetition. That misread any +chain of more than one `z`, and failed outright on an empty patch level. +Neither was reachable in practice — 1.x is end-of-life and never went past +`zh` — and both are covered by tests now. diff --git a/release-tools/do-copyright-year b/release-tools/do-copyright-year deleted file mode 100755 index 4aa0407c..00000000 --- a/release-tools/do-copyright-year +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the OpenSSL license (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -this_year=`date +%Y` -some_year="[12][0-9][0-9][0-9]" -year_range="(${some_year})(-${some_year})?" - -copyright_owner="The OpenSSL Project" -copyright="Copyright .*${year_range} .*${copyright_owner}" - -# sed_script: -# for all lines that contain ${copyright} : { -# replace years yyyy-zzzz (or year yyyy) by yyyy-${this_year} -# replace repeated years yyyy-yyyy by yyyy -# } -ss=/tmp/sed$$ -cat <$ss -/${copyright}/ { -s|${year_range}|\1-${this_year}| -s|(${some_year})-\1|\1| -} -EOF - -collect_files() { - NYD=`date +%Y-01-01` - - git diff-tree -r --name-status `git rev-list -1 --before=$NYD HEAD`..HEAD | \ - grep -v '^ *D' - - # Always update the end year in README.md and/or README. - # It might be listed twice, but this is hardly a problem, - # just slightly suboptimal. - [[ -f README.md ]] && echo 'X README.md' - [[ -f README ]] && echo 'X README' -} - -process_files() { - count=0 - sp="/-\|" - sc=0 - spin() { - printf "\r${sp:sc++:1} %s" "$@" - ((sc==${#sp})) && sc=0 - } - endspin() { - printf "\r%s\n" "$@" - } - - while read STATUS FILE ; do - if [ -d "$FILE" ]; then continue; fi - (( count++ )) - spin $count - # To avoid touching the original files when they aren't modified: - # - # 1. Copy the file, to make sure all permissions and other - # copyable attributes are copied as well - # 2. Run sed on the copy - # 3. IF the copy has been modified, move it back to the original, - # add and commit. - TMPFILE="$(dirname "$FILE")"/"__$(basename "$FILE").new" - cp "$FILE" "$TMPFILE" - sed -E -f /tmp/sed$$ -i "$TMPFILE" - if cmp -s "$FILE" "$TMPFILE"; then - rm "$TMPFILE" - else - mv "$TMPFILE" "$FILE" - git add "$FILE" - fi - done - endspin "Files considered: $count" -} - -echo Updating copyright -collect_files | process_files -echo Files changed: $(git status --porcelain --untracked-files=no --ignore-submodules=all | grep '^ *M' | wc -l) -rm -f $ss diff --git a/release-tools/pyproject.toml b/release-tools/pyproject.toml new file mode 100644 index 00000000..697e12f5 --- /dev/null +++ b/release-tools/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "stagerelease" +version = "1.0.0" +description = "OpenSSL release staging" +requires-python = ">=3.10" +# No runtime dependencies, by design: this runs on release build hosts where +# installing packages is not always possible. pytest is a development-only +# dependency, see [dependency-groups] below. +dependencies = [] + +[dependency-groups] +dev = ["pytest>=7.0"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] +markers = [ + "slow: tests that shell out to git repeatedly", +] diff --git a/release-tools/release-aux/README.md b/release-tools/release-aux/README.md deleted file mode 100644 index d0cc6cc5..00000000 --- a/release-tools/release-aux/README.md +++ /dev/null @@ -1,43 +0,0 @@ -Auxillary files for dev/release.sh -=================================== - -- `release-state-fn.sh` - - This is the main version and state update logic... you could say - that it's the innermost engine for the release mechanism. It - tries to be agnostic of versioning schemes, and relies on - release-version-fn.sh to supply necessary functions that are - specific for versioning schemes. - -- `release-version-fn.sh` - - Supplies functions to manipulate version data appropriately for the - detected version scheme: - - `get_version()` gets the version data from appropriate files. - - `set_version()` writes the version data to appropriate files. - - `fixup_version()` updates the version data, given a first argument - that instructs it what update to do. - - `std_branch_name()` outputs the standard branch name for the OpenSSL - version in the worktree. - - `std_tag_name()` outputs the standard tag name for the the OpenSSL - version in the worktree. - -- `openssl-announce-pre-release.tmpl` and `openssl-announce-release.tmpl` - - Templates for announcements - -- `fixup-*-release.pl` and `fixup-*-postrelease.pl` - - Fixup scripts for specific files, to be done for the release - commit and for the post-release commit. - - Some of the scripts have very similar names, to handle different file layouts. - For example, `fixup-CHANGES.md-postrelease.pl` handles the file `CHANGES.md` - that is used in OpenSSL 3.0 and on, while `fixup-CHANGES-postrelease.pl` - handles the file `CHANGES` that is used in pre-3.0 OpenSSL versions. - Do not confuse these or other similarly named scripts. diff --git a/release-tools/release-aux/fix-title.pl b/release-tools/release-aux/fix-title.pl deleted file mode 100644 index 6fe256eb..00000000 --- a/release-tools/release-aux/fix-title.pl +++ /dev/null @@ -1,6 +0,0 @@ -#! /usr/bin/env perl - -BEGIN { my $prev } -($_ = $prev) =~ s|^( *)(.*)$|"$1" . '=' x length($2)|e - if m|==========|; -$prev = $_; diff --git a/release-tools/release-aux/fixup-CHANGES-postrelease.pl b/release-tools/release-aux/fixup-CHANGES-postrelease.pl deleted file mode 100644 index f7ec8d85..00000000 --- a/release-tools/release-aux/fixup-CHANGES-postrelease.pl +++ /dev/null @@ -1,22 +0,0 @@ -#! /usr/bin/env perl -pi - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $PREV_RELEASE_DATE = $ENV{PREV_RELEASE_DATE} || 'xx XXX xxxx'; - our $PREV_RELEASE_TEXT = $ENV{PREV_RELEASE_TEXT}; -} - -if (/^ Changes between (\S+) and (\S+) \[xx XXX xxxx\]/ - && $count-- > 0) { - my $v1 = $1; - my $v2 = $PREV_RELEASE_TEXT || $2; - - $_ = <<_____ - Changes between $v2 and $RELEASE_TEXT [xx XXX xxxx] - - *) - - Changes between $v1 and $v2 [$PREV_RELEASE_DATE] -_____ -} diff --git a/release-tools/release-aux/fixup-CHANGES-release.pl b/release-tools/release-aux/fixup-CHANGES-release.pl deleted file mode 100644 index 9bee525d..00000000 --- a/release-tools/release-aux/fixup-CHANGES-release.pl +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $RELEASE_DATE = $ENV{RELEASE_DATE}; -} - -if (/^ Changes between (\S+) and (\S+) \[xx XXX xxxx\]/ - && $count-- > 0) { - $_ = " Changes between $1 and $RELEASE_TEXT [$RELEASE_DATE]$'"; -} diff --git a/release-tools/release-aux/fixup-CHANGES.md-postrelease.pl b/release-tools/release-aux/fixup-CHANGES.md-postrelease.pl deleted file mode 100644 index bb971898..00000000 --- a/release-tools/release-aux/fixup-CHANGES.md-postrelease.pl +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env perl -pi - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $PREV_RELEASE_DATE = $ENV{PREV_RELEASE_DATE} || 'xx XXX xxxx'; - our $PREV_RELEASE_TEXT = $ENV{PREV_RELEASE_TEXT}; - - $RELEASE =~ s/-dev//; -} - -if (/^### Changes between (\S+) and (\S+) \[xx XXX xxxx\]/ - && $count-- > 0) { - my $v1 = $1; - my $v2 = $PREV_RELEASE_TEXT || $2; - - # If this is a pre-release, we do nothing - if ($RELEASE !~ /^\d+\.\d+\.\d+-(?:alpha|beta)/) { - $_ = <<_____ -### Changes between $v2 and $RELEASE_TEXT [xx XXX xxxx] - - * none yet - -### Changes between $v1 and $v2 [$PREV_RELEASE_DATE] -_____ - } -} diff --git a/release-tools/release-aux/fixup-CHANGES.md-release.pl b/release-tools/release-aux/fixup-CHANGES.md-release.pl deleted file mode 100644 index 7e5ba7e8..00000000 --- a/release-tools/release-aux/fixup-CHANGES.md-release.pl +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $RELEASE_DATE = $ENV{RELEASE_DATE}; -} - -if (/^### Changes between (\S+) and (\S+) \[xx XXX xxxx\]/ - && $count-- > 0) { - $_ = "### Changes between $1 and $RELEASE_TEXT [$RELEASE_DATE]$'"; -} diff --git a/release-tools/release-aux/fixup-NEWS-postrelease.pl b/release-tools/release-aux/fixup-NEWS-postrelease.pl deleted file mode 100644 index 1048c25d..00000000 --- a/release-tools/release-aux/fixup-NEWS-postrelease.pl +++ /dev/null @@ -1,22 +0,0 @@ -#! /usr/bin/env perl -pi - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $PREV_RELEASE_DATE = $ENV{PREV_RELEASE_DATE} || 'under development'; - our $PREV_RELEASE_TEXT = $ENV{PREV_RELEASE_TEXT}; -} - -if (/^ Major changes between OpenSSL (\S+) and OpenSSL (\S+) \[under development\]/ - && $count-- > 0) { - my $v1 = $1; - my $v2 = $PREV_RELEASE_TEXT || $2; - - $_ = <<_____ - Major changes between OpenSSL $v2 and OpenSSL $RELEASE_TEXT [under development] - - o - - Major changes between OpenSSL $v1 and OpenSSL $v2 [$PREV_RELEASE_DATE] -_____ -} diff --git a/release-tools/release-aux/fixup-NEWS-release.pl b/release-tools/release-aux/fixup-NEWS-release.pl deleted file mode 100644 index 4d191609..00000000 --- a/release-tools/release-aux/fixup-NEWS-release.pl +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $RELEASE_DATE = $ENV{RELEASE_DATE}; -} - -if (/^ Major changes between OpenSSL (\S+) and OpenSSL (\S+) \[under development\]/ - && $count-- > 0) { - $_ = " Major changes between OpenSSL $1 and OpenSSL $RELEASE_TEXT [$RELEASE_DATE]$'"; -} diff --git a/release-tools/release-aux/fixup-NEWS.md-postrelease.pl b/release-tools/release-aux/fixup-NEWS.md-postrelease.pl deleted file mode 100644 index 9231872f..00000000 --- a/release-tools/release-aux/fixup-NEWS.md-postrelease.pl +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env perl -pi - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $PREV_RELEASE_DATE = $ENV{PREV_RELEASE_DATE} || 'under development'; - our $PREV_RELEASE_TEXT = $ENV{PREV_RELEASE_TEXT}; - - $RELEASE =~ s/-dev//; -} - -if (/^### Major changes between OpenSSL (\S+) and OpenSSL (\S+) \[under development\]/ - && $count-- > 0) { - my $v1 = $1; - my $v2 = $PREV_RELEASE_TEXT || $2; - - # If this is a pre-release, we do nothing - if ($RELEASE !~ /^\d+\.\d+\.\d+-(?:alpha|beta)/) { - $_ = <<_____ -### Major changes between OpenSSL $v2 and OpenSSL $RELEASE_TEXT [under development] - - * none - -### Major changes between OpenSSL $v1 and OpenSSL $v2 [$PREV_RELEASE_DATE] -_____ - } -} diff --git a/release-tools/release-aux/fixup-NEWS.md-release.pl b/release-tools/release-aux/fixup-NEWS.md-release.pl deleted file mode 100644 index 212e10e8..00000000 --- a/release-tools/release-aux/fixup-NEWS.md-release.pl +++ /dev/null @@ -1,16 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $RELEASE_DATE = $ENV{RELEASE_DATE}; - - $RELEASE_DATE = 'in pre-release' - if ($RELEASE =~ /\d+\.\d+\.\d+-(?:alpha|beta)/) -} - -if (/^### Major changes between OpenSSL (\S+) and OpenSSL (\S+) \[under development\]/ - && $count-- > 0) { - $_ = "### Major changes between OpenSSL $1 and OpenSSL $RELEASE_TEXT [$RELEASE_DATE]$'"; -} diff --git a/release-tools/release-aux/fixup-README-postrelease.pl b/release-tools/release-aux/fixup-README-postrelease.pl deleted file mode 100644 index 9ed4d704..00000000 --- a/release-tools/release-aux/fixup-README-postrelease.pl +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; -} - -if (/^ OpenSSL.*$/ && $count-- > 0) { - $_ = " OpenSSL $RELEASE$'"; -} diff --git a/release-tools/release-aux/fixup-README-release.pl b/release-tools/release-aux/fixup-README-release.pl deleted file mode 100644 index 5a6f9e72..00000000 --- a/release-tools/release-aux/fixup-README-release.pl +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $RELEASE_TEXT = $ENV{RELEASE_TEXT}; - our $RELEASE_DATE = $ENV{RELEASE_DATE}; -} - -if (/^ OpenSSL.*$/ && $count-- > 0) { - $_ = " OpenSSL $RELEASE $RELEASE_DATE$'"; -} diff --git a/release-tools/release-aux/fixup-openssl.spec-postrelease.pl b/release-tools/release-aux/fixup-openssl.spec-postrelease.pl deleted file mode 100644 index 8c567b31..00000000 --- a/release-tools/release-aux/fixup-openssl.spec-postrelease.pl +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $ispre = $RELEASE =~ /-pre/; - - $RELEASE =~ s/-dev$//; -} - -if (!$ispre && /^Version:\s+(\S+)$/ && $count-- > 0) { - $_ = "Version: $RELEASE$'"; -} diff --git a/release-tools/release-aux/fixup-openssl.spec-release.pl b/release-tools/release-aux/fixup-openssl.spec-release.pl deleted file mode 100644 index 8c567b31..00000000 --- a/release-tools/release-aux/fixup-openssl.spec-release.pl +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env perl -p - -BEGIN { - our $count = 1; # Only the first one - our $RELEASE = $ENV{RELEASE}; - our $ispre = $RELEASE =~ /-pre/; - - $RELEASE =~ s/-dev$//; -} - -if (!$ispre && /^Version:\s+(\S+)$/ && $count-- > 0) { - $_ = "Version: $RELEASE$'"; -} diff --git a/release-tools/release-aux/openssl-announce-pre-release.tmpl b/release-tools/release-aux/openssl-announce-pre-release.tmpl deleted file mode 100644 index 5fc5f1af..00000000 --- a/release-tools/release-aux/openssl-announce-pre-release.tmpl +++ /dev/null @@ -1,49 +0,0 @@ - - OpenSSL version $release_text released - ====================================== - - OpenSSL - The Open Source toolkit for SSL/TLS - https://www.openssl.org/ - - OpenSSL $series is currently in $label. - - OpenSSL $release_text has now been made available. - - Note: This OpenSSL pre-release has been provided for testing ONLY. - It should NOT be used for security critical purposes. - - Specific notes on upgrading to OpenSSL $series from previous versions are - available in the OpenSSL Migration Guide, here: - - https://www.openssl.org/docs/manmaster/man7/migration_guide.html - - The $label release is available for download at these URLs: - - * https://www.openssl.org/source/ - * https://github.com/openssl/openssl/releases - - The distribution file name is: - - o $tarfile - Size: $length - SHA1 checksum: $sha1hash - SHA256 checksum: $sha256hash - - The checksums were calculated using the following commands: - - openssl sha1 -r $tarfile - openssl sha256 -r $tarfile - - Please download and check this $label release as soon as possible. - To report a bug, open an issue on GitHub: - - https://github.com/openssl/openssl/issues - - Please check the release notes and mailing lists to avoid duplicate - reports of known issues. (Of course, the source is also available - on GitHub.) - - Yours, - - The OpenSSL Project Team. - diff --git a/release-tools/release-aux/openssl-announce-release-premium.tmpl b/release-tools/release-aux/openssl-announce-release-premium.tmpl deleted file mode 100644 index 465beca2..00000000 --- a/release-tools/release-aux/openssl-announce-release-premium.tmpl +++ /dev/null @@ -1,39 +0,0 @@ - - - OpenSSL version $release released - ================================= - - OpenSSL - The Open Source toolkit for SSL/TLS - https://www.openssl.org/ - - The OpenSSL project team is pleased to announce the release of - version $release of our open source toolkit for SSL/TLS. - - For details of the changes, see the NEWS file at: - - https://github.openssl.org/openssl/extended-releases/blob/$release_tag/NEWS - - OpenSSL $release is available for download via HTTPS from the following - location on our support system: - - https://github.openssl.org/openssl/extended-releases/releases/tag/$release_tag - - If you have not yet established access to our support system server, - please contact us on osf-contact@openssl.org to arrange your set up. - - The distribution file name is: - - o $tarfile - Size: $length - SHA1 checksum: $sha1hash - SHA256 checksum: $sha256hash - - The checksums were calculated using the following commands: - - openssl sha1 -r $tarfile - openssl sha256 -r $tarfile - - Yours, - - The OpenSSL Project Team. - diff --git a/release-tools/release-aux/openssl-announce-release-public.tmpl b/release-tools/release-aux/openssl-announce-release-public.tmpl deleted file mode 100644 index 2546b137..00000000 --- a/release-tools/release-aux/openssl-announce-release-public.tmpl +++ /dev/null @@ -1,38 +0,0 @@ - - OpenSSL version $release released - ================================= - - OpenSSL - The Open Source toolkit for SSL/TLS - https://www.openssl.org/ - - The OpenSSL project team is pleased to announce the release of - version $release of our open source toolkit for SSL/TLS. - For details of the changes, see the release notes at: - - https://www.openssl.org/news/openssl-$series-notes.html - - Specific notes on upgrading to OpenSSL $series from previous versions are - available in the OpenSSL Migration Guide, here: - - https://www.openssl.org/docs/man$series/man7/migration_guide.html - - The OpenSSL $release is available for download at this URL: - - * https://github.com/openssl/openssl/releases - - The distribution file name is: - - o $tarfile - Size: $length - SHA1 checksum: $sha1hash - SHA256 checksum: $sha256hash - - The checksums were calculated using the following commands: - - openssl sha1 -r $tarfile - openssl sha256 -r $tarfile - - Yours, - - The OpenSSL Project Team. - diff --git a/release-tools/release-aux/release-data-fn.sh b/release-tools/release-aux/release-data-fn.sh deleted file mode 100644 index fa883ee3..00000000 --- a/release-tools/release-aux/release-data-fn.sh +++ /dev/null @@ -1,29 +0,0 @@ -#! /bin/bash -# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -# Public or premium release? Let the version numbers determine it! -declare -A _ossl_release_types=( - [premium]='^1\.' - [public]='^[3-9]\.' -) - -std_release_type () { - local v=$1 - local rt - local re - local release_type= - - for rt in "${!_ossl_release_types[@]}"; do - re="${_ossl_release_types[$rt]}" - if [[ "$v" =~ $re ]]; then - release_type=$rt - break - fi - done - echo $release_type -} diff --git a/release-tools/release-aux/release-state-fn.sh b/release-tools/release-aux/release-state-fn.sh deleted file mode 100644 index 6ebff17b..00000000 --- a/release-tools/release-aux/release-state-fn.sh +++ /dev/null @@ -1,214 +0,0 @@ -#! /bin/sh -# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -# This will increase the version number and pre-release tag, according to the -# current state of the source tree, and the function's first argument (called -# |next| internally), which is how the caller tells what the next step should -# be. -# -# The possible current source tree states are: -# '' The source is in a released state. -# 'dev' The source is in development. This is the normal state. -# 'alpha', 'alphadev' -# The source is undergoing a series of alpha releases. -# 'beta', 'betadev' -# The source is undergoing a series of beta releases. -# These states are computed from $PRE_LABEL and $TYPE -# -# The possible |next| values are: -# 'alpha' The source tree should move to an alpha release state, or -# stay there. This trips the alpha / pre-release counter. -# 'beta' The source tree should move to a beta release state, or -# stay there. This trips the beta / pre-release counter. -# 'final' The source tree should move to a final release (assuming it's -# currently in one of the alpha or beta states). This turns -# off the alpha or beta states. -# '' The source tree should move to the next release. The exact -# meaning depends on the current source state. It may mean -# tripping the alpha / beta / pre-release counter, or increasing -# the PATCH number. -# -# 'minor' The source tree should move to the next minor version. This -# should only be used in the master branch when a release branch -# has been created. -# -# This function uses a private function _fixup_version(), which takes |next| -# value as first argument, and SHOULD increase the label counter or the PATCH -# number accordingly, but only when the current state is "in development". - -# The following global variables are manipulated, either directly here or -# by the fixup_version function: -# -# PRE_LABEL -# PRE_NUM -# PATCH -# VERSION -# SERIES -# TYPE -# RELEASE_DATE - -next_release_state () { - local next="$1" - local today="$(date '+%-d %b %Y')" - local retry=true - - local before="$PRE_LABEL$TYPE" - - while $retry; do - retry=false - - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$before=$before" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$next=$next" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$MAJOR=$MAJOR" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$MINOR=$MINOR" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$FIX=$FIX" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$PATCH=$PATCH" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$TYPE=$TYPE" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$PRE_LABEL=$PRE_LABEL" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$PRE_NUM=$PRE_NUM" - $DEBUG >&2 "DEBUG[next_release_state]: BEGIN: \$RELEASE_DATE=$RELEASE_DATE" - - case "$before+$next" in - # MAKING ALPHA RELEASES ################################## - - # Alpha releases can't be made from beta versions or real versions - beta*+alpha | +alpha ) - echo >&2 "Invalid state for an alpha release" - echo >&2 "Try --beta or --final, or perhaps nothing" - exit 1 - ;; - # For alpha releases, the tag update is dev => alpha or - # alpha dev => alpha for the release itself, and - # alpha => alpha dev for post release. - dev+alpha | alphadev+alpha ) - TYPE= - RELEASE_DATE="$today" - fixup_version "alpha" - ;; - alpha+alpha ) - TYPE=dev - RELEASE_DATE= - fixup_version "alpha" - ;; - - # MAKING BETA RELEASES ################################### - - # Beta releases can't be made from real versions - +beta ) - echo >&2 "Invalid state for beta release" - echo >&2 "Try --final, or perhaps nothing" - exit 1 - ;; - # For beta releases, the tag update is dev => beta1, or - # alpha{n}-dev => beta1 when transitioning from alpha to - # beta, or beta{n}-dev => beta{n} for the release itself, - # or beta{n} => beta{n+1}-dev for post release. - dev+beta | alphadev+beta | betadev+beta ) - TYPE= - RELEASE_DATE="$today" - fixup_version "beta" - ;; - beta+beta ) - TYPE=dev - RELEASE_DATE= - fixup_version "beta" - ;; - # It's possible to switch from alpha to beta in the - # post release. That's what --next-beta does. - alpha+beta ) - TYPE=dev - RELEASE_DATE= - fixup_version "beta" - ;; - - # MAKING FINAL RELEASES ################################## - - # Final releases can't be made from the main development branch - dev+final) - echo >&2 "Invalid state for final release" - echo >&2 "This should have been preceded by an alpha or a beta release" - exit 1 - ;; - # For final releases, the starting point must be a dev state - alphadev+final | betadev+final ) - TYPE= - RELEASE_DATE="$today" - fixup_version "final" - ;; - # The final step of a final release is to switch back to - # development - +final ) - TYPE=dev - RELEASE_DATE= - fixup_version "final" - ;; - - # SWITCHING TO THE NEXT MINOR RELEASE #################### - - *+minor ) - TYPE=dev - RELEASE_DATE= - fixup_version "minor" - ;; - - # MAKING DEFAULT RELEASES ################################ - - # If we're coming from a non-dev, simply switch to dev. - # fixup_version() should trip up the PATCH number. - + ) - TYPE=dev - RELEASE_DATE= - fixup_version "" - ;; - - # If we're coming from development, switch to non-dev, unless - # the PATCH number is zero. If it is, we force the caller to - # go through the alpha and beta release process. - dev+ ) - if [ "$PATCH" = "0" -o "$PATCH" = "" ]; then - echo >&2 "Can't update PATCH version number from 0" - echo >&2 "Please use --alpha or --beta" - exit 1 - fi - TYPE= - RELEASE_DATE="$today" - fixup_version "" - ;; - - # If we're currently in alpha, we continue with alpha, as if - # the user had specified --alpha - alpha*+ ) - next=alpha - retry=true - ;; - - # If we're currently in beta, we continue with beta, as if - # the user had specified --beta - beta*+ ) - next=beta - retry=true - ;; - - *) - echo >&2 "Invalid combination of options" - exit 1 - ;; - esac - - $DEBUG >&2 "DEBUG[next_release_state]: END: \$before=$before" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$next=$next" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$MAJOR=$MAJOR" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$MINOR=$MINOR" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$FIX=$FIX" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$PATCH=$PATCH" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$TYPE=$TYPE" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$PRE_LABEL=$PRE_LABEL" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$PRE_NUM=$PRE_NUM" - $DEBUG >&2 "DEBUG[next_release_state]: END: \$RELEASE_DATE=$RELEASE_DATE" - done -} diff --git a/release-tools/release-aux/release-version-fn.sh b/release-tools/release-aux/release-version-fn.sh deleted file mode 100644 index 51f2495d..00000000 --- a/release-tools/release-aux/release-version-fn.sh +++ /dev/null @@ -1,353 +0,0 @@ -#! /bin/bash -# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -# These functions collect and manipulate information relevant for diverse -# OpenSSL versions, and populate the following variables: -# -# VERSION_FILE The path of the file where the version information -# is found and should be stored. If this is empty, -# no version information was found, and the release -# should be aborted. -# RELEASE_FILES The set of files that must be manipulated during a -# release, separated by semicolons. Other scripts are -# used to actually manipulate these files. -# -# MAJOR, MINOR, FIX, PATCH -# The three or four parts of a version number, depending -# on the version scheme. -# Examples: -# With OpenSSL 3.0.9, MAJOR=3, MINOR=0 and PATCH=9. -# With OpenSSL 1.1.1u, MAJOR=1, MINOR=1, FIX=1 and -# PATCH=t. -# PRE_RELEASE_TAG, BUILD_METADATA, RELEASE_DATE, SHLIB_VERSION -# Supplemental state data found in the version file. -# -# _PRE_RELEASE_TAG, _BUILD_METADATA -# Computed variants of PRE_RELEASE_TAG and BUILD_METADATA, -# with added markup suitable for version numbers in text -# form. -# -# SERIES The current release series. It is computed from -# MAJOR, MINOR and (possibly) FIX -# VERSION The current version number. It is computed from -# MAJOR, MINOR, (possibly) FIX and PATCH -# FULL_VERSION Like VERSION, but with metadata (PRE_RELEASE_TAG, -# BUILD_METADATA) added -# -# TYPE The state the source is in. It may have an empty value -# for released source, or 'dev' for "in development". -# -# PRE_LABEL May be "alpha" or "beta" to signify an ongoing series -# of alpha or beta releases. -# PRE_NUM A pre-release counter for the alpha and beta release -# series, but isn't necessarily strictly tied to the -# prerelease label. -# -# Scripts loading this file are not allowed to manipulate these variables -# directly. They must use next_release_state(), found in release-state-fn.sh. - -get_version () { - ### Reset all variables we defined - # VERSION_FILE is the version file used - VERSION_FILE= - - # These are the variables possibly extracted from the version file - MAJOR= - MINOR= - FIX= - PATCH= - PRE_RELEASE_TAG= - BUILD_METADATA= - RELEASE_DATE= - SHLIB_VERSION= - - # These are computed from extracted variables - SERIES= - VERSION= - FULL_VERSION= - TYPE= - PRE_LABEL= - PRE_NUM= - _PRE_RELEASE_TAG= - _BUILD_METADATA= - - RELEASE_FILES= - - # Detect possible version files. - # OpenSSL 3.0 and on use VERSION.dat. - # OpenSSL 1.1.y use include/openssl/opensslv.h - # OpenSSL 1.0.y (as well as OpenSSL 0.x.y) use crypto/opensslv.h - for vf in VERSION.dat include/openssl/opensslv.h crypto/opensslv.h; do - if [ -n "$(git ls-files $vf)" ]; then - VERSION_FILE=$vf - break - fi - done - - case "$VERSION_FILE" in - VERSION.dat ) - # The base version data is simply there in VERSION.dat, - # All we need is to evaluate that file like a shell script. - eval $(git cat-file blob HEAD:"$VERSION_FILE") - - if [ -n "$PRE_RELEASE_TAG" ]; then - _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}" - fi - if [ -n "$BUILD_METADATA" ]; then - _BUILD_METADATA="+${BUILD_METADATA}" - fi - - SERIES="$MAJOR.$MINOR" - VERSION="$MAJOR.$MINOR.$PATCH" - FULL_VERSION="$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA" - TYPE=$( echo "$PRE_RELEASE_TAG" \ - | sed -E \ - -e 's|^dev$|dev|' \ - -e 's|^alpha([0-9]+)(-(dev))?$|\3|' \ - -e 's|^beta([0-9]+)(-(dev))?$|\3|' ) - PRE_LABEL=$( echo "$PRE_RELEASE_TAG" \ - | sed -E \ - -e 's|^dev$||' \ - -e 's|^alpha([0-9]+)(-(dev))?$|alpha|' \ - -e 's|^beta([0-9]+)(-(dev))?$|beta|' ) - PRE_NUM=$( echo "$PRE_RELEASE_TAG" \ - | sed -E \ - -e 's|^dev$|0|' \ - -e 's|^alpha([0-9]+)(-(dev))?$|\1|' \ - -e 's|^beta([0-9]+)(-(dev))?$|\1|' ) - RELEASE_FILES='CHANGES.md;NEWS.md' - ;; - */opensslv.h ) - # opensslv.h is a bit more difficult to get version data from, - # as it involves find the C macro definition for it, and calculate - # the version number from hex digits, having the following meaning: - # - # 0xMNNFFPPSL - # - # For M = MAJOR, NN = MINOR, FF = FIX, PP = PATCH, S = STATE - # - # S has one of the values 0 for development, 1 to e for betas - # 1 to 14, and f for release. Because the versions using this - # scheme are all already released, and this scheme is otherwise - # abandonned, we only care about the state numbers 0 and f. - - # Extract the base version numbers by converting the macro - # definition of OPENSSL_VERSION_NUMBER into a small shell script - # that defines appropriate shell variables. It turns out the - # perl is the better processor for this sort of thing. - local version_extractor=' -if (m|^[[:space:]]*#[[:space:]]*define[[:space:]]+OPENSSL_VERSION_NUMBER[[:space:]]+0x([[:xdigit:]])([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]])L$|) { - my $PP = hex($4); - my $letter_PP = ""; - while ($PP > 25) { - $letter_PP .= "z"; - $PP -= 25; - } - if ($PP > 0) { - $letter_PP .= chr($PP + ord("a") - 1); - } - my $S = hex($5); - my $tag_S = $S == 0 ? "dev" : ""; - - print "MAJOR=",hex($1),"\n"; - print "MINOR=",hex($2),"\n"; - print "FIX=",hex($3),"\n"; - print "PATCH=",$letter_PP,"\n"; - print "PRE_RELEASE_TAG=$tag_S\n"; -} elsif (m|^[[:space:]]*#[[:space:]]*define[[:space:]]+SHLIB_VERSION_NUMBER[[:space:]]+"([^"]*)"$|) { - print "SHLIB_VERSION=$1\n"; -} -' - eval $(git cat-file blob HEAD:"$VERSION_FILE" \ - | perl -n -e "$version_extractor" ) - - # Additional data that's default or computed from the version - # number data. - if [ "$MINOR" -eq 0 ]; then - # 1.0.x - SHLIB_VERSION="$MAJOR.$MINOR.0" - else - # 1.1.x - SHLIB_VERSION="$MAJOR.$MINOR" - fi - - if [ -n "$PRE_RELEASE_TAG" ]; then - _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}" - fi - - SERIES="$MAJOR.$MINOR.$FIX" - VERSION="$MAJOR.$MINOR.$FIX$PATCH" - FULL_VERSION="$VERSION$_PRE_RELEASE_TAG" - TYPE=$PRE_RELEASE_TAG - PRE_LABEL= - PRE_NUM=0 - - if [ -n "$(git ls-files openssl.spec)" ]; then - # 1.0.x - RELEASE_FILES='README;CHANGES;NEWS;openssl.spec' - else - # 1.1.x - RELEASE_FILES='README;CHANGES;NEWS' - fi - ;; - * ) - ;; - esac -} - -fixup_version () { - local new_label="$1" - - case "$new_label" in - alpha | beta ) - if [ "$new_label" != "$PRE_LABEL" ]; then - PRE_LABEL="$new_label" - PRE_NUM=1 - elif [ "$TYPE" = 'dev' ]; then - PRE_NUM=$(expr $PRE_NUM + 1) - fi - ;; - final | '' ) - if [ "$TYPE" = 'dev' ]; then - case "$VERSION_FILE" in - VERSION.dat ) - PATCH=$(expr $PATCH + 1) - ;; - */opensslv.h ) - local -A patch_transitions - patch_transitions=( - [_]=a [_a]=b [_b]=c [_c]=d [_d]=e [_e]=f - [_f]=g [_g]=h [_h]=i [_i]=j [_j]=k [_k]=l - [_l]=m [_m]=n [_n]=o [_o]=p [_p]=q [_q]=r - [_r]=s [_s]=t [_t]=u [_u]=v [_v]=w [_w]=x - [_x]=y [_y]=za - ) - PATCH=$( eval set -- "$(echo $PATCH | sed -E -e 's|^(z*)([a-y]?)$|"\1" "\2"|')" - echo $1${patch_transitions[_$2]} ) - ;; - esac - fi - PRE_LABEL= - PRE_NUM=0 - ;; - minor ) - if [ "$TYPE" = 'dev' ]; then - case "$VERSION_FILE" in - VERSION.dat ) - MINOR=$(expr $MINOR + 1) - PATCH=0 - ;; - */opensslv.h ) - # Minor release updated the FIX number - FIX=$(expr $FIX + 1) - PATCH= - ;; - esac - fi - PRE_LABEL= - PRE_NUM=0 - ;; - esac - - case "$TYPE+$PRE_LABEL+$PRE_NUM" in - *++* ) - PRE_RELEASE_TAG="$TYPE" - ;; - dev+* ) - PRE_RELEASE_TAG="$PRE_LABEL$PRE_NUM-dev" - ;; - +* ) - PRE_RELEASE_TAG="$PRE_LABEL$PRE_NUM" - ;; - esac - - _PRE_RELEASE_TAG= - if [ -n "$PRE_RELEASE_TAG" ]; then - _PRE_RELEASE_TAG="-${PRE_RELEASE_TAG}" - fi - - case "$VERSION_FILE" in - VERSION.dat ) - SERIES="$MAJOR.$MINOR" - VERSION="$SERIES.$PATCH" - FULL_VERSION="$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA" - ;; - */opensslv.h ) - SERIES="$MAJOR.$MINOR.$FIX" - VERSION="$SERIES$PATCH" - FULL_VERSION="$VERSION$_PRE_RELEASE_TAG" - ;; - esac -} - -set_version () { - case "$VERSION_FILE" in - VERSION.dat ) - cat > "$VERSION_FILE" <&2 "Unknown % directive: $C" - exit 1 - fi - fi - result="$result$PRE$MID" - fmt="$POST" - done - echo "$result" -} diff --git a/release-tools/release-aux/test_suite.sh b/release-tools/release-aux/test_suite.sh deleted file mode 100755 index 9ecf735b..00000000 --- a/release-tools/release-aux/test_suite.sh +++ /dev/null @@ -1,263 +0,0 @@ -#! /usr/bin/env bash -# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -# This script runs a test suite to check the functions in release-state-fn.sh -# and release-version-fn.sh. It does this by setting up a small temporary -# repository with just enough fake data (in include/openssl/opensslv.h or -# VERSION.dat) to see that version data is updated correctly. - -DEBUG=: -export LANG=C - -HERE=$(cd $(dirname $0); pwd) -. $HERE/release-state-fn.sh -. $HERE/release-version-fn.sh - -today="$(date '+%-d %b %Y')" - -repo=release-test-$$.git -git init --quiet /var/tmp/$repo -cd /var/tmp/$repo -trap "cd $HERE; rm -rf /var/tmp/$repo" EXIT - -echo "===== OpenSSL 3.0 version scheme" - -cat > VERSION.dat <<_____ -MAJOR=3 -MINOR=2 -PATCH=0 -PRE_RELEASE_TAG=dev -BUILD_METADATA= -RELEASE_DATE="" -SHLIB_VERSION=3 -_____ -git add VERSION.dat -git commit -m 'Fake 3.2.0-dev' --quiet - -declare -A expected - -function check () { - local errs=0 - - for key in "${!expected[@]}"; do - if [ "${!key}" != "${expected[$key]}" ]; then - (( errs++ )) - fi - done - - if [ $errs -gt 0 ]; then - echo >&2 "Got the wrong data:" - for key in "${!expected[@]}"; do - echo >&2 " \$$key=${!key}" - done - echo >&2 "Expected:" - for key in "${!expected[@]}"; do - echo >&2 " \$$key=${expected[$key]}" - done - exit 1 - fi -} - -echo "Initial read of VERSION.DAT" -expected=( - [TYPE]=dev - [SERIES]=3.2 - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0-dev - [PRE_RELEASE_TAG]=dev - [SHLIB_VERSION]=3 - [RELEASE_FILES]='CHANGES.md;NEWS.md' -) -get_version -check - -echo "Test release of 3.2.0-alpha1" -expected=( - [TYPE]= - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0-alpha1 - [PRE_RELEASE_TAG]=alpha1 - [RELEASE_DATE]="$today" -) -next_release_state alpha -check - -echo "Test post-release of 3.2.0-alpha1" -expected=( - [TYPE]=dev - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0-alpha2-dev - [PRE_RELEASE_TAG]=alpha2-dev - [RELEASE_DATE]= -) -next_release_state alpha -check - -echo "Test release of 3.2.0-beta1" -expected=( - [TYPE]= - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0-beta1 - [PRE_RELEASE_TAG]=beta1 - [RELEASE_DATE]="$today" -) -next_release_state beta -check - -echo "Test post-release of 3.2.0-beta1" -expected=( - [TYPE]=dev - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0-beta2-dev - [PRE_RELEASE_TAG]=beta2-dev - [RELEASE_DATE]= -) -next_release_state beta -check - -echo "Test release of 3.2.0" -expected=( - [TYPE]= - [VERSION]=3.2.0 - [FULL_VERSION]=3.2.0 - [PRE_RELEASE_TAG]= - [RELEASE_DATE]="$today" -) -next_release_state final -check - -echo "Test post-release of 3.2.0" -expected=( - [TYPE]=dev - [VERSION]=3.2.1 - [FULL_VERSION]=3.2.1-dev - [PRE_RELEASE_TAG]=dev - [RELEASE_DATE]= -) -next_release_state final -check - -echo "Test release of 3.2.1" -expected=( - [TYPE]= - [VERSION]=3.2.1 - [FULL_VERSION]=3.2.1 - [PRE_RELEASE_TAG]= - [RELEASE_DATE]="$today" -) -next_release_state '' -check - -echo "Test post-release of 3.2.1" -expected=( - [TYPE]=dev - [VERSION]=3.2.2 - [FULL_VERSION]=3.2.2-dev - [PRE_RELEASE_TAG]=dev - [RELEASE_DATE]= -) -next_release_state '' -check - -echo "Test switch to next minor release (3.3.0-dev)" -expected=( - [TYPE]=dev - [VERSION]=3.3.0 - [FULL_VERSION]=3.3.0-dev - [PRE_RELEASE_TAG]=dev - [RELEASE_DATE]= -) -next_release_state minor -check - -echo "Test writing $VERSION_FILE" -set_version -cat > expected-VERSION.dat <<_____ -MAJOR=3 -MINOR=3 -PATCH=0 -PRE_RELEASE_TAG=dev -BUILD_METADATA= -RELEASE_DATE="" -SHLIB_VERSION=3 -_____ -if ! diff_output="$(diff -u expected-VERSION.dat VERSION.dat)"; then - echo >&2 "$diff_output" - exit 1 -fi - -echo "===== OpenSSL 1.0.2 version scheme" - -git restore . -git rm --quiet VERSION.dat -mkdir crypto -cat > crypto/opensslv.h <<_____ -# define OPENSSL_VERSION_NUMBER 0x10002210L -# ifdef OPENSSL_FIPS -# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zh-fips-dev xx XXX xxxx" -# else -# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zh-dev xx XXX xxxx" -# endif -# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT -_____ -touch openssl.spec -git add openssl.spec crypto/opensslv.h -git commit -m 'Fake 1.0.2zh-dev' --quiet - -echo "Test initial read of crypto/opensslv.h" -expected=( - [TYPE]=dev - [SERIES]=1.0.2 - [VERSION]=1.0.2zh - [FULL_VERSION]=1.0.2zh-dev - [PRE_RELEASE_TAG]=dev - [SHLIB_VERSION]=1.0.0 - [RELEASE_FILES]='README;CHANGES;NEWS;openssl.spec' -) -get_version -check - -echo "Test release of 1.0.2zh" -expected=( - [TYPE]= - [VERSION]=1.0.2zh - [FULL_VERSION]=1.0.2zh - [PRE_RELEASE_TAG]= - [RELEASE_DATE]="$today" -) -next_release_state '' -check - -echo "Test post-release of 1.0.2zh" -expected=( - [TYPE]=dev - [VERSION]=1.0.2zi - [FULL_VERSION]=1.0.2zi-dev - [PRE_RELEASE_TAG]=dev - [RELEASE_DATE]= -) -next_release_state '' -check - -echo "Test writing $VERSION_FILE" -set_version -cat > crypto/expected-opensslv.h <<_____ -# define OPENSSL_VERSION_NUMBER 0x10002220L -# ifdef OPENSSL_FIPS -# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zi-fips-dev xx XXX xxxx" -# else -# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2zi-dev xx XXX xxxx" -# endif -# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT -_____ -if ! diff_output="$(diff -u crypto/expected-opensslv.h crypto/opensslv.h)"; then - echo >&2 "$diff_output" - exit 1 -fi - -echo "===== PASS =====" diff --git a/release-tools/release-aux/upload-fn.sh b/release-tools/release-aux/upload-fn.sh deleted file mode 100644 index 8b571385..00000000 --- a/release-tools/release-aux/upload-fn.sh +++ /dev/null @@ -1,97 +0,0 @@ -#! /bin/bash -e -# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -# These functions all perform uploads, using sftp commands. -# They all expect two parameters: -# -# $1 The destination, properly formed for the backend. -# In other words, it must be usable with SFTP for the sftp backend, -# and it must be a proper existing directory for the file backend. -# $2 A flag, saying if the upload should (true) or shouldn't (false) -# be performed. In verbose mode (governed by the variable $VERBOSE) -# this is useful to output what would happen if uploading was enabled. -# -# They also use the variable $VERBOSE as a command to perform verbose output. -# They may also use the variable $DEBUG as a command to perform debugging -# output. -# These variable must be set accordingly by the loading script. Recommended -# values are ':' for non-verbose and 'echo' for verbose' - -upload_backend_sftp () { - local to=$1 - local do_upload=$2 - - if [ -z "$to" ]; then - echo >&2 "No SFTP address was provided" - exit 1 - fi - if [ -z "$do_upload" ]; then - echo >&2 "Upload or not? The flag hasn't been set" - exit 1 - fi - - if $do_upload; then - sftp $to - else - $VERBOSE "Would 'sftp $to' with the following commands:" - while read L; do - $VERBOSE " $L" - done - fi -} - -upload_backend_file () { - local dest=$1 - local do_upload=$2 - - if [ -z "$dest" ]; then - echo >&2 "No destination directory was provided" - exit 1 - fi - if ! [ -d "$dest" ]; then - echo >&2 "Not a directory: $dest" - exit 1 - fi - if [ -z "$do_upload" ]; then - echo >&2 "Upload or not? The flag hasn't been set" - exit 1 - fi - - ( - progress= - while read L; do - set -- $L - case $1 in - progress ) - if [ -z "$progress" ]; then - progress=-v - else - progress= - fi - ;; - cd ) - if [ -d "$dest/$2" ]; then - dest="$dest/$2" - else - echo >&2 "Warning: Not a directory: $dest/$2" - fi - ;; - put ) - if $do_upload; then - cp $progress $2 $dest/$3 - else - $VERBOSE "Would copy $2 -> $dest/$3" - fi - ;; - * ) - echo >&2 "Warning: Unknown command: $@" - ;; - esac - done - ) -} diff --git a/release-tools/stage-release b/release-tools/stage-release new file mode 100755 index 00000000..a7953642 --- /dev/null +++ b/release-tools/stage-release @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Stage an OpenSSL release. + +Run from inside an OpenSSL source worktree. This lives beside the +`stagerelease` package and is invoked by absolute path, so it puts its own +directory on sys.path rather than requiring an install. +""" +from __future__ import annotations + +import sys +from pathlib import Path + +if sys.version_info < (3, 10): + sys.exit( + "stage-release needs Python 3.10 or later, found " + f"{sys.version_info.major}.{sys.version_info.minor}" + ) + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from stagerelease.cli import main # noqa: E402 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/release-tools/stage-release.sh b/release-tools/stage-release.sh deleted file mode 100755 index dd0ac706..00000000 --- a/release-tools/stage-release.sh +++ /dev/null @@ -1,1393 +0,0 @@ -#! /usr/bin/env bash -# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -set -e - -# This is the most shell agnostic way to specify that POSIX rules. -POSIXLY_CORRECT=1 - -# Force C locale because some commands (like date +%b) relies -# on the current locale. -export LC_ALL=C - -usage () { - cat < - Format for branch names. - Default is "%b" for the release branch. ---tag-fmt= Format for tag names. - Default is "%t" for the release tag. - ---reviewer= The reviewer of the commits. ---local-user= - For the purpose of signing tags and tar files, use this - key (default: use the default e-mail address’ key). ---unsigned Do not sign anything. - ---staging-address=
- The staging location to upload release files to (default: - upload@dev.openssl.org) ---no-upload Don't upload the staging release files. ---no-update Don't perform 'make update' and 'make update-fips-checksums'. ---quiet Really quiet, only the final output will still be output. ---verbose Verbose output. ---debug Include debug output. Implies --no-upload. ---porcelain Give the output in an easy-to-parse format for scripts. - ---force Force execution - ---help This text ---manual The manual - -If none of --alpha, --beta, or --final are given, this script tries to -figure out the next step. -EOF - exit 0 -} - -# Set to one of 'major', 'minor', 'alpha', 'beta' or 'final' -next_method= -next_method2= - -do_branch=false -warn_branch=false - -do_upload=true -do_update=true - -clean_worktree=false - -default_branch_fmt='OSSL--%b--%v' -default_tag_fmt='%t' - -ECHO=echo -DEBUG=: -VERBOSE=: -git_quiet=-q -do_porcelain=false - -force=false - -do_help=false -do_manual=false - -do_signed=true -tagkey=' -s' -gpgkey= -reviewers= - -staging_address=upload@dev.openssl.org - -TEMP=$(getopt -l 'alpha,next-beta,beta,final' \ - -l 'branch' \ - -l 'clean-worktree' \ - -l 'branch-fmt:,tag-fmt:' \ - -l 'reviewer:' \ - -l 'local-user:,unsigned' \ - -l 'staging-address:' \ - -l 'no-upload,no-update' \ - -l 'quiet,verbose,debug' \ - -l 'porcelain' \ - -l 'force' \ - -l 'help,manual' \ - -n stage-release.sh -- - "$@") -eval set -- "$TEMP" -while true; do - case $1 in - --alpha | --beta | --final ) - next_method=$(echo "x$1" | sed -e 's|^x--||') - if [ -z "$next_method2" ]; then - next_method2=$next_method - fi - shift - if [ "$next_method" = 'final' ]; then - do_branch=true - fi - ;; - --next-beta ) - next_method2=$(echo "x$1" | sed -e 's|^x--next-||') - shift - ;; - --branch ) - do_branch=true - warn_branch=true - shift - ;; - --clean-worktree ) - clean_worktree=true - default_branch_fmt='%b' - default_tag_fmt='%t' - shift - ;; - --branch-fmt ) - shift - branch_fmt="$1" - shift - ;; - --tag-fmt ) - shift - tag_fmt="$1" - shift - ;; - --reviewer ) - reviewers="$reviewers $1=$2" - shift - shift - ;; - --local-user ) - shift - do_signed=true - tagkey=" -u $1" - gpgkey=" -u $1" - shift - ;; - --unsigned ) - shift - do_signed=false - tagkey=" -a" - gpgkey= - ;; - --staging-address ) - shift - staging_address="$1" - shift - ;; - --no-upload ) - do_upload=false - shift - ;; - --no-update ) - do_update=false - shift - ;; - --quiet ) - ECHO=: - VERBOSE=: - shift - ;; - --verbose ) - ECHO=echo - VERBOSE=echo - git_quiet= - shift - ;; - --debug ) - DEBUG=echo - do_upload=false - shift - ;; - --porcelain ) - do_porcelain=true - shift - ;; - --force ) - force=true - shift - ;; - --help ) - usage - exit 0 - ;; - --manual ) - sed -e '1,/^### BEGIN MANUAL/d' \ - -e '/^### END MANUAL/,$d' \ - < "$0" \ - | pod2man \ - | man -l - - exit 0 - ;; - -- ) - shift - break - ;; - * ) - echo >&2 "Unknown option $1" - shift - exit 1 - ;; - esac -done - -if [ -z "$branch_fmt" ]; then branch_fmt="$default_branch_fmt"; fi -if [ -z "$tag_fmt" ]; then tag_fmt="$default_tag_fmt"; fi - -$DEBUG >&2 "DEBUG: \$next_method=$next_method" -$DEBUG >&2 "DEBUG: \$next_method2=$next_method2" - -$DEBUG >&2 "DEBUG: \$do_branch=$do_branch" - -$DEBUG >&2 "DEBUG: \$do_upload=$do_upload" -$DEBUG >&2 "DEBUG: \$do_update=$do_update" -$DEBUG >&2 "DEBUG: \$DEBUG=$DEBUG" -$DEBUG >&2 "DEBUG: \$VERBOSE=$VERBOSE" -$DEBUG >&2 "DEBUG: \$git_quiet=$git_quiet" - -case "$next_method+$next_method2" in - major+major | minor+minor ) - # These are expected - ;; - alpha+alpha | alpha+beta | beta+beta | final+final | + | +beta ) - # These are expected - ;; - * ) - echo >&2 "Internal option error ($next_method, $next_method2)" - exit 1 - ;; -esac - -# Verbosity feed for certain commands -VERBOSITY_FIFO=/tmp/openssl-$$.fifo -mkfifo -m 600 $VERBOSITY_FIFO -( cat $VERBOSITY_FIFO | while read L; do $VERBOSE "> $L"; done ) & -exec 42>$VERBOSITY_FIFO -trap "exec 42>&-; rm $VERBOSITY_FIFO" 0 2 - -# Setup ############################################################## - -RELEASE_TOOLS=$(dirname $(realpath $(type -p $0))) -RELEASE_AUX="$RELEASE_TOOLS/release-aux" - -# Check that we have external scripts that we use -found=true -for fn in "$RELEASE_TOOLS/do-copyright-year"; do - if ! [ -f "$fn" ]; then - echo >&2 "'$fn' is missing" - found=false - fi -done -if ! $found; then - exit 1 -fi - -# Check that we have the scripts that define functions we use -found=true -for fn in "$RELEASE_AUX/release-version-fn.sh" \ - "$RELEASE_AUX/release-state-fn.sh" \ - "$RELEASE_AUX/release-data-fn.sh" \ - "$RELEASE_AUX/string-fn.sh" \ - "$RELEASE_AUX/upload-fn.sh"; do - if ! [ -f "$fn" ]; then - echo >&2 "'$fn' is missing" - found=false - fi -done -if ! $found; then - exit 1 -fi - -# Load version functions -. $RELEASE_AUX/release-version-fn.sh -. $RELEASE_AUX/release-state-fn.sh -. $RELEASE_AUX/release-data-fn.sh -# Load string manipulation functions -. $RELEASE_AUX/string-fn.sh -# Load upload backend functions -. $RELEASE_AUX/upload-fn.sh - -# Make sure we're in the work directory, and remember it -if HERE=$(git rev-parse --show-toplevel); then - : -else - echo >&2 "Not in a git worktree" - exit 1 -fi - -# Make sure that it's a plausible OpenSSL work tree, by checking -# that a version file is found -get_version - -if [ -z "$VERSION_FILE" ]; then - echo >&2 "Couldn't find OpenSSL version data" - exit 1 -fi - -orig_HEAD=$(git rev-parse HEAD) -orig_branch=$(git rev-parse --abbrev-ref HEAD) -orig_remote=$(git for-each-ref --format='%(push:remotename)' \ - $(git symbolic-ref -q HEAD)) -if ! orig_remote_url=$(git remote get-url $orig_remote 2>/dev/null); then - # If there is no registered remote, then $orig_remote is the URL - orig_remote_url="$orig_remote" -fi -orig_head=$(git rev-parse --abbrev-ref '@{u}' 2>/dev/null || git rev-parse HEAD) - -# Make sure it's a branch we recognise -if (echo "$orig_branch" \ - | grep -E -q \ - -e '^master$' \ - -e '^OpenSSL_[0-9]+_[0-9]+_[0-9]+[a-z]*-stable$' \ - -e '^openssl-[0-9]+\.[0-9]+$'); then - : -elif $force; then - : -else - echo >&2 "Not in master or any recognised release branch" - echo >&2 "Please 'git checkout' an appropriate branch" - exit 1 -fi - -# Make sure that we have fixup scripts for all the files that need -# to be modified for a release. We trust this, because we're not -# going to change versioning scheme in the middle of a release. -save_IFS=$IFS -IFS=';' -found=true -for fn in $RELEASE_FILES; do - for file in "$RELEASE_AUX/fixup-$fn-release.pl" \ - "$RELEASE_AUX/fixup-$fn-postrelease.pl"; do - if ! [ -f "$file" ]; then - echo >&2 "'$file' is missing" - found=false - fi - done -done -IFS=$save_IFS -if ! $found; then - exit 1 -fi - -# We turn staging_address into a few variables, which can be used -# by backends that must understand a subset of the SFTP commands -staging_directory= -staging_backend= -case "$staging_address" in - *:* ) - # Something with a colon is interpreted as the typical SCP - # location. We reinterpret that in our terms - staging_directory="${staging_address#*:}" - staging_address="${staging_address%%:*}" - staging_backend=sftp - ;; - *@* ) - staging_backend=sftp - ;; - sftp://?*/* | sftp://?* ) - # First, remove the URI scheme - staging_address="${staging_address#sftp://}" - # Now we know that we have a host, followed by a slash, followed by - # a directory spec. If there is no slash, there's no directory. - staging_directory="${staging_address#*/}" - if [ "$staging_directory" = "$staging_address" ]; then - # There was nothing with a slash to remove, so no directory. - staging_directory= - fi - staging_address="${staging_address%%/*}" - staging_backend=sftp - ;; - sftp:* ) - echo >&2 "Invalid staging address $staging_address" - exit 1 - ;; - * ) - if $do_upload && ! [ -d "$staging_address" ]; then - echo >&2 "Not an existing directory: $staging_address" - exit 1 - fi - staging_backend=file - ;; -esac - -# Initialize ######################################################### - -$ECHO "== Initializing work tree" - -release_clone= -if $clean_worktree; then - if [ -n "$(git status -s)" ]; then - echo >&2 "You've specified --clean-worktree, but your worktree is unclean" - exit 1 - fi -else - # Generate a cloned directory name - release_clone="$orig_branch-release-tmp" - - $ECHO "== Work tree will be in $release_clone" - - # Make a clone in a subdirectory and move there - if ! [ -d "$release_clone" ]; then - $VERBOSE "== Cloning to $release_clone" - git clone $git_quiet -b "$orig_branch" -o parent . "$release_clone" - fi - cd "$release_clone" -fi - -get_version - -# Branches to start from. The release branch is where the changes for the -# release are made, and the update branch is where the post-release changes are -# made. If --branch was given and is relevant, they should be different (and -# the update branch should be 'master'), otherwise they should be the same. -orig_update_branch="$orig_branch" -orig_release_branch="$(std_branch_name)" - -# among others, we only create a release branch if the patch number is zero -if [ "$orig_update_branch" = "$orig_release_branch" ] \ - || [ -n "$PATCH" -a "$PATCH" != 0 ]; then - if $do_branch && $warn_branch; then - echo >&2 "Warning! We're already in a release branch; --branch ignored" - fi - do_branch=false -fi - -if $do_branch; then - if [ "$orig_update_branch" != "master" ]; then - echo >&2 "--branch is invalid unless the current branch is 'master'" - exit 1 - fi - # No need to check if $orig_update_branch and $orig_release_branch differ, - # 'cause the code a few lines up guarantee that if they are the same, - # $do_branch becomes false -else - # In this case, the computed release branch may differ from the update branch, - # even if it shouldn't... this is the case when alpha or beta releases are - # made in the master branch, which is perfectly ok. Therefore, simply reset - # the release branch to be the same as the update branch and carry on. - orig_release_branch="$orig_update_branch" -fi - -# Check that the current branch is still on the same branch as our parent repo, -# or on a release branch -current_branch=$(git rev-parse --abbrev-ref HEAD) -if [ "$current_branch" = "$orig_update_branch" ]; then - : -elif [ "$current_branch" = "$orig_release_branch" ]; then - : -else - # It is an error to end up here. Let's try to figure out what went wrong - - if $clean_worktree; then - # We should never get here. If we do, something is incorrect in - # the code above. - echo >&2 "Unexpected current branch: $current_branch" - else - echo >&2 "The cloned sub-directory '$release_clone' is on a branch" - if [ "$orig_update_branch" = "$orig_release_branch" ]; then - echo >&2 "other than '$orig_update_branch'." - else - echo >&2 "other than '$orig_update_branch' or '$orig_release_branch'." - fi - echo >&2 "Please 'cd \"$(pwd)\"; git checkout $orig_update_branch'" - fi - exit 1 -fi - -SOURCEDIR=$(pwd) -$DEBUG >&2 "DEBUG: Source directory is $SOURCEDIR" - -# Release ############################################################ - -# We always expect to start from a state of development -if [ "$TYPE" != 'dev' ]; then - if $clean_worktree; then - cat >&2 <&2 <&42 - -$VERBOSE "== Checking source file updates and fips checksums" - -make update >&42 -# As long as we're doing an alpha release, we can have symbols without specific -# numbers assigned. In a beta or final release, all symbols MUST have an -# assigned number. -if [ "$next_method" != 'alpha' ] && grep -q '^renumber *:' Makefile; then - make renumber >&42 -fi -if grep -q '^update-fips-checksums *:' Makefile; then - make clean >&42 - make update-fips-checksums >&42 -fi - -if [ -n "$(git status --porcelain --untracked-files=no --ignore-submodules=all)" ]; then - $VERBOSE "== Committing updates" - git add -u - git commit $git_quiet -m $'make update\n\nRelease: yes' - if [ -n "$reviewers" ]; then - addrev --release --nopr $reviewers - fi -fi - -# Create a update branch, unless it's the same as the update branch -if [ "$release_branch" != "$update_branch" ]; then - $VERBOSE "== Creating a local release branch and switch to it: $release_branch" - git checkout $git_quiet -b "$release_branch" -fi - -# Write the version information we updated -set_version - -release="$FULL_VERSION" -if [ -n "$PRE_LABEL" ]; then - release_text="$SERIES$_BUILD_METADATA $PRE_LABEL $PRE_NUM" - announce_template=openssl-announce-pre-release.tmpl -else - release_type=$(std_release_type $VERSION) - release_text="$release" - announce_template=openssl-announce-release-$release_type.tmpl -fi -$VERBOSE "== Updated version information to $release" - -$VERBOSE "== Updating files with release date for $release : $RELEASE_DATE" -( - IFS=';' - for file in $RELEASE_FILES; do - fixup="$RELEASE_AUX/fixup-$(basename "$file")-release.pl" - $VERBOSE "> $file" - RELEASE="$release" RELEASE_TEXT="$release_text" RELEASE_DATE="$RELEASE_DATE" \ - perl -pi $fixup $file - done -) - -$VERBOSE "== Committing updates and tagging" -git add -u -git commit $git_quiet -m "Prepare for release of $release_text"$'\n\nRelease: yes' -if [ -n "$reviewers" ]; then - addrev --release --nopr $reviewers -fi -$ECHO "Tagging release with tag $release_tag. You may need to enter a pass phrase" -git tag$tagkey "$release_tag" -m "OpenSSL $release release tag" - -tarfile=openssl-$release.tar -tgzfile=$tarfile.gz -metadata=openssl-$release.dat -announce=openssl-$release.txt - -$ECHO "== Generating tar, hash, announcement and metadata files." -$ECHO "== This make take a bit of time..." - -$VERBOSE "== Making tarfile: $tgzfile" - -# Unfortunately, some tarball generators do verbose output on STDERR... for -# good reason, but it means we don't display errors unless --verbose -( - if [ -f ./util/mktar.sh ]; then - ./util/mktar.sh --tarfile="../$tarfile" 2>&1 - else - make DISTTARVARS=TARFILE="../$tarfile" dist 2>&1 - fi -) | while read L; do $VERBOSE "> $L"; done - -if ! [ -f "../$tgzfile" ]; then - echo >&2 "Where did the tarball end up? (../$tgzfile)" - exit 1 -fi - -$VERBOSE "== Generating checksums: $tgzfile.sha1 $tgzfile.sha256" -sha1hash=$(openssl sha1 < "../$tgzfile" | \ - (IFS='= '; while read X H; do echo $H; done)) -echo $sha1hash "$tgzfile" > "../$tgzfile.sha1" -sha256hash=$(openssl sha256 < "../$tgzfile" | \ - (IFS='= '; while read X H; do echo $H; done)) -echo $sha256hash "$tgzfile" > "../$tgzfile.sha256" -length=$(wc -c < "../$tgzfile") - -$VERBOSE "== Generating announcement text: $announce" -# Hack the announcement template -cat "$RELEASE_AUX/$announce_template" \ - | sed -e "s|\\\$release_text|$release_text|g" \ - -e "s|\\\$release_tag|$release_tag|g" \ - -e "s|\\\$release|$release|g" \ - -e "s|\\\$series|$SERIES|g" \ - -e "s|\\\$label|$PRE_LABEL|g" \ - -e "s|\\\$tarfile|$tgzfile|" \ - -e "s|\\\$length|$length|" \ - -e "s|\\\$sha1hash|$sha1hash|" \ - -e "s|\\\$sha256hash|$sha256hash|" \ - | perl -p "$RELEASE_AUX/fix-title.pl" \ - > "../$announce" - -$VERBOSE "== Generating signatures: $tgzfile.asc $announce.asc" -rm -f "../$tgzfile.asc" "../$announce.asc" -$ECHO "Signing the release files. You may need to enter a pass phrase" -if $do_signed; then - gpg$gpgkey --use-agent -sba "../$tgzfile" - gpg$gpgkey --use-agent -sta --clearsign "../$announce" -fi - -if ! $clean_worktree; then - # Push everything to the parent repo - $VERBOSE "== Push what we have to the parent repository" - git push --follow-tags parent HEAD -fi - -if $do_signed; then - staging_files=( "$tgzfile" "$tgzfile.sha1" "$tgzfile.sha256" - "$tgzfile.asc" "$announce.asc" ) -else - staging_files=( "$tgzfile" "$tgzfile.sha1" "$tgzfile.sha256" "$announce" ) -fi - -$VERBOSE "== Generating metadata file: $metadata" - -( - set -x - if [ "$update_branch" != "$orig_update_branch" ]; then - echo "staging_update_branch='$update_branch'" - fi - echo "update_branch='$orig_update_branch'" - if [ "$release_branch" != "$update_branch" ]; then - if [ "$release_branch" != "$orig_release_branch" ]; then - echo "staging_release_branch='$release_branch'" - fi - echo "release_branch='$orig_release_branch'" - fi - echo "release_tag='$release_tag'" - echo "upload_files='${staging_files[@]}'" - echo "source_repo='$orig_remote_url'" -) > ../$metadata - -if $do_upload; then - $ECHO "== Upload tar, hash, announcement and metadata files to staging location" -fi - -( - # With sftp, the progress meter is enabled by default, - # so we turn it off unless --verbose was given - if [ "$VERBOSE" == ':' ]; then - echo "progress" - fi - if [ -n "$staging_directory" ]; then - echo "cd $staging_directory" - fi - for uf in "${staging_files[@]}" "$metadata"; do - echo "put ../$uf" - done -) | upload_backend_$staging_backend "$staging_address" $do_upload - -# Post-release ####################################################### - -# Reset the files to their pre-release contents. This doesn't affect -# HEAD, but simply set all the files in a state that 'git revert -n HEAD' -# would have given, but without the artifacts that 'git revert' adds. -# -# This allows all the post-release fixup scripts to perform from the -# same point as the release fixup scripts, hopefully making them easier -# to write. This also makes the same post-release fixup scripts easier -# to run when --branch has been used, as they will be run both on the -# release branch and on the update branch, essentially from the same -# state for affected files. -$VERBOSE "== Reset all files to their pre-release contents" -git reset $git_quiet HEAD^ -- . -git checkout -- . - -prev_release_text="$release_text" -prev_release_date="$RELEASE_DATE" - -next_release_state "$next_method2" -set_version - -release="$FULL_VERSION" -release_text="$VERSION$_BUILD_METADATA" -if [ -n "$PRE_LABEL" ]; then - release_text="$SERIES$_BUILD_METADATA $PRE_LABEL $PRE_NUM" -fi -$VERBOSE "== Updated version information to $release" - -$VERBOSE "== Updating files for $release :" -( - IFS=';' - for file in $RELEASE_FILES; do - fixup="$RELEASE_AUX/fixup-$(basename "$file")-postrelease.pl" - $VERBOSE "> $file" - RELEASE="$release" RELEASE_TEXT="$release_text" \ - PREV_RELEASE_TEXT="$prev_release_text" \ - PREV_RELEASE_DATE="$prev_release_date" \ - perl -pi $fixup $file - done -) - -$VERBOSE "== Committing updates" -git add -u -git commit $git_quiet -m "Prepare for $release_text"$'\n\nRelease: yes' -if [ -n "$reviewers" ]; then - addrev --release --nopr $reviewers -fi - -if ! $clean_worktree; then - # Push everything to the parent repo - $VERBOSE "== Push what we have to the parent repository" - git push parent HEAD -fi - -if [ "$release_branch" != "$update_branch" ]; then - $VERBOSE "== Going back to the update branch $update_branch" - git checkout $git_quiet "$update_branch" - - get_version - next_release_state "minor" - set_version - - release="$FULL_VERSION" - release_text="$SERIES$_BUILD_METADATA" - $VERBOSE "== Updated version information to $release" - - $VERBOSE "== Updating files for $release :" - ( - IFS=';' - for file in $RELEASE_FILES; do - fixup="$RELEASE_AUX/fixup-$(basename "$file")-postrelease.pl" - $VERBOSE "> $file" - RELEASE="$release" RELEASE_TEXT="$release_text" \ - perl -pi $fixup $file - done - ) - - $VERBOSE "== Committing updates" - git add -u - git commit $git_quiet -m "Prepare for $release_text"$'\n\nRelease: yes' - if [ -n "$reviewers" ]; then - addrev --release --nopr $reviewers - fi -fi - -if ! $clean_worktree; then - # Push everything to the parent repo - $VERBOSE "== Push what we have to the parent repository" - git push parent HEAD -fi - -# Done ############################################################### - -$VERBOSE "== Done" - -cd $HERE -if $do_porcelain; then - if [ -n "$release_clone" ]; then - echo "clone_directory='$release_clone'" - fi - echo "orig_head='$orig_head'" - echo "metadata='$metadata'" -else - cat < -[ -B<--alpha> | -B<--next-beta> | -B<--beta> | -B<--final> | -B<--branch> | -B<--clean-worktree> | -B<--branch-fmt>=I | -B<--tag-fmt>=I | -B<--local-user>=I | -B<--unsigned> | -B<--reviewer>=I | -B<--staging-address>=I
| -B<--no-upload> | -B<--no-update> | -B<--quiet> | -B<--verbose> | -B<--debug> | -B<--porcelain> | -B<--help> | -B<--manual> -] - -=head1 DESCRIPTION - -B creates an OpenSSL release, given current worktree -conditions. It will refuse to work unless the current branch is C -or a release branch (see L below for a -discussion on those). - -B tries to be smart and figure out the next release if no -hints are given through options, and will exit with an error in ambiguous -cases. - -B normally finishes off with instructions on what to do -next. When B<--porcelain> is given, it finishes off with script friendly -data instead, see the description of that option. When finishing commands -are given, they must be followed exactly. - -B normally leaves behind a clone of the local repository, -as a subdirectory in the current worktree, as well as an extra branch with -the results of running this script in the local repository. This extra -branch is useful to create a pull request from, which will also be mentioned -at the end of the run of B. This local clone subdirectory -as well as this extra branch can safely be removed after all instructions -have been successfully followed. - -When the option B<--clean-worktree> is given, B has a -different behaviour. In this case, it doesn't create that clone or any -extra branch, and it will update the current branch of the worktree -directly. This is useful when it's desirable to push the changes directly -to a remote repository without having to go through a pull request and -approval process. - -=head1 OPTIONS - -=over 4 - -=item B<--alpha>, B<--beta> - -Set the state of this branch to indicate that alpha or beta releases are -to be done. - -B<--alpha> is only acceptable if the I version number is zero and -the current state is "in development" or that alpha releases are ongoing. - -B<--beta> is only acceptable if the I version number is zero and -that alpha or beta releases are ongoing. - -=item B<--next-beta> - -Use together with B<--alpha> to switch to beta releases after the current -release is done. - -=item B<--final> - -Set the state of this branch to indicate that regular releases are to be -done. This is only valid if alpha or beta releases are currently ongoing. - -This implies B<--branch>. - -=item B<--branch> - -Create a branch specific for the I release series, if it doesn't -already exist, and switch to it when making the release files. The exact -branch name will be C<< openssl-I >>. - -=item B<--clean-worktree> - -This indicates that the current worktree is clean and can be acted on -directly, instead of creating a clone of the local repository or creating -any extra branch. - -=item B<--branch-fmt>=I - -=item B<--tag-fmt>=I - -Format for branch and tag names. This can be used to tune the names of -branches and tags that are updated or added by this script. - -I can include printf-like formating directives: - -=over 4 - -=item %b - -is replaced with a branch name. This branch name is usually the current -branch of the current repository, but may also be the default release -branch name that is generated when B<--branch> is given. - -=item %t - -is replaced with the generated release tag name. - -=item %v - -is replaced with the version number. The exact version number varies -through the process of this script. - -=back - -This script uses the following defaults: - -=over 4 - -=item * Without B<--clean-worktree> - -For branches: C - -For tags: C<%t> - -=item * With B<--clean-worktree> - -For branches: C<%b> - -For tags: C<%t> - -=back - -=item B<--reviewer>=I - -Add I to the set of reviewers for the commits performed by this script. -Multiple reviewers are allowed. - -If no reviewer is given, you will have to run C manually, which -means retagging a release commit manually as well. - -=item B<--local-user>=I - -Use I as the local user for C and for signing with C. - -If not given, then the default e-mail address' key is used. - -=item B<--unsigned> - -Do not sign the tarball or announcement file. This leaves it for other -scripts to sign the files later. - -=item B<--staging-address>=I
- -The staging location that the release files are to be uploaded to. -Supported values are: - -=over 4 - -=item - - -an existing local directory - -=item - - -something that can be interpreted as an SCP/SFTP address. In this case, -SFTP will always be used. Typical SCP remote file specs will be translated -into something that makes sense for SFTP. - -=back - -The default staging address is C. - -=item B<--no-upload> - -Don't upload the release files to the staging location. - -=item B<--no-update> - -Don't run C and C. - -=item B<--quiet> - -Really quiet, only bare necessity output, which is the final instructions, -or should the B<--porcelain> option be used, only that output. - -messages appearing on standard error will still be shown, but should be -fairly minimal. - -=item B<--verbose> - -Verbose output. - -=item B<--debug> - -Display extra debug output. Implies B<--no-upload> - -=item B<--porcelain> - -Give final output in an easy-to-parse format for scripts. The output comes -in a form reminicent of shell variable assignments. Currently supported are: - -=over 4 - -=item B=I - -The directory for the clone that this script creates. This is not given when -the option B<--clean-worktree> is used. - -=item B=I - -The metadata file. See L for a description of all generated files -as well as the contents of the metadata file. - -=back - -=item B<--force> - -Force execution. Precisely, the check that the current branch is C -or a release branch is not done. - -=item B<--help> - -Display a quick help text and exit. - -=item B<--manual> - -Display this manual and exit. - -=back - -=head1 RELEASE BRANCHES AND TAGS - -Prior to OpenSSL 3.0, the release branches were named -C<< OpenSSL_I-stable >>, and the release tags were named -C<< OpenSSL_I >> for regular releases, or -C<< OpenSSL_I-preI >> for pre-releases. - -From OpenSSL 3.0 ongoing, the release branches are named -C<< openssl-I >>, and the release tags are named -C<< openssl-I >> for regular releases, or -C<< openssl-I-alphaI >> for alpha releases -and C<< openssl-I-betaI >> for beta releases. - -B recognises both forms. - -=head1 VERSION AND STATE - -With OpenSSL 3.0, all the version and state information is in the file -F, where the following variables are used and changed: - -=over 4 - -=item B, B, B - -The three part of the version number. - -=item B - -The indicator of the current state of the branch. The value may be one pf: - -=over 4 - -=item C - -This branch is "in development". This is typical for the C branch -unless there are ongoing alpha or beta releases. - -=item C<< alphaI >> or C<< alphaI-dev >> - -This branch has alpha releases going on. C<< alphaI-dev >> is what -should normally be seen in the git workspace, indicating that -C<< alphaI >> is in development. C<< alphaI >> is what should be -found in the alpha release tar file. - -=item C<< alphaI >> or C<< alphaI-dev >> - -This branch has beta releases going on. The details are otherwise exactly -as for alpha. - -=item I - -This is normally not seen in the git workspace, but should always be what's -found in the tar file of a regular release. - -=back - -=item B - -Extra build metadata to be used by anyone for their own purposes. - -=item B - -This is normally empty in the git workspace, but should always have the -release date in the tar file of any release. - -=back - -=head1 FILES - -The following files are produced and normally uploaded to the staging -address: - -=over 4 - -=item F - -The source tarball itself. - -=item F, F - -The SHA1 and SHA256 checksums for F. - -=item F - -The detached PGP signature for F. - -=item F - -The announcement text, clear signed with PGP. - -=item F - -The metadata file for F. It contains shell -variable assignments with data that may be of interest for other scripts, -such as a script to promote this release to an actual release: - -=over 4 - -=item B=I - -The update branch. This is always given. - -=item B=I - -If a staging update branch was used (because B<--clean-worktree> wasn't -given or because B<--branch-fmt> was used), it's given here. - -=item B=I - -The release branch, if it differs from the update branch (i.e. B<--branch> -was given or implied). - -=item B=I - -If a staging release branch was used (because B<--clean-worktree> wasn't -given or because B<--branch-fmt> was used), it's given here. - -=item B=I - -The release tag. This is always given. - -=item B='I' - -The space separated list of files that were or would have been uploaded -to the staging location (depending on the presence of B<--no-upload>). This -list doesn't include the metadata file itself. - -=item B='I' - -The URL of the source repository that this release was generated from. - -=back - -=back - -=head1 COPYRIGHT - -Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the Apache License 2.0 (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -L. - -=cut -### END MANUAL -EOF diff --git a/release-tools/stagerelease/__init__.py b/release-tools/stagerelease/__init__.py new file mode 100644 index 00000000..75afca6e --- /dev/null +++ b/release-tools/stagerelease/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""OpenSSL release staging. + +Replaces release-tools/stage-release.sh, its release-aux/*.sh helpers and the +release-aux/fixup-*.pl scripts. Standard library only: this runs on release +build hosts where installing packages is not always possible. +""" +from __future__ import annotations + +__version__ = "1.0.0" diff --git a/release-tools/stagerelease/__main__.py b/release-tools/stagerelease/__main__.py new file mode 100644 index 00000000..49292fa0 --- /dev/null +++ b/release-tools/stagerelease/__main__.py @@ -0,0 +1,15 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Allow `python -m stagerelease`.""" +from __future__ import annotations + +import sys + +from .cli import main + +if __name__ == "__main__": + sys.exit(main()) diff --git a/release-tools/stagerelease/build.py b/release-tools/stagerelease/build.py new file mode 100644 index 00000000..922b4500 --- /dev/null +++ b/release-tools/stagerelease/build.py @@ -0,0 +1,53 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Driving the OpenSSL build system during staging. + +Kept behind a class so the orchestration can be tested without configuring +and building OpenSSL, which is what made the shell version untestable. +""" +from __future__ import annotations + +import re +from pathlib import Path + +from .run import Runner + + +class Build: + """The `./Configure` and `make` steps a release needs.""" + + def __init__(self, runner: Runner, source_dir: Path) -> None: + self.runner = runner + self.source_dir = source_dir + + def has_make_target(self, target: str) -> bool: + """Whether the generated Makefile defines `target`. + + Not every branch has `renumber` or `update-fips-checksums`, so their + absence is normal rather than an error. + """ + makefile = self.source_dir / "Makefile" + if not makefile.is_file(): + return False + pattern = re.compile(rf"^{re.escape(target)} *:", re.MULTILINE) + return bool(pattern.search(makefile.read_text(errors="replace"))) + + def configure(self) -> None: + self.runner.run(["./Configure", "cc"], echo_output=True) + + def update(self, *, is_alpha: bool) -> None: + """Run `make update`, plus the checks a non-alpha release requires.""" + self.runner.run(["make", "update"], echo_output=True) + + # An alpha may still have symbols without assigned ordinal numbers; + # a beta or final release may not. + if not is_alpha and self.has_make_target("renumber"): + self.runner.run(["make", "renumber"], echo_output=True) + + if self.has_make_target("update-fips-checksums"): + self.runner.run(["make", "clean"], echo_output=True) + self.runner.run(["make", "update-fips-checksums"], echo_output=True) diff --git a/release-tools/stagerelease/cli.py b/release-tools/stagerelease/cli.py new file mode 100644 index 00000000..de953105 --- /dev/null +++ b/release-tools/stagerelease/cli.py @@ -0,0 +1,280 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Command line entry point.""" +from __future__ import annotations + +import argparse +import sys +from datetime import date +from pathlib import Path +from typing import Sequence + +from .errors import ReleaseError +from .git import Git +from .report import Reporter +from .run import Runner +from .stage import StageOptions, StageResult, stage_release + +#: next_method + next_method2 pairs that describe a coherent release. +VALID_METHOD_PAIRS = { + ("alpha", "alpha"), + ("alpha", "beta"), + ("beta", "beta"), + ("final", "final"), + ("", ""), + ("", "beta"), +} + +PUSH_URL = "git@github.openssl.org:openssl/openssl.git" + +MANUAL = """\ +stage-release - OpenSSL release staging + +SYNOPSIS + stage-release [--alpha | --beta | --final] [--next-beta] + [--reviewer=ID ...] [--quiet | --verbose] [--debug] + [--porcelain] + +DESCRIPTION + Stages an OpenSSL release from the current worktree. Run it from inside + an OpenSSL source checkout, with the branch to release from checked out. + It refuses to run unless that branch is master or a recognised release + branch, and unless the worktree is clean. + + If none of --alpha, --beta or --final is given, the next release is + worked out from the current state of the branch. + + Nothing is signed, pushed or uploaded. The release tag is annotated but + not signed: the signing key is held on an HSM the build host cannot + reach, so signing the tag and the tarball happens separately, where that + access exists. Shipping the artifacts is the caller's job. + +OPTIONS + --alpha, --beta + Move the branch into alpha or beta releases, or continue an ongoing + series. Both require PATCH to be zero. + + --next-beta + Use with --alpha to switch to beta releases once this one is done. + + --final + Leave the alpha or beta series and make a regular release. Only + valid when alpha or beta releases are ongoing. + + --reviewer=ID + Add ID as a reviewer of the commits this makes. May be repeated. + Without it you have to run addrev by hand afterwards, which means + re-tagging the release commit by hand as well. + + --quiet, --verbose, --debug + Control how much progress output is produced. + + --porcelain + Print the final result as shell variable assignments instead of + instructions: orig_head and metadata. + +RELEASE BRANCHES AND TAGS + Before 3.0, release branches were named OpenSSL_-stable and tags + OpenSSL_. From 3.0 on, branches are openssl- and tags + are openssl-, with -alpha or -beta for pre-releases. + Both forms are recognised. + +FILES + Written to the parent directory of the worktree: + + openssl-.tar.gz + The source tarball. + + openssl-.tar.gz.sha1, openssl-.tar.gz.sha256 + Its checksums, in the binary-mode format sha256sum -c reads. + + openssl-.dat + Metadata for later pipeline steps, as shell variable assignments: + update_branch, release_branch (only when one was created), + release_tag, release_files and source_repo. +""" + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="stage-release", + description="Stage an OpenSSL release from the current worktree.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=( + "If none of --alpha, --beta or --final is given, the next release\n" + "is worked out from the current state of the branch." + ), + ) + + step = parser.add_mutually_exclusive_group() + step.add_argument( + "--alpha", + dest="next_method", + action="store_const", + const="alpha", + help='start or continue the "alpha" pre-release series', + ) + step.add_argument( + "--beta", + dest="next_method", + action="store_const", + const="beta", + help='start or continue the "beta" pre-release series', + ) + step.add_argument( + "--final", + dest="next_method", + action="store_const", + const="final", + help='leave "alpha" or "beta" and make a final release', + ) + parser.set_defaults(next_method="") + + parser.add_argument( + "--next-beta", + action="store_true", + help="switch to beta releases afterwards; use with --alpha", + ) + parser.add_argument( + "--reviewer", + dest="reviewers", + action="append", + default=[], + metavar="ID", + help="reviewer of the commits made (repeatable)", + ) + + noise = parser.add_mutually_exclusive_group() + noise.add_argument( + "--quiet", action="store_true", help="only print the final output" + ) + noise.add_argument("--verbose", action="store_true", help="verbose output") + + parser.add_argument("--debug", action="store_true", help="include debug output") + parser.add_argument( + "--porcelain", + action="store_true", + help="print the result in an easy-to-parse form", + ) + parser.add_argument( + "--manual", action="store_true", help="print the manual and exit" + ) + return parser + + +def resolve_methods(next_method: str, next_beta: bool) -> tuple[str, str]: + """Work out the (release, post-release) pair, rejecting incoherent ones.""" + next_method2 = "beta" if next_beta else next_method + if (next_method, next_method2) not in VALID_METHOD_PAIRS: + raise ReleaseError( + f"Invalid combination of options ({next_method or 'none'}," + f" {next_method2 or 'none'})", + "--next-beta only goes with --alpha.", + ) + return next_method, next_method2 + + +def format_result(result: StageResult, porcelain: bool) -> str: + """The closing message: instructions, or parseable assignments.""" + if porcelain: + return ( + f"orig_head='{result.orig_head}'\n" + f"metadata='{result.metadata_path.name}'\n" + ) + + lines = [ + "", + "=" * 70, + "The release is done. The release artifacts and a metadata file have", + "been written to the parent directory; pushing the commits and tag and", + "shipping the artifacts are the caller's responsibility.", + "=" * 70, + "", + "The following files were generated:", + "", + ] + lines += [f" {name}" for name in result.artifacts.names()] + lines += ["", "-" * 70, ""] + + if result.created_release_branch: + lines += [ + "A release tag and a release branch have been added to the worktree,", + "and the current branch has been updated.", + "", + f" Updated branch: {result.update_branch}", + f" Release branch: {result.release_branch}", + f" Tag: {result.release_tag}", + "", + "When pushing everything to the main repository, do it like this:", + "", + f" git push {PUSH_URL} \\", + f" {result.release_branch}", + f" git push {PUSH_URL} \\", + f" {result.update_branch}", + f" git push {PUSH_URL} \\", + f" {result.release_tag}", + ] + else: + lines += [ + "A release tag has been added to the worktree, and the current branch", + "has been updated.", + "", + f" Release/update branch: {result.update_branch}", + f" Tag: {result.release_tag}", + "", + "When pushing everything to the main repository, do it like this:", + "", + f" git push {PUSH_URL} \\", + f" {result.update_branch}", + f" git push {PUSH_URL} \\", + f" {result.release_tag}", + ] + + lines += ["", "-" * 70] + return "".join(f"{line}\n" for line in lines) + + +def main(argv: Sequence[str] | None = None, *, today: date | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + + if args.manual: + print(MANUAL, end="") + return 0 + + reporter = Reporter( + quiet=args.quiet, + verbose_enabled=args.verbose, + debug_enabled=args.debug, + ) + + try: + next_method, next_method2 = resolve_methods(args.next_method, args.next_beta) + + runner = Runner(cwd=Path.cwd(), log=reporter.verbose) + git = Git(runner) + # Everything after this runs relative to the worktree root, matching + # the shell, which cd'd there implicitly by being run from it. + runner.cwd = git.toplevel() + + result = stage_release( + StageOptions( + next_method=next_method, + next_method2=next_method2, + reviewers=tuple(args.reviewers), + ), + runner=runner, + git=git, + reporter=reporter, + today=today, + ) + except ReleaseError as error: + print(str(error), file=sys.stderr) + return 1 + + reporter.out(format_result(result, args.porcelain).rstrip("\n")) + return 0 diff --git a/release-tools/stagerelease/copyright_year.py b/release-tools/stagerelease/copyright_year.py new file mode 100644 index 00000000..59208a0b --- /dev/null +++ b/release-tools/stagerelease/copyright_year.py @@ -0,0 +1,106 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Bring copyright years up to date -- a port of do-copyright-year. + +Every source file touched since the start of the current year has the end +year of its OpenSSL copyright notice extended to this year. A notice that +already names a single year becomes a range, and a range that would collapse +to one year (2026-2026) is written as that year alone. +""" +from __future__ import annotations + +import re +from dataclasses import dataclass +from datetime import date +from pathlib import Path +from typing import Callable, Protocol + +from .textutil import read_text, split_lines, write_text + +COPYRIGHT_OWNER = "The OpenSSL Project" + +_SOME_YEAR = r"[12][0-9][0-9][0-9]" +_YEAR_RANGE = re.compile(rf"({_SOME_YEAR})(-{_SOME_YEAR})?") +_REPEATED_YEAR = re.compile(rf"({_SOME_YEAR})-\1") +_COPYRIGHT = re.compile(rf"Copyright .*{_SOME_YEAR}(-{_SOME_YEAR})? .*{COPYRIGHT_OWNER}") + +#: Always refreshed, whether or not they were touched this year. +ALWAYS_CONSIDER = ("README.md", "README") + + +def update_line(line: str, this_year: int) -> str: + """Extend the copyright year range on one line, if it carries a notice.""" + if not _COPYRIGHT.search(line): + return line + line = _YEAR_RANGE.sub(lambda m: f"{m[1]}-{this_year}", line, count=1) + return _REPEATED_YEAR.sub(r"\1", line, count=1) + + +def update_text(text: str, this_year: int) -> str: + return "".join(update_line(line, this_year) for line in split_lines(text)) + + +class ChangedSince(Protocol): + """The slice of the git interface this pass needs.""" + + def changed_since(self, before: str) -> list[tuple[str, str]]: ... + def add(self, path: str) -> None: ... + + +@dataclass +class CopyrightResult: + considered: int + updated: list[str] + + +def update_copyright_years( + git: ChangedSince, + root: Path, + today: date, + log: Callable[[str], None] = lambda line: None, +) -> CopyrightResult: + """Update and stage copyright years across files touched this year. + + Returns how many files were examined and which ones changed. Files are + only written when their contents actually differ, so unchanged files keep + their mtime -- the shell achieved the same thing by rewriting a copy and + comparing before moving it back. + """ + new_year_day = f"{today.year}-01-01" + + candidates: list[str] = [path for _, path in git.changed_since(new_year_day)] + for name in ALWAYS_CONSIDER: + if (root / name).is_file(): + candidates.append(name) + + considered = 0 + updated: list[str] = [] + seen: set[str] = set() + + for relative in candidates: + if relative in seen: + continue + seen.add(relative) + + path = root / relative + if not path.is_file(): + # Directories, submodules, and paths removed since the commit + # that mentioned them. + continue + considered += 1 + + text = read_text(path) + new_text = update_text(text, today.year) + if new_text == text: + continue + + write_text(path, new_text) + git.add(relative) + updated.append(relative) + log(f"> {relative}") + + return CopyrightResult(considered=considered, updated=updated) diff --git a/release-tools/stagerelease/errors.py b/release-tools/stagerelease/errors.py new file mode 100644 index 00000000..3fe4eae3 --- /dev/null +++ b/release-tools/stagerelease/errors.py @@ -0,0 +1,28 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""The one exception type this tool raises on expected failures. + +Anything that the operator could plausibly hit -- a dirty worktree, an +impossible release transition, an unrecognised branch -- is a ReleaseError. +The CLI catches it, prints the message to stderr and exits 1, so a stack +trace only ever means a genuine bug. +""" +from __future__ import annotations + + +class ReleaseError(Exception): + """An expected, operator-facing failure.""" + + def __init__(self, message: str, hint: str | None = None) -> None: + super().__init__(message) + self.message = message + self.hint = hint + + def __str__(self) -> str: + if self.hint: + return f"{self.message}\n{self.hint}" + return self.message diff --git a/release-tools/stagerelease/fixups.py b/release-tools/stagerelease/fixups.py new file mode 100644 index 00000000..fc22a00f --- /dev/null +++ b/release-tools/stagerelease/fixups.py @@ -0,0 +1,268 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html +"""Per-file edits applied for the release and post-release commits. + +A port of the twelve `release-aux/fixup-*.pl` scripts, which were run as +`perl -pi