👷 ci(circleci): add release configuration for automated deployment
- create release.yml for CircleCI to automate Rust project releases - include commands for version checks, binary packaging, and GitHub release 🔧 chore(dependencies): update toml dependencies in Cargo.lock - adjust versions and dependencies for toml packages - ensure compatibility with spec-1.1.0 across updated dependencies Signed-off-by: Jeremiah Russell <jerry@jrussell.ie>
This commit is contained in:
722
.circleci/release.yml
Normal file
722
.circleci/release.yml
Normal file
@@ -0,0 +1,722 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
min_rust_version:
|
||||||
|
type: string
|
||||||
|
default: "1.87"
|
||||||
|
fingerprint:
|
||||||
|
type: string
|
||||||
|
default: SHA256:OkxsH8Z6Iim6WDJBaII9eTT9aaO1f3eDc6IpsgYYPVg
|
||||||
|
# Version override for crate release (used when nextsv cannot calculate)
|
||||||
|
# Set to empty string "" to use nextsv auto-detection
|
||||||
|
crate_version_override:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
# Version override for workspace/PRLOG release
|
||||||
|
# Set to empty string "" to use nextsv auto-detection
|
||||||
|
workspace_version_override:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
orbs:
|
||||||
|
toolkit: jerus-org/circleci-toolkit@4.4.2
|
||||||
|
|
||||||
|
# Commands designed for future migration to circleci-toolkit
|
||||||
|
# These extend existing toolkit patterns with backward-compatible parameters
|
||||||
|
commands:
|
||||||
|
# New command: Check if version exists on crates.io
|
||||||
|
# Used for recovery scenarios where publish succeeded but workflow failed
|
||||||
|
check_crates_io_version:
|
||||||
|
description: >
|
||||||
|
Check if a version already exists on crates.io.
|
||||||
|
Sets SKIP_PUBLISH=true if version exists, false otherwise.
|
||||||
|
Used for recovery scenarios where crates.io publish succeeded but workflow failed afterward.
|
||||||
|
parameters:
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
description: "Crate name on crates.io"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Check crates.io for << parameters.package >>
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
# Use SEMVER or NEXT_VERSION (whichever is set)
|
||||||
|
VERSION="${SEMVER:-${NEXT_VERSION:-none}}"
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to check"
|
||||||
|
echo "export SKIP_PUBLISH=false" >> "$BASH_ENV"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER_AGENT="circleci-toolkit/1.0 (https://github.com/jerus-org/circleci-toolkit)"
|
||||||
|
|
||||||
|
if curl -s -H "User-Agent: ${USER_AGENT}" "https://crates.io/api/v1/crates/<< parameters.package >>/versions" | \
|
||||||
|
jq -e ".versions[] | select(.num == \"${VERSION}\")" > /dev/null 2>&1; then
|
||||||
|
echo "Version ${VERSION} exists on crates.io - will skip publish"
|
||||||
|
echo "export SKIP_PUBLISH=true" >> "$BASH_ENV"
|
||||||
|
else
|
||||||
|
echo "Version ${VERSION} not found on crates.io - will publish"
|
||||||
|
echo "export SKIP_PUBLISH=false" >> "$BASH_ENV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# New command: Check if release tag already exists
|
||||||
|
# Used for recovery scenarios where tag was created but workflow failed
|
||||||
|
check_tag_exists:
|
||||||
|
description: >
|
||||||
|
Check if the release tag already exists.
|
||||||
|
Sets SKIP_RELEASE=true if tag exists, false otherwise.
|
||||||
|
Used for recovery scenarios where release partially succeeded.
|
||||||
|
parameters:
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
description: "Package name (used to construct tag name)"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Check if tag exists for << parameters.package >>
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
# Use SEMVER or NEXT_VERSION (whichever is set)
|
||||||
|
VERSION="${SEMVER:-${NEXT_VERSION:-none}}"
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to check"
|
||||||
|
echo "export SKIP_RELEASE=false" >> "$BASH_ENV"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAG="<< parameters.package >>-v${VERSION}"
|
||||||
|
|
||||||
|
# Fetch tags from remote
|
||||||
|
git fetch --tags
|
||||||
|
|
||||||
|
if git tag -l "$TAG" | grep -q .; then
|
||||||
|
echo "Tag ${TAG} already exists - will skip release"
|
||||||
|
echo "export SKIP_RELEASE=true" >> "$BASH_ENV"
|
||||||
|
else
|
||||||
|
echo "Tag ${TAG} not found - will proceed with release"
|
||||||
|
echo "export SKIP_RELEASE=false" >> "$BASH_ENV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enhanced make_cargo_release with conditional publish support
|
||||||
|
# Backward compatible: publishes by default unless SKIP_PUBLISH=true or publish=false
|
||||||
|
# Also respects SKIP_RELEASE=true to skip entirely when tag already exists
|
||||||
|
make_cargo_release:
|
||||||
|
description: >
|
||||||
|
Make a release using cargo release.
|
||||||
|
Enhanced version that respects SKIP_PUBLISH environment variable for recovery scenarios.
|
||||||
|
When SKIP_PUBLISH=true, adds --no-publish flag to skip crates.io publish.
|
||||||
|
The publish parameter controls default behavior; SKIP_PUBLISH overrides it at runtime.
|
||||||
|
parameters:
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: "Package to release"
|
||||||
|
verbosity:
|
||||||
|
type: string
|
||||||
|
default: "-vv"
|
||||||
|
description: "Verbosity for cargo release"
|
||||||
|
publish:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
description: "If true, the release will be published to crates.io"
|
||||||
|
no_push:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: "Whether cargo release should push the changes"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: List changes using cargo release
|
||||||
|
command: |
|
||||||
|
set -exo pipefail
|
||||||
|
cargo release changes
|
||||||
|
- run:
|
||||||
|
name: Execute cargo release
|
||||||
|
command: |
|
||||||
|
set -exo pipefail
|
||||||
|
|
||||||
|
# Check if release should be skipped (tag already exists)
|
||||||
|
if [ "$SKIP_RELEASE" = "true" ]; then
|
||||||
|
echo "Skipping release (tag already exists)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use SEMVER or NEXT_VERSION
|
||||||
|
VERSION="${SEMVER:-${NEXT_VERSION:-none}}"
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to release - skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Releasing version: $VERSION"
|
||||||
|
|
||||||
|
# Build cargo release arguments
|
||||||
|
release_args="--execute --no-confirm --sign-tag"
|
||||||
|
|
||||||
|
if [ "<< parameters.package >>" != "" ]; then
|
||||||
|
release_args="$release_args --package << parameters.package >>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "<< parameters.no_push >>" = "true" ]; then
|
||||||
|
release_args="$release_args --no-push"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle publish: parameter controls default, SKIP_PUBLISH overrides at runtime
|
||||||
|
if [ "<< parameters.publish >>" = "false" ]; then
|
||||||
|
release_args="$release_args --no-publish"
|
||||||
|
echo "Publishing disabled by parameter"
|
||||||
|
elif [ "$SKIP_PUBLISH" = "true" ]; then
|
||||||
|
release_args="$release_args --no-publish"
|
||||||
|
echo "Skipping publish (version already on crates.io)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Map verbosity
|
||||||
|
case "<< parameters.verbosity >>" in
|
||||||
|
"-vvv"|"-vvvv")
|
||||||
|
release_args="-vv $release_args"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cargo release $release_args "$VERSION"
|
||||||
|
|
||||||
|
# Enhanced make_github_release with package support
|
||||||
|
make_github_release:
|
||||||
|
description: >
|
||||||
|
Create a GitHub release using the pcu utility.
|
||||||
|
When package is provided, uses 'pcu release package' which derives the correct tag prefix.
|
||||||
|
When package is empty, uses 'pcu release version' with the specified prefix.
|
||||||
|
parameters:
|
||||||
|
prefix:
|
||||||
|
type: string
|
||||||
|
default: "v"
|
||||||
|
description: "Tag prefix for the release (used when package is empty)"
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: "Package name - when provided, derives tag prefix automatically"
|
||||||
|
verbosity:
|
||||||
|
type: string
|
||||||
|
default: "-vv"
|
||||||
|
description: "Verbosity for pcu command"
|
||||||
|
update_prlog:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: "Update PRLOG when creating the release"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Create GitHub release
|
||||||
|
command: |
|
||||||
|
set -exo pipefail
|
||||||
|
|
||||||
|
# Use SEMVER or NEXT_VERSION
|
||||||
|
VERSION="${SEMVER:-${NEXT_VERSION:-none}}"
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to release - skipping GitHub release"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine the tag name
|
||||||
|
if [ "<< parameters.package >>" != "" ]; then
|
||||||
|
TAG="<< parameters.package >>-v${VERSION}"
|
||||||
|
else
|
||||||
|
TAG="<< parameters.prefix >>${VERSION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if GitHub release already exists using API
|
||||||
|
# Extract owner/repo from git remote
|
||||||
|
REPO_URL=$(git remote get-url origin)
|
||||||
|
REPO_PATH=$(echo "$REPO_URL" | sed -E 's|.*github\.com[:/]||' | sed 's|\.git$||')
|
||||||
|
|
||||||
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
"https://api.github.com/repos/${REPO_PATH}/releases/tags/${TAG}")
|
||||||
|
|
||||||
|
if [ "$HTTP_STATUS" = "200" ]; then
|
||||||
|
echo "GitHub release ${TAG} already exists - skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "GitHub release ${TAG} not found (HTTP ${HTTP_STATUS}) - will create"
|
||||||
|
|
||||||
|
pcu_args="<< parameters.verbosity >> release"
|
||||||
|
|
||||||
|
if [ "<< parameters.package >>" != "" ]; then
|
||||||
|
pcu_args="$pcu_args package << parameters.package >>"
|
||||||
|
else
|
||||||
|
pcu_args="$pcu_args version --prefix << parameters.prefix >>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "<< parameters.update_prlog >>" = "true" ]; then
|
||||||
|
pcu_args="$pcu_args --update-prlog"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pcu $pcu_args
|
||||||
|
|
||||||
|
# Build a release binary using cargo.
|
||||||
|
# Requires VERSION environment variable (skips if "none").
|
||||||
|
build_release_binary:
|
||||||
|
description: >
|
||||||
|
Build a release-optimised binary using cargo build --release.
|
||||||
|
Expects VERSION to be set in BASH_ENV; skips when VERSION is "none".
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Build release binary
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to release - skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# Package a binary from target/release into a .tar.gz archive.
|
||||||
|
# Sets ASSET_NAME in BASH_ENV for use by upload_release_asset.
|
||||||
|
package_binary:
|
||||||
|
description: >
|
||||||
|
Package a binary from target/release into a .tar.gz archive named
|
||||||
|
<binary_name>-<target>.tar.gz. Sets ASSET_NAME in BASH_ENV.
|
||||||
|
parameters:
|
||||||
|
binary_name:
|
||||||
|
type: string
|
||||||
|
description: "Name of the binary in target/release"
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
default: x86_64-unknown-linux-gnu
|
||||||
|
description: "Rust target triple for the archive name"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Package binary as tar.gz
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to release - skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_NAME="<< parameters.binary_name >>-<< parameters.target >>.tar.gz"
|
||||||
|
|
||||||
|
tar czf "$ASSET_NAME" -C target/release "<< parameters.binary_name >>"
|
||||||
|
echo "Created $ASSET_NAME ($(du -h "$ASSET_NAME" | cut -f1))"
|
||||||
|
|
||||||
|
echo "export ASSET_NAME=$ASSET_NAME" >> "$BASH_ENV"
|
||||||
|
|
||||||
|
# Upload a binary asset to a GitHub release.
|
||||||
|
# Adapted from toolkit/upload_release_asset with release_tag parameter
|
||||||
|
# so it works in pipelines where CIRCLE_TAG is not set.
|
||||||
|
# TODO: Replace with toolkit/upload_release_asset once it accepts a
|
||||||
|
# release_tag parameter (circleci-toolkit#333).
|
||||||
|
upload_release_asset:
|
||||||
|
description: >
|
||||||
|
Upload a binary asset to a GitHub release. Accepts a release_tag
|
||||||
|
parameter instead of relying on CIRCLE_TAG, making it usable in
|
||||||
|
scheduled/manual pipelines. Requires GITHUB_TOKEN environment variable.
|
||||||
|
parameters:
|
||||||
|
asset_path:
|
||||||
|
type: string
|
||||||
|
description: "Path to the asset file to upload"
|
||||||
|
asset_name:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: "Name for the asset in the release (defaults to filename)"
|
||||||
|
release_tag:
|
||||||
|
type: string
|
||||||
|
description: "Git tag identifying the GitHub release"
|
||||||
|
github_token_var:
|
||||||
|
type: env_var_name
|
||||||
|
default: GITHUB_TOKEN
|
||||||
|
description: "Environment variable containing the GitHub token"
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Upload asset to GitHub release
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No version to release - skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_PATH="<< parameters.asset_path >>"
|
||||||
|
ASSET_NAME="<< parameters.asset_name >>"
|
||||||
|
TOKEN="${<< parameters.github_token_var >>}"
|
||||||
|
TAG="<< parameters.release_tag >>"
|
||||||
|
|
||||||
|
if [ -z "${ASSET_NAME}" ]; then
|
||||||
|
ASSET_NAME="$(basename "${ASSET_PATH}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${ASSET_PATH}" ]; then
|
||||||
|
echo "ERROR: Asset file not found: ${ASSET_PATH}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Derive repo slug from git remote
|
||||||
|
REPO_URL=$(git remote get-url origin)
|
||||||
|
REPO_SLUG=$(echo "$REPO_URL" | sed -E 's|.*github\.com[:/]||' | sed 's|\.git$||')
|
||||||
|
echo "Repository: ${REPO_SLUG}"
|
||||||
|
echo "Looking up release for tag ${TAG}..."
|
||||||
|
|
||||||
|
RELEASE_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"https://api.github.com/repos/${REPO_SLUG}/releases/tags/${TAG}")
|
||||||
|
|
||||||
|
HTTP_CODE=$(echo "${RELEASE_RESPONSE}" | tail -1)
|
||||||
|
RELEASE_BODY=$(echo "${RELEASE_RESPONSE}" | sed '$d')
|
||||||
|
|
||||||
|
if [ "${HTTP_CODE}" != "200" ]; then
|
||||||
|
echo "ERROR: GitHub API returned HTTP ${HTTP_CODE}" >&2
|
||||||
|
echo "${RELEASE_BODY}" | jq -r '.message // .' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RELEASE_ID=$(echo "${RELEASE_BODY}" | jq -r '.id')
|
||||||
|
|
||||||
|
if [ -z "${RELEASE_ID}" ] || [ "${RELEASE_ID}" = "null" ]; then
|
||||||
|
echo "ERROR: Could not find GitHub release for tag ${TAG}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Uploading ${ASSET_NAME} to release ${RELEASE_ID}..."
|
||||||
|
UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||||
|
-X POST \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
"https://uploads.github.com/repos/${REPO_SLUG}/releases/${RELEASE_ID}/assets?name=${ASSET_NAME}" \
|
||||||
|
--data-binary "@${ASSET_PATH}")
|
||||||
|
|
||||||
|
HTTP_CODE=$(echo "${UPLOAD_RESPONSE}" | tail -1)
|
||||||
|
UPLOAD_BODY=$(echo "${UPLOAD_RESPONSE}" | sed '$d')
|
||||||
|
|
||||||
|
if [ "${HTTP_CODE}" != "201" ]; then
|
||||||
|
echo "ERROR: Upload failed with HTTP ${HTTP_CODE}" >&2
|
||||||
|
echo "${UPLOAD_BODY}" | jq -r '.message // .' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Upload complete: ${ASSET_NAME}"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tools:
|
||||||
|
executor:
|
||||||
|
name: toolkit/rust_env_rolling
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Verify tools
|
||||||
|
command: |
|
||||||
|
set -ex
|
||||||
|
nextsv --version
|
||||||
|
pcu --version
|
||||||
|
cargo release --version
|
||||||
|
jq --version
|
||||||
|
|
||||||
|
# Calculate and display versions for approval
|
||||||
|
# Persists calculated versions to workspace for downstream jobs
|
||||||
|
calculate-versions:
|
||||||
|
parameters:
|
||||||
|
crate_version:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: "Override version for crate release (empty = auto-detect)"
|
||||||
|
workspace_version:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
description: "Override version for workspace/PRLOG release (empty = auto-detect)"
|
||||||
|
executor:
|
||||||
|
name: toolkit/rust_env_rolling
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Calculate release versions
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
echo "=============================================="
|
||||||
|
echo " RELEASE VERSION CALCULATION"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for version override (passed from workflow)
|
||||||
|
VERSION_OVERRIDE="<< parameters.crate_version >>"
|
||||||
|
|
||||||
|
# Calculate crate version
|
||||||
|
echo "--- Crate: cull-gmail ---"
|
||||||
|
if [ -n "$VERSION_OVERRIDE" ]; then
|
||||||
|
CRATE_VERSION="$VERSION_OVERRIDE"
|
||||||
|
echo "Result: Will release version $CRATE_VERSION (OVERRIDE)"
|
||||||
|
echo "Note: Version explicitly set via crate_version parameter"
|
||||||
|
NEXTSV_VERSION=$(nextsv -bn calculate --package cull-gmail 2>/dev/null || echo "none")
|
||||||
|
echo " (nextsv would have calculated: $NEXTSV_VERSION)"
|
||||||
|
else
|
||||||
|
CRATE_VERSION=$(nextsv -bn calculate --package cull-gmail 2>/dev/null || echo "")
|
||||||
|
if [ -z "$CRATE_VERSION" ]; then
|
||||||
|
CRATE_VERSION="none"
|
||||||
|
echo "Result: No crate release needed"
|
||||||
|
echo "Reason: No changes to crates/cull-gmail/ or its dependencies"
|
||||||
|
else
|
||||||
|
echo "Result: Will release version $CRATE_VERSION"
|
||||||
|
echo "Changes detected in crate scope (code, deps, or Cargo.lock)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Calculate workspace version
|
||||||
|
echo "--- Workspace (PRLOG) ---"
|
||||||
|
WORKSPACE_OVERRIDE="<< parameters.workspace_version >>"
|
||||||
|
if [ -n "$WORKSPACE_OVERRIDE" ]; then
|
||||||
|
WORKSPACE_VERSION="$WORKSPACE_OVERRIDE"
|
||||||
|
echo "Result: Will release version $WORKSPACE_VERSION (OVERRIDE)"
|
||||||
|
echo "Note: Version explicitly set via workspace_version parameter"
|
||||||
|
NEXTSV_WS=$(nextsv -bn calculate --prefix "v" 2>/dev/null || echo "none")
|
||||||
|
echo " (nextsv would have calculated: $NEXTSV_WS)"
|
||||||
|
else
|
||||||
|
WORKSPACE_VERSION=$(nextsv -bn calculate --prefix "v" 2>/dev/null || echo "")
|
||||||
|
if [ -z "$WORKSPACE_VERSION" ]; then
|
||||||
|
WORKSPACE_VERSION="none"
|
||||||
|
echo "Result: No workspace release needed"
|
||||||
|
echo "Reason: No changes since last v* tag"
|
||||||
|
else
|
||||||
|
echo "Result: Will release version $WORKSPACE_VERSION"
|
||||||
|
echo "Changes detected in workspace scope"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=============================================="
|
||||||
|
echo " SUMMARY"
|
||||||
|
echo "=============================================="
|
||||||
|
echo "Crate (cull-gmail): $CRATE_VERSION"
|
||||||
|
echo "Workspace (PRLOG): $WORKSPACE_VERSION"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Validation rules
|
||||||
|
echo "--- Validation ---"
|
||||||
|
if [ "$CRATE_VERSION" != "none" ] && [ "$WORKSPACE_VERSION" = "none" ]; then
|
||||||
|
echo "WARNING: Crate release without workspace release is unusual"
|
||||||
|
echo " Workspace should increment when crate changes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CRATE_VERSION" = "none" ] && [ "$WORKSPACE_VERSION" = "none" ]; then
|
||||||
|
echo "INFO: No releases needed - workflow will skip release steps"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Please review the versions above and approve to proceed."
|
||||||
|
|
||||||
|
# Persist versions to workspace for downstream jobs
|
||||||
|
mkdir -p /tmp/release-versions
|
||||||
|
echo "$CRATE_VERSION" > /tmp/release-versions/crate-version
|
||||||
|
echo "$WORKSPACE_VERSION" > /tmp/release-versions/workspace-version
|
||||||
|
echo "Versions persisted to workspace for release jobs"
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp
|
||||||
|
paths:
|
||||||
|
- release-versions
|
||||||
|
|
||||||
|
# Release a single crate
|
||||||
|
# Reads version from workspace (calculated by calculate-versions job)
|
||||||
|
release-crate:
|
||||||
|
parameters:
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
executor:
|
||||||
|
name: toolkit/rust_env_rolling
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp
|
||||||
|
- add_ssh_keys:
|
||||||
|
fingerprints:
|
||||||
|
- << pipeline.parameters.fingerprint >>
|
||||||
|
- run:
|
||||||
|
name: Remove original SSH key from agent
|
||||||
|
command: |
|
||||||
|
ssh-add -l
|
||||||
|
# GitHub App integration doesn't create id_rsa.pub, handle gracefully
|
||||||
|
if [ -f ~/.ssh/id_rsa.pub ]; then
|
||||||
|
ssh-add -d ~/.ssh/id_rsa.pub
|
||||||
|
else
|
||||||
|
echo "No id_rsa.pub found (GitHub App integration) - skipping removal"
|
||||||
|
fi
|
||||||
|
ssh-add -l
|
||||||
|
- toolkit/gpg_key
|
||||||
|
- toolkit/git_config
|
||||||
|
# Step 1: Load version from workspace (calculated by calculate-versions)
|
||||||
|
- run:
|
||||||
|
name: Load version from workspace
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
VERSION_FILE="/tmp/release-versions/crate-version"
|
||||||
|
if [ -f "$VERSION_FILE" ]; then
|
||||||
|
VERSION=$(cat "$VERSION_FILE")
|
||||||
|
echo "Loaded version from workspace: $VERSION"
|
||||||
|
echo "export NEXT_VERSION=$VERSION" >> "$BASH_ENV"
|
||||||
|
echo "export SEMVER=$VERSION" >> "$BASH_ENV"
|
||||||
|
else
|
||||||
|
echo "ERROR: Version file not found at $VERSION_FILE"
|
||||||
|
echo "This job requires calculate-versions to run first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Step 2: Check crates.io for recovery scenarios
|
||||||
|
- check_crates_io_version:
|
||||||
|
package: << parameters.package >>
|
||||||
|
# Step 3: Check if tag already exists for recovery scenarios
|
||||||
|
- check_tag_exists:
|
||||||
|
package: << parameters.package >>
|
||||||
|
# Step 4: Run cargo release (respects SKIP_PUBLISH and SKIP_RELEASE)
|
||||||
|
- make_cargo_release:
|
||||||
|
package: << parameters.package >>
|
||||||
|
verbosity: "-vv"
|
||||||
|
# Step 5: Update pcu to latest version (command not yet in toolkit release)
|
||||||
|
- run:
|
||||||
|
name: Update to latest pcu
|
||||||
|
command: |
|
||||||
|
cargo install --force --git https://github.com/jerus-org/pcu --branch main
|
||||||
|
# Step 6: Create GitHub release
|
||||||
|
- make_github_release:
|
||||||
|
package: << parameters.package >>
|
||||||
|
verbosity: "-vv"
|
||||||
|
|
||||||
|
# Release PRLOG/workspace
|
||||||
|
# Reads version from workspace (calculated by calculate-versions job)
|
||||||
|
release-prlog:
|
||||||
|
executor:
|
||||||
|
name: toolkit/rust_env_rolling
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp
|
||||||
|
- add_ssh_keys:
|
||||||
|
fingerprints:
|
||||||
|
- << pipeline.parameters.fingerprint >>
|
||||||
|
- run:
|
||||||
|
name: Remove original SSH key from agent
|
||||||
|
command: |
|
||||||
|
ssh-add -l
|
||||||
|
# GitHub App integration doesn't create id_rsa.pub, handle gracefully
|
||||||
|
if [ -f ~/.ssh/id_rsa.pub ]; then
|
||||||
|
ssh-add -d ~/.ssh/id_rsa.pub
|
||||||
|
else
|
||||||
|
echo "No id_rsa.pub found (GitHub App integration) - skipping removal"
|
||||||
|
fi
|
||||||
|
ssh-add -l
|
||||||
|
- toolkit/gpg_key
|
||||||
|
- toolkit/git_config
|
||||||
|
- run:
|
||||||
|
name: Release PRLOG
|
||||||
|
command: |
|
||||||
|
set -exo pipefail
|
||||||
|
chmod +x scripts/*.sh
|
||||||
|
|
||||||
|
# Load version from workspace (calculated by calculate-versions)
|
||||||
|
VERSION_FILE="/tmp/release-versions/workspace-version"
|
||||||
|
if [ -f "$VERSION_FILE" ]; then
|
||||||
|
VERSION=$(cat "$VERSION_FILE")
|
||||||
|
echo "Loaded workspace version from workspace: $VERSION"
|
||||||
|
else
|
||||||
|
echo "ERROR: Version file not found at $VERSION_FILE"
|
||||||
|
echo "This job requires calculate-versions to run first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$VERSION" = "none" ]; then
|
||||||
|
echo "No PRLOG release needed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if tag already exists
|
||||||
|
TAG="v${VERSION}"
|
||||||
|
git fetch --tags origin main
|
||||||
|
|
||||||
|
# Pull latest main (release-crate may have pushed commits)
|
||||||
|
git pull --rebase origin main
|
||||||
|
|
||||||
|
if git tag -l "$TAG" | grep -q .; then
|
||||||
|
echo "Tag $TAG already exists - updating PRLOG without new tag"
|
||||||
|
# Update PRLOG.md with the release date but don't create new tag
|
||||||
|
./scripts/release-prlog.sh "$VERSION" --no-tag 2>/dev/null || \
|
||||||
|
./scripts/release-prlog.sh "$VERSION"
|
||||||
|
git push origin main || echo "No changes to push"
|
||||||
|
else
|
||||||
|
echo "Creating new release for version $VERSION"
|
||||||
|
./scripts/release-prlog.sh "$VERSION"
|
||||||
|
git push origin main --tags
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build release binary and upload to GitHub release for cargo-binstall
|
||||||
|
release-binary:
|
||||||
|
executor:
|
||||||
|
name: toolkit/rust_env_rolling
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp
|
||||||
|
- run:
|
||||||
|
name: Load version from workspace
|
||||||
|
command: |
|
||||||
|
set -eo pipefail
|
||||||
|
VERSION_FILE="/tmp/release-versions/crate-version"
|
||||||
|
if [ -f "$VERSION_FILE" ]; then
|
||||||
|
VERSION=$(cat "$VERSION_FILE")
|
||||||
|
echo "Loaded version from workspace: $VERSION"
|
||||||
|
echo "export VERSION=$VERSION" >> "$BASH_ENV"
|
||||||
|
else
|
||||||
|
echo "ERROR: Version file not found at $VERSION_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- build_release_binary
|
||||||
|
- package_binary:
|
||||||
|
binary_name: cull-gmail
|
||||||
|
- upload_release_asset:
|
||||||
|
asset_path: cull-gmail-x86_64-unknown-linux-gnu.tar.gz
|
||||||
|
release_tag: cull-gmail-v${VERSION}
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
release:
|
||||||
|
jobs:
|
||||||
|
- tools
|
||||||
|
|
||||||
|
# Calculate and display versions for review
|
||||||
|
# Pass pipeline parameters to job for version overrides
|
||||||
|
- calculate-versions:
|
||||||
|
requires: [tools]
|
||||||
|
crate_version: << pipeline.parameters.crate_version_override >>
|
||||||
|
workspace_version: << pipeline.parameters.workspace_version_override >>
|
||||||
|
|
||||||
|
# Manual approval gate - review calculated versions before release
|
||||||
|
- approve-release:
|
||||||
|
type: approval
|
||||||
|
requires: [calculate-versions]
|
||||||
|
|
||||||
|
# Release cull-gmail crate
|
||||||
|
# Version is read from workspace (set by calculate-versions)
|
||||||
|
# Pipeline parameter crate_version_override controls the override
|
||||||
|
# Set parameter to "" to resume nextsv auto-detection
|
||||||
|
- release-crate:
|
||||||
|
name: release-cull-gmail
|
||||||
|
requires: [approve-release]
|
||||||
|
package: cull-gmail
|
||||||
|
context:
|
||||||
|
- release
|
||||||
|
- bot-check
|
||||||
|
|
||||||
|
# Build and upload release binary for cargo-binstall
|
||||||
|
- release-binary:
|
||||||
|
requires: [release-cull-gmail]
|
||||||
|
context:
|
||||||
|
- release
|
||||||
|
- bot-check
|
||||||
|
|
||||||
|
# Release PRLOG (after crate released)
|
||||||
|
- release-prlog:
|
||||||
|
requires: [release-cull-gmail]
|
||||||
|
context:
|
||||||
|
- release
|
||||||
|
- bot-check
|
||||||
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -345,7 +345,7 @@ dependencies = [
|
|||||||
"pathdiff",
|
"pathdiff",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"toml",
|
"toml 0.9.12+spec-1.1.0",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"toml",
|
"toml 1.0.1+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2207,11 +2207,24 @@ name = "toml"
|
|||||||
version = "0.9.12+spec-1.1.0"
|
version = "0.9.12+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime 0.7.5+spec-1.1.0",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "1.0.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.13.0",
|
"indexmap 2.13.0",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime 1.0.0+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"toml_writer",
|
"toml_writer",
|
||||||
"winnow",
|
"winnow",
|
||||||
@@ -2227,10 +2240,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_datetime"
|
||||||
version = "1.0.7+spec-1.1.0"
|
version = "1.0.0+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1"
|
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.0.8+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user