Compare commits
1 commit
566547ee26
...
9ef4d95e3c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ef4d95e3c |
2 changed files with 1 additions and 169 deletions
34
bin/disinto
34
bin/disinto
|
|
@ -12,7 +12,6 @@
|
||||||
# disinto secrets <subcommand> Manage encrypted secrets
|
# disinto secrets <subcommand> Manage encrypted secrets
|
||||||
# disinto run <action-id> Run action in ephemeral runner container
|
# disinto run <action-id> Run action in ephemeral runner container
|
||||||
# disinto ci-logs <pipeline> [--step <name>] Read CI logs from Woodpecker SQLite
|
# disinto ci-logs <pipeline> [--step <name>] Read CI logs from Woodpecker SQLite
|
||||||
# disinto backup create <outfile> Export factory state for migration
|
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# disinto init https://github.com/user/repo
|
# disinto init https://github.com/user/repo
|
||||||
|
|
@ -40,7 +39,6 @@ source "${FACTORY_ROOT}/lib/generators.sh"
|
||||||
source "${FACTORY_ROOT}/lib/forge-push.sh"
|
source "${FACTORY_ROOT}/lib/forge-push.sh"
|
||||||
source "${FACTORY_ROOT}/lib/ci-setup.sh"
|
source "${FACTORY_ROOT}/lib/ci-setup.sh"
|
||||||
source "${FACTORY_ROOT}/lib/release.sh"
|
source "${FACTORY_ROOT}/lib/release.sh"
|
||||||
source "${FACTORY_ROOT}/lib/backup.sh"
|
|
||||||
source "${FACTORY_ROOT}/lib/claude-config.sh"
|
source "${FACTORY_ROOT}/lib/claude-config.sh"
|
||||||
source "${FACTORY_ROOT}/lib/disinto/backup.sh" # backup create/import
|
source "${FACTORY_ROOT}/lib/disinto/backup.sh" # backup create/import
|
||||||
|
|
||||||
|
|
@ -65,7 +63,6 @@ Usage:
|
||||||
disinto hire-an-agent <agent-name> <role> [--formula <path>] [--local-model <url>] [--model <name>]
|
disinto hire-an-agent <agent-name> <role> [--formula <path>] [--local-model <url>] [--model <name>]
|
||||||
Hire a new agent (create user + .profile repo; re-run to rotate credentials)
|
Hire a new agent (create user + .profile repo; re-run to rotate credentials)
|
||||||
disinto agent <subcommand> Manage agent state (enable/disable)
|
disinto agent <subcommand> Manage agent state (enable/disable)
|
||||||
disinto backup create <outfile> Export factory state (issues + ops bundle)
|
|
||||||
disinto edge <verb> [options] Manage edge tunnel registrations
|
disinto edge <verb> [options] Manage edge tunnel registrations
|
||||||
disinto backup <subcommand> Backup and restore factory state
|
disinto backup <subcommand> Backup and restore factory state
|
||||||
|
|
||||||
|
|
@ -2910,35 +2907,6 @@ EOF
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── backup command ────────────────────────────────────────────────────────────
|
|
||||||
# Usage: disinto backup <subcommand> [args]
|
|
||||||
# Subcommands:
|
|
||||||
# create <outfile.tar.gz> Create backup of factory state
|
|
||||||
# import <infile.tar.gz> Restore factory state from backup
|
|
||||||
disinto_backup() {
|
|
||||||
local subcmd="${1:-}"
|
|
||||||
shift || true
|
|
||||||
|
|
||||||
case "$subcmd" in
|
|
||||||
create)
|
|
||||||
echo "Usage: disinto backup create <outfile.tar.gz>" >&2
|
|
||||||
echo " (Not yet implemented)" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
import)
|
|
||||||
backup_import "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: disinto backup <subcommand> [args]" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo "Subcommands:" >&2
|
|
||||||
echo " create <outfile.tar.gz> Create backup of factory state" >&2
|
|
||||||
echo " import <infile.tar.gz> Restore factory state from backup" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Main dispatch ────────────────────────────────────────────────────────────
|
# ── Main dispatch ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
|
|
@ -2955,7 +2923,7 @@ case "${1:-}" in
|
||||||
hire-an-agent) shift; disinto_hire_an_agent "$@" ;;
|
hire-an-agent) shift; disinto_hire_an_agent "$@" ;;
|
||||||
agent) shift; disinto_agent "$@" ;;
|
agent) shift; disinto_agent "$@" ;;
|
||||||
edge) shift; disinto_edge "$@" ;;
|
edge) shift; disinto_edge "$@" ;;
|
||||||
backup) shift; disinto_backup "$@" ;;
|
backup) shift; backup_import "$@" ;;
|
||||||
-h|--help) usage ;;
|
-h|--help) usage ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
136
lib/backup.sh
136
lib/backup.sh
|
|
@ -1,136 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# =============================================================================
|
|
||||||
# disinto backup — export factory state for migration
|
|
||||||
#
|
|
||||||
# Usage: source this file, then call backup_create <outfile.tar.gz>
|
|
||||||
# Requires: FORGE_URL, FORGE_TOKEN, FORGE_REPO, FORGE_OPS_REPO, OPS_REPO_ROOT
|
|
||||||
# =============================================================================
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Fetch all issues (open + closed) for a repo slug and emit the normalized JSON array.
|
|
||||||
# Usage: _backup_fetch_issues <org/repo>
|
|
||||||
_backup_fetch_issues() {
|
|
||||||
local repo_slug="$1"
|
|
||||||
local api_url="${FORGE_API_BASE}/repos/${repo_slug}"
|
|
||||||
|
|
||||||
local all_issues="[]"
|
|
||||||
for state in open closed; do
|
|
||||||
local page=1
|
|
||||||
while true; do
|
|
||||||
local page_items
|
|
||||||
page_items=$(curl -sf -X GET \
|
|
||||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"${api_url}/issues?state=${state}&type=issues&limit=50&page=${page}") || {
|
|
||||||
echo "ERROR: failed to fetch ${state} issues from ${repo_slug} (page ${page})" >&2
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
local count
|
|
||||||
count=$(printf '%s' "$page_items" | jq 'length' 2>/dev/null) || count=0
|
|
||||||
[ -z "$count" ] && count=0
|
|
||||||
[ "$count" -eq 0 ] && break
|
|
||||||
all_issues=$(printf '%s\n%s' "$all_issues" "$page_items" | jq -s 'add')
|
|
||||||
[ "$count" -lt 50 ] && break
|
|
||||||
page=$((page + 1))
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# Normalize to the schema: number, title, body, labels, state
|
|
||||||
printf '%s' "$all_issues" | jq '[.[] | {
|
|
||||||
number: .number,
|
|
||||||
title: .title,
|
|
||||||
body: .body,
|
|
||||||
labels: [.labels[]?.name],
|
|
||||||
state: .state
|
|
||||||
}] | sort_by(.number)'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create a backup tarball of factory state.
|
|
||||||
# Usage: backup_create <outfile.tar.gz>
|
|
||||||
backup_create() {
|
|
||||||
local outfile="${1:-}"
|
|
||||||
if [ -z "$outfile" ]; then
|
|
||||||
echo "Error: output file required" >&2
|
|
||||||
echo "Usage: disinto backup create <outfile.tar.gz>" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Resolve to absolute path before cd-ing into tmpdir
|
|
||||||
case "$outfile" in
|
|
||||||
/*) ;;
|
|
||||||
*) outfile="$(pwd)/${outfile}" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Validate required env
|
|
||||||
: "${FORGE_URL:?FORGE_URL must be set}"
|
|
||||||
: "${FORGE_TOKEN:?FORGE_TOKEN must be set}"
|
|
||||||
: "${FORGE_REPO:?FORGE_REPO must be set}"
|
|
||||||
|
|
||||||
local forge_ops_repo="${FORGE_OPS_REPO:-${FORGE_REPO}-ops}"
|
|
||||||
local ops_repo_root="${OPS_REPO_ROOT:-}"
|
|
||||||
|
|
||||||
if [ -z "$ops_repo_root" ] || [ ! -d "$ops_repo_root/.git" ]; then
|
|
||||||
echo "Error: OPS_REPO_ROOT (${ops_repo_root:-<unset>}) is not a valid git repo" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tmpdir
|
|
||||||
tmpdir=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$tmpdir"' EXIT
|
|
||||||
|
|
||||||
local project_name="${FORGE_REPO##*/}"
|
|
||||||
|
|
||||||
echo "=== disinto backup create ==="
|
|
||||||
echo "Forge: ${FORGE_URL}"
|
|
||||||
echo "Repos: ${FORGE_REPO}, ${forge_ops_repo}"
|
|
||||||
|
|
||||||
# ── 1. Export issues ──────────────────────────────────────────────────────
|
|
||||||
mkdir -p "${tmpdir}/issues"
|
|
||||||
|
|
||||||
echo "Fetching issues for ${FORGE_REPO}..."
|
|
||||||
_backup_fetch_issues "$FORGE_REPO" > "${tmpdir}/issues/${project_name}.json"
|
|
||||||
local main_count
|
|
||||||
main_count=$(jq 'length' "${tmpdir}/issues/${project_name}.json")
|
|
||||||
echo " ${main_count} issues exported"
|
|
||||||
|
|
||||||
echo "Fetching issues for ${forge_ops_repo}..."
|
|
||||||
_backup_fetch_issues "$forge_ops_repo" > "${tmpdir}/issues/${project_name}-ops.json"
|
|
||||||
local ops_count
|
|
||||||
ops_count=$(jq 'length' "${tmpdir}/issues/${project_name}-ops.json")
|
|
||||||
echo " ${ops_count} issues exported"
|
|
||||||
|
|
||||||
# ── 2. Git bundle of ops repo ────────────────────────────────────────────
|
|
||||||
mkdir -p "${tmpdir}/repos"
|
|
||||||
|
|
||||||
echo "Creating git bundle for ${forge_ops_repo}..."
|
|
||||||
git -C "$ops_repo_root" bundle create "${tmpdir}/repos/${project_name}-ops.bundle" --all 2>&1
|
|
||||||
echo " bundle created ($(du -h "${tmpdir}/repos/${project_name}-ops.bundle" | cut -f1))"
|
|
||||||
|
|
||||||
# ── 3. Metadata ──────────────────────────────────────────────────────────
|
|
||||||
local created_at
|
|
||||||
created_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg created_at "$created_at" \
|
|
||||||
--arg source_host "$(hostname)" \
|
|
||||||
--argjson schema_version 1 \
|
|
||||||
--arg forgejo_url "$FORGE_URL" \
|
|
||||||
'{
|
|
||||||
created_at: $created_at,
|
|
||||||
source_host: $source_host,
|
|
||||||
schema_version: $schema_version,
|
|
||||||
forgejo_url: $forgejo_url
|
|
||||||
}' > "${tmpdir}/metadata.json"
|
|
||||||
|
|
||||||
# ── 4. Pack tarball ──────────────────────────────────────────────────────
|
|
||||||
echo "Creating tarball: ${outfile}"
|
|
||||||
tar -czf "$outfile" -C "$tmpdir" metadata.json issues repos
|
|
||||||
local size
|
|
||||||
size=$(du -h "$outfile" | cut -f1)
|
|
||||||
echo "=== Backup complete: ${outfile} (${size}) ==="
|
|
||||||
|
|
||||||
# Clean up before returning — the EXIT trap references the local $tmpdir
|
|
||||||
# which goes out of scope after return, causing 'unbound variable' under set -u.
|
|
||||||
trap - EXIT
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue