Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions deploy/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,31 @@ PROCESSGIT_UPDATER_REPO=Algomation-AI/ProcessGit
PROCESSGIT_UPDATER_STUB=true

# =============================================================================
# ProcessGit app config (passed through to the main container)
# ProcessGit app config (consumed by the init-config bootstrap step)
# =============================================================================
#
# Any Gitea/ProcessGit configuration env vars can go here. The main
# container receives this file via env_file, so every key=value pair
# becomes an env var inside the container. See:
# https://docs.gitea.com/installation/install-with-docker
# These are written into /data/gitea/conf/app.ini on first boot by the
# processgit-init-config service. They're effective only on the first
# `up -d` for a fresh volume; subsequent restarts read the existing
# app.ini and ignore these (idempotency). To change them after the
# first boot, edit /data/gitea/conf/app.ini inside the volume directly.
#
# Some common ones:
#
# DOMAIN=processgit.example.com
# SSH_DOMAIN=processgit.example.com
# ROOT_URL=https://processgit.example.com/
# APP_NAME=ProcessGit
# DISABLE_REGISTRATION=true
# Defaults work for `docker compose up -d` on localhost with the
# default port mapping (18080 → 3000, 12222 → 22).

# Hostname/IP that operators will reach the instance at. Used in clone
# URLs and OAuth callbacks.
PROCESSGIT_DOMAIN=localhost

# Full ROOT_URL including scheme and trailing slash. Match what your
# users will type in the browser.
PROCESSGIT_ROOT_URL=http://localhost:18080/

# External SSH port — must match the host side of the SSH port mapping
# in docker-compose.yml ("12222:22" → 12222). Affects displayed
# `git clone ssh://...` URLs only; the container always listens on 22
# internally.
PROCESSGIT_SSH_PORT=12222

# =============================================================================
# Opting out of the self-update sidecar
Expand All @@ -67,7 +77,7 @@ PROCESSGIT_UPDATER_STUB=true
# blank above, and at `up` time list only the services you want:
#
# docker compose -f deploy/docker-compose.yml up -d \
# processgit-init-perms processgit processgit-bootstrap
# processgit-init-perms processgit-init-config processgit processgit-bootstrap
#
# The main app starts identically — you just won't have in-product
# self-updates. You can always opt back in later by setting the token
Expand Down
146 changes: 146 additions & 0 deletions deploy/bootstrap/init-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/usr/bin/env sh
# Bootstrap Gitea's app.ini before the main container starts.
#
# Why: Gitea, when no app.ini is present, falls through to its interactive
# install wizard. The container then never satisfies the
# /api/v1/version healthcheck (which is not served in install mode), the
# compose orchestration declares it unhealthy, and the
# processgit-bootstrap step that waits on healthy is deadlocked.
#
# This script is the deterministic alternative to depending on the
# upstream gitea/gitea image's s6 startup conventions (which moved
# between s6-overlay v2 and v3 and broke our path-based override of
# /etc/s6/gitea/run, silently skipping the env-var-to-ini conversion
# the image would otherwise do).
#
# Idempotent: if app.ini already exists with INSTALL_LOCK = true, do
# nothing. So restarts and updates don't clobber the operator's tuned
# config or the secrets generated on first boot.

set -eu

CONF=/data/gitea/conf/app.ini

if [ -f "$CONF" ] && grep -q '^INSTALL_LOCK *= *true' "$CONF"; then
echo "[init-config] $CONF exists and is locked; skipping"
exit 0
fi

echo "[init-config] generating $CONF"

mkdir -p \
/data/gitea/conf \
/data/gitea/log \
/data/gitea/attachments \
/data/gitea/avatars \
/data/gitea/repo-avatars \
/data/gitea/sessions \
/data/gitea/indexers \
/data/git/repositories \
/data/git/lfs

# Generate per-deployment secrets using the bundled gitea binary. These
# are written into the file once and never regenerated — losing them
# would invalidate all existing sessions, signed cookies, and lfs JWT
# tokens, so the idempotent guard above protects them.
SECRET_KEY="$(/app/gitea/gitea generate secret SECRET_KEY)"
INTERNAL_TOKEN="$(/app/gitea/gitea generate secret INTERNAL_TOKEN)"
JWT_SECRET="$(/app/gitea/gitea generate secret JWT_SECRET)"
LFS_JWT_SECRET="$(/app/gitea/gitea generate secret LFS_JWT_SECRET)"

# Operator-overridable values; defaults are sane for `docker compose up`
# on localhost with the published port mapping (18080:3000, 12222:22).
APP_NAME="${APP_NAME:-ProcessGit}"
DOMAIN="${PROCESSGIT_DOMAIN:-localhost}"
ROOT_URL="${PROCESSGIT_ROOT_URL:-http://localhost:18080/}"
SSH_PORT="${PROCESSGIT_SSH_PORT:-12222}"

