diff --git a/changemaker-control-panel/agent/Dockerfile b/changemaker-control-panel/agent/Dockerfile index 06cf98a..cf3aca7 100644 --- a/changemaker-control-panel/agent/Dockerfile +++ b/changemaker-control-panel/agent/Dockerfile @@ -8,7 +8,10 @@ COPY src/ ./src/ RUN npx tsc FROM node:20-alpine -RUN apk add --no-cache docker-cli docker-cli-compose git rsync +# bash + curl + jq + python3 are required by the changemaker scripts the agent +# shells out to (upgrade-check.sh, upgrade.sh, backup.sh). Without them, every +# /upgrade/* and /backup/* call returns "command not found" failures. +RUN apk add --no-cache docker-cli docker-cli-compose git rsync bash curl jq python3 WORKDIR /app COPY package*.json ./ RUN npm ci --production diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 6f860c0..bfaa678 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1427,9 +1427,10 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ccp-agent-data:/var/lib/ccp-agent - ccp-agent-certs:/etc/ccp-agent - # Mount the instance directory so the agent can read compose files and run - # `docker compose -p ` commands against the real project on disk. - - .:/app/instance:ro + # Mount the instance directory so the agent can read compose files and + # write status.json + backups (writable; agent already has docker.sock, + # so file write access is not an additional security escalation). + - .:/app/instance environment: - AGENT_PORT=7443 - AGENT_DATA_DIR=/var/lib/ccp-agent diff --git a/docker-compose.yml b/docker-compose.yml index a60f3a6..fa307a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1450,7 +1450,10 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ccp-agent-data:/var/lib/ccp-agent - ccp-agent-certs:/etc/ccp-agent - - .:/app/instance:ro + # Writable: agent must write data/upgrade/{status,progress,result}.json + # and data/backups/*.tar.gz. Agent already has docker.sock — file write + # access is not an additional security escalation. + - .:/app/instance environment: - AGENT_PORT=7443 - AGENT_DATA_DIR=/var/lib/ccp-agent diff --git a/scripts/build-release.sh b/scripts/build-release.sh index dd02921..b49579a 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -295,12 +295,23 @@ if [[ "$UPLOAD" == "true" ]]; then fi fi + # Gitea 1.23.x only initializes Release.CreatedUnix inside its createTag() + # path. If the git tag already exists on origin when we POST /releases, + # createTag() is skipped and CreatedUnix stays 0, which makes /releases/latest + # silently return an older release. Remove the remote tag first so Gitea + # creates it via target_commitish below. The tag is preserved locally and + # gets recreated at the same SHA — no history is lost. + if git ls-remote --exit-code origin "refs/tags/${TAG}" >/dev/null 2>&1; then + warn "Removing remote tag ${TAG} so Gitea can recreate it (CreatedUnix init)" + git push origin ":refs/tags/${TAG}" >/dev/null 2>&1 || true + fi + info "Creating Gitea release ${TAG}..." RELEASE_RESPONSE=$(curl -sf -X POST \ "${GITEA_HOST}/api/v1/repos/admin/changemaker.lite/releases" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ - -d "{\"tag_name\":\"${TAG}\",\"name\":\"Changemaker Lite ${TAG}\",\"body\":\"Release ${TAG} (${COMMIT_SHA})\"}" \ + -d "{\"tag_name\":\"${TAG}\",\"target_commitish\":\"${COMMIT_SHA}\",\"name\":\"Changemaker Lite ${TAG}\",\"body\":\"Release ${TAG} (${COMMIT_SHA})\"}" \ 2>/dev/null || true) RELEASE_ID=$(echo "$RELEASE_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)