diff --git a/bin/disinto b/bin/disinto index 652e42d..cc9a95d 100755 --- a/bin/disinto +++ b/bin/disinto @@ -40,7 +40,6 @@ Usage: disinto status Show factory status disinto secrets Manage encrypted secrets disinto run Run action in ephemeral runner container - disinto release Create vault PR for release (e.g., v1.2.0) disinto hire-an-agent [--formula ] Hire a new agent (create user + .profile repo) @@ -233,6 +232,7 @@ 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,7 +261,9 @@ services: security_opt: - apparmor=unconfined volumes: - - agent-data:/home/agent/data + - ./vault:/home/agent/disinto/vault + - ./lib:/home/agent/disinto/lib:ro + - ./formulas:/home/agent/disinto/formulas:ro environment: FORGE_URL: http://forgejo:3000 DISINTO_CONTAINER: "1" @@ -2611,125 +2613,6 @@ 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 -# 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 " >&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" </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 @@ -2741,7 +2624,6 @@ 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 ;; diff --git a/docker/agents/Dockerfile b/docker/agents/Dockerfile index 0b6fad5..b1543fb 100644 --- a/docker/agents/Dockerfile +++ b/docker/agents/Dockerfile @@ -24,14 +24,11 @@ RUN curl -sL https://dl.gitea.com/tea/0.9.2/tea-0.9.2-linux-amd64 -o /usr/local/ # Non-root user RUN useradd -m -u 1000 -s /bin/bash agent -# Copy disinto code into the image -COPY . /home/agent/disinto - COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Entrypoint runs as root to start the cron daemon; # cron jobs execute as the agent user (crontab -u agent). -WORKDIR /home/agent/disinto +WORKDIR /home/agent ENTRYPOINT ["/entrypoint.sh"] diff --git a/formulas/release.toml b/formulas/release.toml deleted file mode 100644 index 62add13..0000000 --- a/formulas/release.toml +++ /dev/null @@ -1,245 +0,0 @@ -# formulas/release.toml — Release formula -# -# Defines the release workflow: tag Forgejo main, push to mirrors, build -# and tag the agents Docker image, and restart agents. -# -# Triggered by vault PR approval (human creates vault PR, approves it, then -# runner executes via `disinto run `). -# -# Example vault item: -# id = "release-v1.2.0" -# formula = "release" -# context = "Tag v1.2.0 — includes vault redesign, .profile system, architect agent" -# secrets = [] -# -# Steps: preflight → tag-main → push-mirrors → build-image → tag-image → restart-agents → commit-result - -name = "release" -description = "Tag Forgejo main, push to mirrors, build and tag agents image, restart agents" -version = 1 - -[context] -files = ["docker-compose.yml"] - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 1: preflight -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "preflight" -title = "Validate release prerequisites" -description = """ -Validate release prerequisites before proceeding. - -1. Check that RELEASE_VERSION is set: - - Must be in format: v1.2.3 (semver with 'v' prefix) - - Validate with regex: ^v[0-9]+\\.[0-9]+\\.[0-9]+$ - - If not set, exit with error - -2. Check that FORGE_TOKEN and FORGE_URL are set: - - Required for Forgejo API calls - -3. Check that DOCKER_HOST is accessible: - - Test with: docker info - - Required for image build - -4. Check current branch is main: - - git rev-parse --abbrev-ref HEAD - - Must be 'main' or 'master' - -5. Pull latest code: - - git fetch origin "$PRIMARY_BRANCH" - - git reset --hard origin/"$PRIMARY_BRANCH" - - Ensure working directory is clean - -6. Check if tag already exists locally: - - git tag -l "$RELEASE_VERSION" - - If exists, exit with error - -7. Check if tag already exists on Forgejo: - - curl -sf -H "Authorization: token $FORGE_TOKEN" \ - - "$FORGE_URL/api/v1/repos/johba/disinto/git/tags/$RELEASE_VERSION" - - If exists, exit with error - -8. Export RELEASE_VERSION for subsequent steps: - - export RELEASE_VERSION (already set from vault action) -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 2: tag-main -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "tag-main" -title = "Create tag on Forgejo main via API" -description = """ -Create the release tag on Forgejo main via the Forgejo API. - -1. Get current HEAD SHA of main: - - curl -sf -H "Authorization: token $FORGE_TOKEN" \ - - "$FORGE_URL/api/v1/repos/johba/disinto/branches/$PRIMARY_BRANCH" - - Parse sha field from response - -2. Create tag via Forgejo API: - - curl -sf -X POST \ - - -H "Authorization: token $FORGE_TOKEN" \ - - -H "Content-Type: application/json" \ - - "$FORGE_URL/api/v1/repos/johba/disinto/tags" \ - - -d "{\"tag\":\"$RELEASE_VERSION\",\"target\":\"$HEAD_SHA\",\"message\":\"Release $RELEASE_VERSION\"}" - - Parse response for success - -3. Log the tag creation: - - echo "Created tag $RELEASE_VERSION on Forgejo (SHA: $HEAD_SHA)" - -4. Store HEAD SHA for later verification: - - echo "$HEAD_SHA" > /tmp/release-head-sha -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 3: push-mirrors -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "push-mirrors" -title = "Push tag to mirrors (Codeberg, GitHub)" -description = """ -Push the newly created tag to all configured mirrors. - -1. Add mirror remotes if not already present: - - Codeberg: git remote add codeberg git@codeberg.org:johba/disinto.git - - GitHub: git remote add github git@github.com:disinto/disinto.git - - Check with: git remote -v - -2. Push tag to Codeberg: - - git push codeberg "$RELEASE_VERSION" --tags - - Or push all tags: git push codeberg --tags - -3. Push tag to GitHub: - - git push github "$RELEASE_VERSION" --tags - - Or push all tags: git push github --tags - -4. Verify tags exist on mirrors: - - curl -sf -H "Authorization: token $GITHUB_TOKEN" \ - - "https://api.github.com/repos/disinto/disinto/tags/$RELEASE_VERSION" - - curl -sf -H "Authorization: token $FORGE_TOKEN" \ - - "$FORGE_URL/api/v1/repos/johba/disinto/git/tags/$RELEASE_VERSION" - -5. Log success: - - echo "Tag $RELEASE_VERSION pushed to mirrors" -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 4: build-image -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "build-image" -title = "Build agents Docker image" -description = """ -Build the new agents Docker image with the tagged code. - -1. Build image without cache to ensure fresh build: - - docker compose build --no-cache agents - -2. Verify image was created: - - docker images | grep disinto-agents - - Check image exists and has recent timestamp - -3. Store image ID for later: - - docker images disinto-agents --format "{{.ID}}" > /tmp/release-image-id - -4. Log build completion: - - echo "Built disinto-agents image" -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 5: tag-image -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "tag-image" -title = "Tag Docker image with version" -description = """ -Tag the newly built agents image with the release version. - -1. Get the untagged image ID: - - docker images disinto-agents --format "{{.ID}}" --no-trunc | head -1 - -2. Tag the image: - - docker tag disinto-agents disinto-agents:$RELEASE_VERSION - -3. Verify tag: - - docker images disinto-agents - -4. Log tag: - - echo "Tagged disinto-agents:$RELEASE_VERSION" -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 6: restart-agents -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "restart-agents" -title = "Restart agent containers with new image" -description = """ -Restart agent containers to use the new image. - -1. Pull the new image (in case it was pushed somewhere): - - docker compose pull agents - -2. Stop and remove existing agent containers: - - docker compose down agents agents-llama 2>/dev/null || true - -3. Start agents with new image: - - docker compose up -d agents agents-llama - -4. Wait for containers to be healthy: - - for i in {1..30}; do - - if docker inspect --format='{{.State.Health.Status}}' agents | grep -q healthy; then - - echo "Agents container healthy"; break - - fi - - sleep 5 - - done - -5. Verify containers are running: - - docker compose ps agents agents-llama - -6. Log restart: - - echo "Restarted agents containers" -""" - -# ───────────────────────────────────────────────────────────────────────────────── -# Step 7: commit-result -# ───────────────────────────────────────────────────────────────────────────────── - -[[steps]] -id = "commit-result" -title = "Write release result" -description = """ -Write the release result to a file for tracking. - -1. Get the image ID: - - IMAGE_ID=$(cat /tmp/release-image-id) - -2. Create result file: - - cat > /tmp/release-result.json <