Radish alpha
r
rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5
Radicle web interface
Radicle
Git
Document release process
Merged rudolfs opened 3 months ago

Best reviewed commit-by-commit.

check check-visual check-unit-test check-http-client-unit-test check-radicle-httpd check-e2e check-build check-http 👉 Preview 👉 Workflow runs 👉 Branch on GitHub

8 files changed +235 -94 14162868 b0b6d166
added radicle-httpd/RELEASE.md
@@ -0,0 +1,132 @@
+
# Release Process
+

+
## Prerequisites
+

+
- Git signing configured via SSH:
+
  ```sh
+
  git config set gpg.format ssh
+
  git config set user.signingKey "key::$(rad self --ssh-key)"
+
  ```
+
- Working `podman` setup (see [Podman Setup](#podman-setup) below)
+
- Required tools: `podman`, `rad`, `sha256sum`
+

+
## Prepare Release
+

+
1. Create release branch:
+
   ```sh
+
   git checkout -b releases/<version>
+
   ```
+

+
2. Bump version in `radicle-httpd/Cargo.toml`
+

+
3. Update lockfiles:
+
   ```sh
+
   cargo build
+
   ```
+

+
4. Generate changelog:
+
   ```sh
+
   radicle-httpd/build/changelog >> radicle-httpd/CHANGELOG.md
+
   ```
+

+
5. Edit changelog and remove irrelevant entries
+

+
6. Commit release:
+
   ```sh
+
   git commit -m "httpd: Release <version>"
+
   ```
+

+
7. Push for review:
+
   ```sh
+
   git push rad HEAD:refs/patches
+
   ```
+

+
## Review & Merge
+

+
1. Wait for delegate approvals
+
2. Merge patch into `master`
+

+
## Tag Release
+

+
Create and sign the release tag:
+
```sh
+
build/tag <version>
+
```
+

+
Publish the tag to Radicle:
+
```sh
+
git push rad --tags
+
```
+

+
Request delegate approval to make the tag canonical:
+
```sh
+
rad sync
+
git push -f self <username>/tags/releases/<version>:refs/tags/releases/<version>
+
```
+

+
## Build Artifacts
+

+
Run the reproducible build (Linux x86_64 or macOS):
+
```sh
+
build/build
+
```
+

+
> **Note:** Artifacts are written to `build/artifacts/`. A tagged commit is required; versioning uses `git describe`.
+

+
## Upload Artifacts
+

+
```sh
+
SSH_LOGIN=<username> build/upload
+
```
+

+
> **Warning:** Uploading immediately publishes the release.
+

+
## Announce Release
+

+
1. Mark the previous release as resolved in Zulip `#Announcements`
+

+
2. Create a new announcement with the following template:
+

+
   **Topic:** `radicle-httpd <version>`
+

+
   **Message:**
+
   ````markdown
+
   # 👾 radicle-httpd <version>
+

+
   `radicle-httpd` <version> (<build-id>) is released.
+

+
   ## Installation
+

+
   Download binaries from:
+
   - https://files.radicle.xyz/releases/radicle-httpd/<version>
+
   - https://radicle.xyz/download
+

+
   ## Changelog
+

+
   <copy output from radicle-httpd/build/changelog>
+

+
   ## Checksums
+

+
   ```
+
   <copy output from radicle-httpd/build/checksums>
+
   ```
+

+
   :info: This release was built on <platform>. Build reproducibility across platforms is unsupported; checksums may differ if rebuilt elsewhere.
+
   ````
+

+
## Notes
+

+
> **Important:**
+
> - macOS binaries are unsigned and not notarized; download via CLI only
+
> - Build reproducibility across platforms is not guaranteed
+

+
## Podman Setup
+

+
We use `podman` for reproducible builds across machines. It's preferred over Docker because it runs without a daemon and doesn't require root access.
+

+
On first run, you may need to configure UID/GID ranges:
+

+
```sh
+
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
+
podman system migrate
+
```
deleted radicle-httpd/build/README.md
@@ -1,77 +0,0 @@
-
# Builds
-

-
Radicle uses a [reproducible build][rb] pipeline to make binary verification
-
easier and more secure.
-

-
[rb]: https://reproducible-builds.org/
-

-
This build pipeline is designed to be run on an x86_64 machine running Linux.
-
The output is a set of `.tar.xz` archives containing binaries for the supported
-
platforms and signed by the user's Radicle key.
-

-
These binaries are statically linked to be maximally portable, and designed to
-
be reproducible, byte for byte.
-

-
To run the build, simply enter the following command from the repository root:
-

-
    build/build
-

-
This will build all targets and place the output in `build/artifacts` with
-
one sub-directory per build target.
-

-
Note that it will use `git describe` to get a version number for the build.
-
You *must* have a commit tagged with a version in your history or the build
-
will fail, eg. `v1.0.0`.
-
Make sure to create a release commit where you update the `version` field in
-
the `Cargo.toml` file and tag the commit with a version e.g. `build/tag 0.21.0`.
-

-
When the build completes, the SHA-256 checksums of the artifacts are output.
-
For a given Radicle version and source tree, the same set of checksums should
-
always be output, no matter where or when the build is run. If they do not
-
match, either the build pipeline has a bug, making it non-reproducible, or one
-
of the machines is compromised.
-

-
Here's an example output for a development version of Radicle:
-

-
    b9aa75bba175e18e05df4f6b39ec097414bbf56ccdeb4a2229b557f8b8e05404  radicle-1.0.0-rc.4-3-gb299f3b5-aarch64-apple-darwin.tar.xz
-
    c7070806bf2d17a8a0d3b329e4d57b1e544b7b82cb58e2863074d96348a2ab0d  radicle-1.0.0-rc.4-3-gb299f3b5-aarch64-unknown-linux-musl.tar.xz
-
    1a8327854f16ea90491fb90e0c3291a63c4b2ab01742c8435faec7d370cacb79  radicle-1.0.0-rc.4-3-gb299f3b5-x86_64-apple-darwin.tar.xz
-
    709ac67541ff0c0c570ac22ab2de9f98320e0cc2cc9b67f1909c014a2bb5bd49  radicle-1.0.0-rc.4-3-gb299f3b5-x86_64-unknown-linux-musl.tar.xz
-

-
A script is included in `build/checksums` to output these checksums after
-
the artifacts are built.
-

-
Once you have the build artifacts and you have a user on our files.radicle.xyz
-
you can upload them with the following command.
-

-
    SSH_LOGIN=<username> build/upload
-

-
## Requirements
-

-
The following software is required for the build:
-

-
  * `podman`
-
  * `rad` (The Radicle CLI)
-
  * `sha256sum`
-

-
## macOS
-

-
macOS binaries are not signed or notarized, so they have to be downloaded via
-
the CLI to avoid issues. A copy of a small subset of the Apple SDK is included
-
here to be able to cross-compile.
-

-
## Podman
-

-
We use `podman` to make the build reproducible on any machine by controlling
-
the build environment. We prefer `podman` to `docker` because it doesn't
-
require a background process to run and can be run without root access out of
-
the box.
-

-
The first time you run `podman`, you may have to give yourself some extra UIDs
-
for `podman` to use, with:
-

-
    sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
-

-
Then update `podman` with:
-

-
    podman system migrate
modified radicle-httpd/build/build
@@ -32,9 +32,9 @@ main() {
  tempdir="$(mktemp -d)"
  gitarchive="$tempdir/heartwood-$rev.tar.gz"
  keypath="$(rad path)/keys/radicle.pub"
-
  version="$(build/version)"
+
  version="$("$(dirname "$0")/version")"
  image=radicle-build-$version
-
  rust_version="$(cat ./rust-toolchain)"
+
  rust_version="$(cat "$(dirname "$0")/../rust-toolchain")"

  if [ ! -f "$keypath" ]; then
    echo "fatal: no key found at $keypath" >&2
@@ -58,7 +58,7 @@ main() {
      --build-arg LC_ALL \
      --build-arg RADICLE_VERSION=$version \
      --build-arg GIT_HEAD=$rev \
-
      --arch $ARCH --tag $image -f ./build/Dockerfile - <$gitarchive
+
      --arch $ARCH --tag $image -f "$(dirname "$0")/Dockerfile" - <$gitarchive

    echo "Creating container (radicle-build-container).."
    podman create --ulimit=host --replace --name radicle-build-container $image
@@ -71,7 +71,7 @@ main() {
      --build-arg LC_ALL \
      --build-arg GIT_HEAD=$rev \
      --build-arg RADICLE_VERSION=$version \
-
      --arch $ARCH --tag $image -f ./build/Dockerfile - <$gitarchive
+
      --arch $ARCH --tag $image -f "$(dirname "$0")/Dockerfile" - <$gitarchive

    echo "Creating container (radicle-build-container).."
    podman --cgroup-manager=cgroupfs create --ulimit=host --replace --name radicle-build-container $image
@@ -79,11 +79,11 @@ main() {
  esac

  # Copy build artifacts to output folder.
-
  outdir=build/artifacts
+
  outdir="$(dirname "$0")/artifacts"
  mkdir -p $outdir
  podman cp --overwrite radicle-build-container:/builds/. $outdir/

-
  for target in $(cat build/TARGETS); do
+
  for target in $(cat "$(dirname "$0")/TARGETS"); do
    echo "Signing artifacts for $target.."

    filename="radicle-httpd-$version-$target.tar.xz"
@@ -112,7 +112,7 @@ main "$@"

# Show artifact checksums.
echo
-
build/checksums
+
"$(dirname "$0")/checksums"
echo

echo "Build successful."
added radicle-httpd/build/changelog
@@ -0,0 +1,86 @@
+
#!/bin/sh
+
set -e
+

+
# Version from which to generate the changelog.
+
from=""
+

+
while [ $# -gt 0 ]; do
+
  case "$1" in
+
    --from-version)
+
      if [ -z "$2" ]; then
+
        echo "error: '--from-version' requires a version number" >&2
+
        exit 1
+
      fi
+
      shift
+
      from=$(echo "$1" | sed 's/^releases\///')
+
      break
+
      ;;
+
    *)
+
      echo "error: unknown argument '$1'" >&2
+
      exit 1
+
      ;;
+
  esac
+
done
+

+
# Get version from Cargo.toml
+
version=$(grep '^version = ' "$(dirname "$0")/../Cargo.toml" | head -1 | sed 's/version = "\(.*\)"/\1/')
+

+
if [ -z "$version" ]; then
+
  echo "error: could not determine version from Cargo.toml" >&2
+
  exit 1
+
fi
+

+
# Current/latest tag or HEAD
+
current="releases/${version}"
+

+
# Check if the tag exists, otherwise use HEAD
+
if git rev-parse --verify "$current^{tag}" >/dev/null 2>&1; then
+
  use_head=false
+
else
+
  echo "warning: tag '$current' not found, using HEAD instead" >&2
+
  current="HEAD"
+
  use_head=true
+
fi
+

+
if [ -z "$from" ]; then
+
  # Try to find the previous tag
+
  if previous=$(git describe --tags --abbrev=0 --match='releases/*' "$current^" 2>/dev/null); then
+
    :  # previous tag found
+
  else
+
    # No previous tag found, use the first commit
+
    previous=$(git rev-list --max-parents=0 HEAD)
+
    echo "warning: no previous release tag found, using first commit" >&2
+
  fi
+
else
+
  previous="releases/$from"
+
fi
+

+
if [ -n "$from" ] && ! git rev-parse --verify "$previous^{tag}" >/dev/null 2>&1; then
+
  echo "error: '$from' is not a valid version, tag '$previous' not found" >&2
+
  exit 1
+
fi
+

+
echo "# radicle-httpd $version"
+
echo
+
echo "radicle-httpd $version ($(git rev-parse --short HEAD)) is released."
+
echo
+
echo "## Changelog"
+

+
range="${previous}..${current}"
+
ncommits=$(git rev-list --count "$range")
+
ncontribs=$(git log "$range" --format='%ae' | sort -u | wc -l)
+

+
echo
+
echo "This release contains $ncommits commit(s) by $ncontribs contributor(s)."
+
echo
+

+
# shellcheck disable=SC2016
+
git log --pretty=format:'* `%h` **%s** *<%ae>*' "$range"
+

+
echo
+
echo "## Checksums"
+
echo
+

+
echo '```'
+
"$(dirname "$0")/checksums"
+
echo '```'
modified radicle-httpd/build/checksums
@@ -1,2 +1,2 @@
#!/bin/sh
-
find build/artifacts -type f -name '*.sha256' -exec cat {} +
+
find "$(dirname "$0")/artifacts" -type f -name '*.sha256' -exec cat {} +
modified radicle-httpd/build/tag
@@ -7,7 +7,7 @@ if [ $# -ne 1 ]; then
fi

version="$1"
-
tag="v$version"
+
tag="releases/$version"
commit="$(git rev-parse HEAD)"
signing_key=$(git config user.signingKey)

modified radicle-httpd/build/upload
@@ -6,7 +6,7 @@ SSH_ADDRESS=${SSH_ADDRESS:-$SSH_LOGIN@files.radicle.xyz}
SSH_KEY="$(rad path)/keys/radicle"

main() {
-
  version="$(build/version)"
+
  version="$("$(dirname "$0")/version")"

  echo "Uploading Radicle $version..."

@@ -18,10 +18,10 @@ main() {
  # Create remote folder.
  ssh -i $SSH_KEY $SSH_ADDRESS mkdir -p /var/www/files.radicle.xyz/releases/radicle-httpd/$version
  # Copy files over.
-
  scp -i $SSH_KEY build/artifacts/radicle-httpd-$version* $SSH_ADDRESS:/var/www/files.radicle.xyz/releases/radicle-httpd/$version
-
  scp -i $SSH_KEY build/artifacts/radicle-httpd.json $SSH_ADDRESS:/var/www/files.radicle.xyz/releases/radicle-httpd/$version
+
  scp -i $SSH_KEY "$(dirname "$0")/artifacts/radicle-httpd-$version"* $SSH_ADDRESS:/var/www/files.radicle.xyz/releases/radicle-httpd/$version
+
  scp -i $SSH_KEY "$(dirname "$0")/artifacts/radicle-httpd.json" $SSH_ADDRESS:/var/www/files.radicle.xyz/releases/radicle-httpd/$version

-
  for target in $(cat build/TARGETS); do
+
  for target in $(cat "$(dirname "$0")/TARGETS"); do
    archive=/var/www/files.radicle.xyz/releases/radicle-httpd/$version/radicle-httpd-$version-$target.tar.xz
    symlink=/var/www/files.radicle.xyz/releases/radicle-httpd/$version/radicle-httpd-$target.tar.xz

@@ -32,7 +32,7 @@ main() {
    ssh -i $SSH_KEY $SSH_ADDRESS ln -snf $archive.sha256 $symlink.sha256
  done

-
  if git describe --exact-match --match='v*' 2>/dev/null; then
+
  if git describe --exact-match --match='releases/*' 2>/dev/null; then
    echo "Creating 'latest' symlink.."
    ssh -i $SSH_KEY $SSH_ADDRESS ln -snf /var/www/files.radicle.xyz/releases/radicle-httpd/$version /var/www/files.radicle.xyz/releases/radicle-httpd/latest
  else
modified radicle-httpd/build/version
@@ -1,9 +1,9 @@
#!/bin/sh

-
if ! version="$(git describe --match='v*' --candidates=1 2>/dev/null)"; then
+
if ! version="$(git describe --match='releases/*' --candidates=1 2>/dev/null)"; then
  echo "fatal: no version tag found by 'git describe'" >&2 ; exit 1
fi
-
# Remove `v` prefix from version.
-
version=${version#v}
+
# Remove `releases/` prefix from version.
+
version=${version#releases/}

echo $version