#!/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 # ============================================================================= 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 " >&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" </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" }