cat > "$CONF" <<EOF
APP_NAME = ${APP_NAME}
RUN_USER = git
RUN_MODE = prod

[server]
PROTOCOL = http
DOMAIN = ${DOMAIN}
HTTP_PORT = 3000
ROOT_URL = ${ROOT_URL}
SSH_DOMAIN = ${DOMAIN}
SSH_PORT = ${SSH_PORT}
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
OFFLINE_MODE = true
DISABLE_ROUTER_LOG = false

[database]
DB_TYPE = sqlite3
PATH = /data/gitea/gitea.db
LOG_SQL = false

[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve

[session]
PROVIDER = file
PROVIDER_CONFIG = /data/gitea/sessions

[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false

[attachment]
PATH = /data/gitea/attachments

[log]
ROOT_PATH = /data/gitea/log
MODE = console
LEVEL = info

[security]
INSTALL_LOCK = true
SECRET_KEY = ${SECRET_KEY}
INTERNAL_TOKEN = ${INTERNAL_TOKEN}
PASSWORD_HASH_ALGO = pbkdf2_v2

[oauth2]
JWT_SECRET = ${JWT_SECRET}

[lfs]
PATH = /data/git/lfs
JWT_SECRET = ${LFS_JWT_SECRET}

[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost

[mailer]
ENABLED = false

[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false

[cron.update_checker]
ENABLED = false

[repository]
ROOT = /data/git/repositories

[mirror]
DEFAULT_INTERVAL = 8h
EOF

chmod 0640 "$CONF"

echo "[init-config] wrote $CONF ($(wc -c < "$CONF") bytes)"
echo "[init-config] APP_NAME=${APP_NAME} DOMAIN=${DOMAIN} ROOT_URL=${ROOT_URL} SSH_PORT=${SSH_PORT}"
28 changes: 27 additions & 1 deletion deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# optional. To run without it:
#
# docker compose -f deploy/docker-compose.yml up -d \
# processgit-init-perms processgit processgit-bootstrap
# processgit-init-perms processgit-init-config processgit processgit-bootstrap
#
# The main app starts identically; you simply lose the in-product update
# story. If PROCESSGIT_UPDATER_TOKEN isn't set in .env, the main app
Expand All @@ -37,6 +37,8 @@ services:
depends_on:
processgit-init-perms:
condition: service_completed_successfully
processgit-init-config:
condition: service_completed_successfully
ports:
- "18080:3000"
- "12222:22"
Expand Down Expand Up @@ -69,6 +71,30 @@ services:
command: ["mkdir -p /data /data/.processgit /data/gitea && chown -R 1000:1000 /data"]
restart: "no"

# First-boot config bootstrap: writes /data/gitea/conf/app.ini with
# INSTALL_LOCK = true and generated secrets so the main container
# boots straight to the API instead of the install wizard. Idempotent
# — re-runs on every `up -d` but only writes if the lock isn't
# already present. Honors PROCESSGIT_DOMAIN, PROCESSGIT_ROOT_URL,
# PROCESSGIT_SSH_PORT from deploy/.env for customization.
processgit-init-config:
image: ghcr.io/algomation-ai/processgit:${PROCESSGIT_VERSION:-latest}
user: "1000:1000"
depends_on:
processgit-init-perms:
condition: service_completed_successfully
volumes:
- processgit-data:/data
env_file:
- .env
environment:
APP_NAME: "ProcessGit"
PROCESSGIT_DOMAIN: ${PROCESSGIT_DOMAIN:-localhost}
PROCESSGIT_ROOT_URL: ${PROCESSGIT_ROOT_URL:-http://localhost:18080/}
PROCESSGIT_SSH_PORT: ${PROCESSGIT_SSH_PORT:-12222}
entrypoint: ["/opt/processgit/bootstrap/init-config.sh"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make init-config runnable before new image is published

The new processgit-init-config service invokes /opt/processgit/bootstrap/init-config.sh, but that script only exists in images built from this commit onward; if PROCESSGIT_VERSION resolves to an older published tag (for example latest before the new release is pushed), this container fails at startup with a missing entrypoint, and processgit is then blocked by depends_on: condition: service_completed_successfully. Because this service has no build section, it cannot use a locally built image as a fallback in that scenario.

Useful? React with 👍 / 👎.

restart: "no"

processgit-bootstrap:
image: ghcr.io/algomation-ai/processgit:${PROCESSGIT_VERSION:-latest}
restart: "no"
Expand Down
Loading