fix: feat: versioned releases — vault-gated tag, image build, and deploy (#112)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
Agent 2026-04-01 13:51:19 +00:00
parent 0455040d02
commit a7ad6eb32a
4 changed files with 406 additions and 5 deletions

View file

@ -40,6 +40,7 @@ Usage:
disinto status Show factory status
disinto secrets <subcommand> Manage encrypted secrets
disinto run <action-id> Run action in ephemeral runner container
disinto release <version> Create vault PR for release (e.g., v1.2.0)
disinto hire-an-agent <agent-name> <role> [--formula <path>]
Hire a new agent (create user + .profile repo)
@ -232,7 +233,6 @@ services:
volumes:
- agent-data:/home/agent/data
- project-repos:/home/agent/repos
- ./:/home/agent/disinto:ro
- ${HOME}/.claude:/home/agent/.claude
- ${HOME}/.claude.json:/home/agent/.claude.json:ro
- CLAUDE_BIN_PLACEHOLDER:/usr/local/bin/claude:ro
@ -261,9 +261,7 @@ services:
security_opt:
- apparmor=unconfined
volumes:
- ./vault:/home/agent/disinto/vault
- ./lib:/home/agent/disinto/lib:ro
- ./formulas:/home/agent/disinto/formulas:ro
- agent-data:/home/agent/data
environment:
FORGE_URL: http://forgejo:3000
DISINTO_CONTAINER: "1"
@ -2613,6 +2611,125 @@ EOF
echo " Formula: ${role}.toml"
}
# ── release command ───────────────────────────────────────────────────────────
#
# Creates a vault PR for the release. This is a convenience wrapper that
# creates the vault item TOML and submits it as a PR to the ops repo.
#
# Usage: disinto release <version>
# Example: disinto release v1.2.0
disinto_release() {
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
# 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/pending/${id}.toml"
# Create vault TOML with the specific version
cat > "$vault_toml" <<EOF
# vault/pending/${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
cd "$ops_root"
git checkout -B "$branch_name" 2>/dev/null || git checkout "$branch_name"
# Add and commit
git add -A
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/${PROJECT_REPO}/pulls" \
-d "{\"title\":\"${pr_title}\",\"head\":\"${branch_name}\",\"base\":\"main\",\"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}/${PROJECT_REPO}/pulls/${pr_number}"
echo ""
echo "Release PR created: ${pr_url}"
echo ""
echo "Next steps:"
echo " 1. Review the PR"
echo " 2. Approve and merge (requires 2 reviewers for vault items)"
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"
}
# ── Main dispatch ────────────────────────────────────────────────────────────
case "${1:-}" in
@ -2624,6 +2741,7 @@ case "${1:-}" in
status) shift; disinto_status "$@" ;;
secrets) shift; disinto_secrets "$@" ;;
run) shift; disinto_run "$@" ;;
release) shift; disinto_release "$@" ;;
hire-an-agent) shift; disinto_hire_an_agent "$@" ;;
-h|--help) usage ;;
*) usage ;;