Compare commits
5 Commits
chore/runn
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa35579609
|
||
|
|
7c2ba75c36
|
||
|
f1ac88259b
|
|||
|
b2109e7937
|
|||
| 373b1953a2 |
68
.gitea/actions/bump-stacks/action.yml
Normal file
68
.gitea/actions/bump-stacks/action.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
name: bump-stacks
|
||||||
|
description: |
|
||||||
|
Push the new short sha for a stack into libretech/gitops-sandbox/stacks.yml.
|
||||||
|
Idempotent: a no-op when the sha is already pinned. Retries up to 3× on
|
||||||
|
non-fast-forward to absorb concurrent bumps from sibling stack repos.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
stack:
|
||||||
|
description: stack name (key under `stacks:` in stacks.yml)
|
||||||
|
required: true
|
||||||
|
sha:
|
||||||
|
description: short git sha (7 chars) of the image tag just pushed
|
||||||
|
required: true
|
||||||
|
bot_token:
|
||||||
|
description: PAT with write:repository on libretech/gitops-sandbox
|
||||||
|
required: true
|
||||||
|
sandbox_repo:
|
||||||
|
description: orchestrator repo slug
|
||||||
|
required: false
|
||||||
|
default: libretech/gitops-sandbox
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
env:
|
||||||
|
STACK: ${{ inputs.stack }}
|
||||||
|
SHA: ${{ inputs.sha }}
|
||||||
|
BOT_TOKEN: ${{ inputs.bot_token }}
|
||||||
|
SANDBOX_REPO: ${{ inputs.sandbox_repo }}
|
||||||
|
ACTION_PATH: ${{ github.action_path }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
cd "$WORK"
|
||||||
|
git config --global user.name "gitops-bot"
|
||||||
|
git config --global user.email "gitops-bot@librete.ch"
|
||||||
|
|
||||||
|
REPO_URL="https://oauth2:${BOT_TOKEN}@git.librete.ch/${SANDBOX_REPO}.git"
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
rm -rf clone
|
||||||
|
git clone --depth=2 "$REPO_URL" clone
|
||||||
|
cd clone
|
||||||
|
[ -f package.json ] || echo '{"type":"module","dependencies":{"yaml":"^2.6.1"}}' > package.json
|
||||||
|
bun install --silent
|
||||||
|
|
||||||
|
set +e
|
||||||
|
bun "$ACTION_PATH/bump.js" "$STACK" "$SHA"
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
if [ "$rc" = "10" ]; then
|
||||||
|
echo "::notice::stacks.yml already at sha=$SHA, no commit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
[ "$rc" = "0" ] || exit "$rc"
|
||||||
|
|
||||||
|
git add stacks.yml
|
||||||
|
git commit -m "bump(${STACK}): sha=${SHA}"
|
||||||
|
if git push origin main; then
|
||||||
|
echo "::notice::pushed bump for ${STACK} to ${SANDBOX_REPO}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "push rejected, attempt ${attempt}/3"
|
||||||
|
cd ..
|
||||||
|
sleep $((attempt * 3))
|
||||||
|
done
|
||||||
|
echo "::error::could not push bump after 3 attempts"
|
||||||
|
exit 1
|
||||||
29
.gitea/actions/bump-stacks/bump.js
Normal file
29
.gitea/actions/bump-stacks/bump.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
// Edit stacks.yml in the gitops-sandbox checkout to pin a new sha for `stack`.
|
||||||
|
// Exit 10 = no-op (already at sha). Exit 0 = mutated, ready to commit.
|
||||||
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { parseDocument } from "yaml";
|
||||||
|
|
||||||
|
const [, , stack, sha] = process.argv;
|
||||||
|
if (!stack || !sha) {
|
||||||
|
console.error("usage: bump.js <stack> <sha>");
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = parseDocument(readFileSync("stacks.yml", "utf8"));
|
||||||
|
const stacks = doc.get("stacks");
|
||||||
|
if (!stacks?.has(stack)) {
|
||||||
|
console.error(`stack '${stack}' not in stacks.yml`);
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = stacks.get(stack);
|
||||||
|
const oldSha = node.get("sha");
|
||||||
|
if (String(oldSha) === String(sha)) {
|
||||||
|
console.log(`no-op: ${stack} already at sha=${sha}`);
|
||||||
|
process.exit(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.set("sha", sha);
|
||||||
|
writeFileSync("stacks.yml", doc.toString());
|
||||||
|
console.log(`bumped ${stack}: ${oldSha} → ${sha}`);
|
||||||
76
.gitea/actions/deploy-stack/action.yml
Normal file
76
.gitea/actions/deploy-stack/action.yml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
name: deploy-stack
|
||||||
|
description: |
|
||||||
|
SSH to netcup, pull the pinned image, restart the stack via docker compose.
|
||||||
|
Each stack repo holds its own SSH key; the netcup `authorized_keys` line
|
||||||
|
is forced-command-locked to that stack's directory.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
stack:
|
||||||
|
description: stack name (cosmetic, used in deploy-marker file)
|
||||||
|
required: true
|
||||||
|
target:
|
||||||
|
description: absolute path on netcup, e.g. /srv/gitops-hello
|
||||||
|
required: true
|
||||||
|
image:
|
||||||
|
description: full image ref including tag, e.g. git.librete.ch/libretech/gitops-hello:sha-abc1234
|
||||||
|
required: true
|
||||||
|
ssh_key:
|
||||||
|
description: private SSH key (PEM, multi-line ok)
|
||||||
|
required: true
|
||||||
|
ssh_host:
|
||||||
|
description: netcup hostname, e.g. cloud.librete.ch
|
||||||
|
required: true
|
||||||
|
ssh_user:
|
||||||
|
description: netcup user, e.g. tengo
|
||||||
|
required: true
|
||||||
|
registry:
|
||||||
|
description: registry hostname for docker login
|
||||||
|
required: true
|
||||||
|
registry_user:
|
||||||
|
description: registry username
|
||||||
|
required: true
|
||||||
|
registry_pass:
|
||||||
|
description: registry PAT
|
||||||
|
required: true
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
env:
|
||||||
|
STACK: ${{ inputs.stack }}
|
||||||
|
TARGET: ${{ inputs.target }}
|
||||||
|
IMAGE: ${{ inputs.image }}
|
||||||
|
SSH_KEY: ${{ inputs.ssh_key }}
|
||||||
|
SSH_HOST: ${{ inputs.ssh_host }}
|
||||||
|
SSH_USER: ${{ inputs.ssh_user }}
|
||||||
|
REGISTRY: ${{ inputs.registry }}
|
||||||
|
REGISTRY_USER: ${{ inputs.registry_user }}
|
||||||
|
REGISTRY_PASS: ${{ inputs.registry_pass }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
umask 077
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_deploy
|
||||||
|
chmod 600 ~/.ssh/id_deploy
|
||||||
|
ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
|
SSH="ssh -i ~/.ssh/id_deploy -o BatchMode=yes ${SSH_USER}@${SSH_HOST}"
|
||||||
|
|
||||||
|
# The forced-command on netcup's side is parameterised by the
|
||||||
|
# original command, so we send a single-line script via stdin.
|
||||||
|
$SSH bash -s <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$TARGET"
|
||||||
|
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
||||||
|
# Pin the image for compose via a stack-local .env file.
|
||||||
|
# HELLO_IMAGE / <STACK_UPPER>_IMAGE etc. — generic env name pattern.
|
||||||
|
STACK_UPPER=\$(echo "$STACK" | tr a-z A-Z)
|
||||||
|
printf '%s_IMAGE=%s\n' "\$STACK_UPPER" "$IMAGE" > .env.deploy
|
||||||
|
docker compose --env-file .env.deploy pull
|
||||||
|
docker compose --env-file .env.deploy up -d
|
||||||
|
# Write deploy marker — read by drift checks + observability.
|
||||||
|
printf '%s\n' "image=$IMAGE" "deployed_at=\$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .deployed
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "::notice::deployed ${STACK}: ${IMAGE}"
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
# Example build.yml for a libreshop component repo.
|
# Example build.yml for a libreshop component repo.
|
||||||
# Copy to .gitea/workflows/build.yml and remove the "on:" override below.
|
# Copy to .gitea/workflows/build.yml in the consuming repo.
|
||||||
#
|
#
|
||||||
# Replace the existing multi-step build.yml with these ~20 lines.
|
# Lives outside .gitea/workflows/ on purpose — Gitea would auto-run it
|
||||||
|
# here against this repo (which has no Dockerfile) and the build would
|
||||||
|
# fail every time.
|
||||||
|
|
||||||
name: build
|
name: build
|
||||||
|
|
||||||
Reference in New Issue
Block a user