178 lines
5.7 KiB
Bash
178 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# release.sh — disinto_release() function
|
|
#
|
|
# Handles vault TOML creation, branch setup on ops repo, PR creation,
|
|
# and auto-merge request for a versioned release.
|
|
#
|
|
# Globals expected:
|
|
# FORGE_URL - Forge instance URL (e.g. http://localhost:3000)
|
|
# FORGE_TOKEN - API token for Forge operations
|
|
# FORGE_OPS_REPO - Ops repo slug (e.g. disinto-admin/myproject-ops)
|
|
# FACTORY_ROOT - Root of the disinto factory
|
|
# PRIMARY_BRANCH - Primary branch name (e.g. main)
|
|
#
|
|
# Usage:
|
|
# source "${FACTORY_ROOT}/lib/release.sh"
|
|
# disinto_release <version>
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
# Source vault.sh for _vault_log helper
|
|
source "${FACTORY_ROOT}/lib/vault.sh"
|
|
|
|
# Assert required globals are set before using this module.
|
|
_assert_release_globals() {
|
|
local missing=()
|
|
[ -z "${FORGE_URL:-}" ] && missing+=("FORGE_URL")
|
|
[ -z "${FORGE_TOKEN:-}" ] && missing+=("FORGE_TOKEN")
|
|
[ -z "${FORGE_OPS_REPO:-}" ] && missing+=("FORGE_OPS_REPO")
|
|
[ -z "${FACTORY_ROOT:-}" ] && missing+=("FACTORY_ROOT")
|
|
[ -z "${PRIMARY_BRANCH:-}" ] && missing+=("PRIMARY_BRANCH")
|
|
if [ "${#missing[@]}" -gt 0 ]; then
|
|
echo "Error: release.sh requires these globals to be set: ${missing[*]}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
disinto_release() {
|
|
_assert_release_globals
|
|
|
|
local version="${1:-}"
|
|
local formula_path="${FACTORY_ROOT}/formulas/release.toml"
|
|
|
|
if [ -z "$version" ]; then
|
|
echo "Error: version required" >&2
|
|
echo "Usage: disinto release <version>" >&2
|
|
echo "Example: disinto release v1.2.0" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Validate version format (must start with 'v' followed by semver)
|
|
if ! echo "$version" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
echo "Error: version must be in format v1.2.3 (semver with 'v' prefix)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Load project config to get FORGE_OPS_REPO
|
|
if [ -z "${PROJECT_NAME:-}" ]; then
|
|
# PROJECT_NAME is unset - detect project TOML from projects/ directory
|
|
local found_toml
|
|
found_toml=$(find "${FACTORY_ROOT}/projects" -maxdepth 1 -name "*.toml" ! -name "*.example" 2>/dev/null | head -1)
|
|
if [ -n "$found_toml" ]; then
|
|
source "${FACTORY_ROOT}/lib/load-project.sh" "$found_toml"
|
|
fi
|
|
else
|
|
local project_toml="${FACTORY_ROOT}/projects/${PROJECT_NAME}.toml"
|
|
if [ -f "$project_toml" ]; then
|
|
source "${FACTORY_ROOT}/lib/load-project.sh" "$project_toml"
|
|
fi
|
|
fi
|
|
|
|
# Check formula exists
|
|
if [ ! -f "$formula_path" ]; then
|
|
echo "Error: release formula not found at ${formula_path}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Get the ops repo root
|
|
local ops_root="${FACTORY_ROOT}/../disinto-ops"
|
|
if [ ! -d "${ops_root}/.git" ]; then
|
|
echo "Error: ops repo not found at ${ops_root}" >&2
|
|
echo " Run 'disinto init' to set up the ops repo first" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Generate a unique ID for the vault item
|
|
local id="release-${version//./}"
|
|
local vault_toml="${ops_root}/vault/actions/${id}.toml"
|
|
|
|
# Create vault TOML with the specific version
|
|
cat > "$vault_toml" <<EOF
|
|
# vault/actions/${id}.toml
|
|
# Release vault item for ${version}
|
|
# Auto-generated by disinto release
|
|
|
|
id = "${id}"
|
|
formula = "release"
|
|
context = "Release ${version}"
|
|
secrets = []
|
|
EOF
|
|
|
|
echo "Created vault item: ${vault_toml}"
|
|
|
|
# Create a PR to submit the vault item to the ops repo
|
|
local branch_name="release/${version//./}"
|
|
local pr_title="release: ${version}"
|
|
local pr_body="Release ${version}
|
|
|
|
This PR creates a vault item for the release of version ${version}.
|
|
|
|
## Changes
|
|
- Added vault item: ${id}.toml
|
|
|
|
## Next Steps
|
|
1. Review this PR
|
|
2. Approve and merge
|
|
3. The vault runner will execute the release formula
|
|
"
|
|
|
|
# Create branch from clean primary branch
|
|
(
|
|
cd "$ops_root"
|
|
git checkout "$PRIMARY_BRANCH"
|
|
git pull origin "$PRIMARY_BRANCH"
|
|
git checkout -B "$branch_name" "$PRIMARY_BRANCH"
|
|
|
|
# Add and commit only the vault TOML file
|
|
git add "vault/actions/${id}.toml"
|
|
git commit -m "$pr_title" -m "$pr_body" 2>/dev/null || true
|
|
|
|
# Push branch
|
|
git push -u origin "$branch_name" 2>/dev/null || {
|
|
echo "Error: failed to push branch" >&2
|
|
exit 1
|
|
}
|
|
)
|
|
|
|
# Create PR
|
|
local pr_response
|
|
pr_response=$(curl -sf -X POST \
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"${FORGE_URL}/api/v1/repos/${FORGE_OPS_REPO}/pulls" \
|
|
-d "{\"title\":\"${pr_title}\",\"head\":\"${branch_name}\",\"base\":\"${PRIMARY_BRANCH}\",\"body\":\"$(echo "$pr_body" | sed ':a;N;$!ba;s/\n/\\n/g')\"}" 2>/dev/null) || {
|
|
echo "Error: failed to create PR" >&2
|
|
echo "Response: ${pr_response}" >&2
|
|
exit 1
|
|
}
|
|
|
|
local pr_number
|
|
pr_number=$(echo "$pr_response" | jq -r '.number')
|
|
|
|
local pr_url="${FORGE_URL}/${FORGE_OPS_REPO}/pulls/${pr_number}"
|
|
|
|
# Enable auto-merge on the PR — Forgejo will auto-merge after approval
|
|
_vault_log "Enabling auto-merge for PR #${pr_number}"
|
|
curl -sf -X POST \
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"${FORGE_URL}/api/v1/repos/${FORGE_OPS_REPO}/pulls/${pr_number}/merge" \
|
|
-d '{"Do":"merge","merge_when_checks_succeed":true}' >/dev/null 2>&1 || {
|
|
echo "Warning: failed to enable auto-merge (may already be enabled or not supported)" >&2
|
|
}
|
|
|
|
echo ""
|
|
echo "Release PR created: ${pr_url}"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Review the PR"
|
|
echo " 2. Approve the PR (auto-merge will trigger after approval)"
|
|
echo " 3. The vault runner will execute the release formula"
|
|
echo ""
|
|
echo "After merge, the release will:"
|
|
echo " 1. Tag Forgejo main with ${version}"
|
|
echo " 2. Push tag to mirrors (Codeberg, GitHub)"
|
|
echo " 3. Build and tag the agents Docker image"
|
|
echo " 4. Restart agent containers"
|
|
}
|