CI/CD / build-and-push (push) Successful in 7s
Path-based build selection diffed against github.event.before: - bot/, api/, requirements.txt -> bot + api images (both COPY the same python sources; only the CMD differs) - Dockerfile -> bot only; Dockerfile.api -> api only - dashboard/ -> dashboard only - .gitea/workflows/ci.yml, first push or force push -> all (safe fallback) - anything else (tests/, docs) -> no builds, no manifest update The k8s-manifests sed only bumps tags of rebuilt images, so unchanged deployments keep their current tag and don't restart. Registry login, buildx, verification and manifest update are all skipped when nothing needs building. Telegram message now lists what was built. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
246 lines
10 KiB
YAML
246 lines
10 KiB
YAML
name: CI/CD
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
|
|
env:
|
|
REGISTRY: gitea.gitea.svc.cluster.local:3000
|
|
K8S_MANIFESTS_REPO: http://chemavx:${{ secrets.CI_TOKEN }}@gitea.gitea.svc.cluster.local:3000/chemavx/k8s-manifests.git
|
|
GIT_SSL_NO_VERIFY: "true"
|
|
TELEGRAM_CHAT_ID: "5138407666"
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ssl-verify: false
|
|
# Full history: needed to diff against github.event.before
|
|
fetch-depth: 0
|
|
|
|
- name: Set image tag
|
|
id: tag
|
|
run: echo "TAG=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
|
|
|
- name: Detect changed components
|
|
id: changes
|
|
run: |
|
|
BEFORE="${{ github.event.before }}"
|
|
CHANGED=""
|
|
case "$BEFORE" in
|
|
""|0000000000000000000000000000000000000000)
|
|
echo "First push or unknown base — building all images"
|
|
CHANGED="__all__"
|
|
;;
|
|
*)
|
|
if git cat-file -e "$BEFORE" 2>/dev/null; then
|
|
CHANGED=$(git diff --name-only "$BEFORE" "$GITHUB_SHA")
|
|
else
|
|
echo "Base commit $BEFORE not in history (force push?) — building all images"
|
|
CHANGED="__all__"
|
|
fi
|
|
;;
|
|
esac
|
|
echo "Changed files:"
|
|
echo "$CHANGED"
|
|
|
|
if [ "$CHANGED" = "__all__" ]; then
|
|
BOT=true; API=true; DASH=true
|
|
else
|
|
BOT=false; API=false; DASH=false
|
|
matches() { echo "$CHANGED" | grep -qE "$1"; }
|
|
# The workflow itself affects every image build
|
|
if matches '^\.gitea/workflows/ci\.yml$'; then BOT=true; API=true; DASH=true; fi
|
|
# bot and api images both COPY bot/, api/ and requirements.txt
|
|
if matches '^(bot/|api/|requirements\.txt$)'; then BOT=true; API=true; fi
|
|
if matches '^Dockerfile$'; then BOT=true; fi
|
|
if matches '^Dockerfile\.api$'; then API=true; fi
|
|
# dashboard image builds from the dashboard/ context only
|
|
if matches '^dashboard/'; then DASH=true; fi
|
|
fi
|
|
|
|
ANY=false
|
|
if [ "$BOT" = "true" ] || [ "$API" = "true" ] || [ "$DASH" = "true" ]; then ANY=true; fi
|
|
echo "build_bot=$BOT" >> $GITHUB_OUTPUT
|
|
echo "build_api=$API" >> $GITHUB_OUTPUT
|
|
echo "build_dashboard=$DASH" >> $GITHUB_OUTPUT
|
|
echo "build_any=$ANY" >> $GITHUB_OUTPUT
|
|
echo "Will build: bot=$BOT api=$API dashboard=$DASH"
|
|
|
|
- name: Log in to registry
|
|
if: steps.changes.outputs.build_any == 'true'
|
|
run: echo "${{ secrets.CI_TOKEN }}" | docker login gitea.gitea.svc.cluster.local:3000 -u chemavx --password-stdin
|
|
|
|
- name: Create buildx builder
|
|
if: steps.changes.outputs.build_any == 'true'
|
|
run: |
|
|
cat > /tmp/buildkitd.toml << 'EOF'
|
|
[registry."registry-cache.registry-cache.svc.cluster.local:5000"]
|
|
http = true
|
|
insecure = true
|
|
|
|
[registry."gitea.gitea.svc.cluster.local:3000"]
|
|
http = true
|
|
insecure = true
|
|
|
|
[registry."docker.io"]
|
|
mirrors = ["registry-cache.registry-cache.svc.cluster.local:5000"]
|
|
EOF
|
|
docker buildx create \
|
|
--name ci-builder \
|
|
--driver docker-container \
|
|
--driver-opt network=host \
|
|
--config /tmp/buildkitd.toml \
|
|
--use
|
|
docker buildx inspect --bootstrap
|
|
|
|
- name: Build and push bot image
|
|
if: steps.changes.outputs.build_bot == 'true'
|
|
run: |
|
|
TAG=${{ steps.tag.outputs.TAG }}
|
|
docker buildx build \
|
|
--builder ci-builder \
|
|
--cache-from type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot:buildcache \
|
|
--cache-to type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot:buildcache,mode=max \
|
|
-t gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot:${TAG} \
|
|
--push \
|
|
-f Dockerfile .
|
|
|
|
- name: Build and push API image
|
|
if: steps.changes.outputs.build_api == 'true'
|
|
run: |
|
|
TAG=${{ steps.tag.outputs.TAG }}
|
|
docker buildx build \
|
|
--builder ci-builder \
|
|
--cache-from type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-api:buildcache \
|
|
--cache-to type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-api:buildcache,mode=max \
|
|
-t gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-api:${TAG} \
|
|
--push \
|
|
-f Dockerfile.api .
|
|
|
|
- name: Build and push dashboard image
|
|
if: steps.changes.outputs.build_dashboard == 'true'
|
|
run: |
|
|
TAG=${{ steps.tag.outputs.TAG }}
|
|
docker buildx build \
|
|
--builder ci-builder \
|
|
--cache-from type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-dashboard:buildcache \
|
|
--cache-to type=registry,ref=gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-dashboard:buildcache,mode=max \
|
|
-t gitea.gitea.svc.cluster.local:3000/chemavx/polymarket-bot-dashboard:${TAG} \
|
|
--push \
|
|
-f dashboard/Dockerfile \
|
|
dashboard
|
|
|
|
- name: Verify images in registry
|
|
if: steps.changes.outputs.build_any == 'true'
|
|
run: |
|
|
TAG=${{ steps.tag.outputs.TAG }}
|
|
check_image() {
|
|
local image=$1
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
-u "chemavx:${{ secrets.CI_TOKEN }}" \
|
|
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
|
"http://gitea.gitea.svc.cluster.local:3000/v2/chemavx/${image}/manifests/${TAG}")
|
|
if [ "$HTTP_CODE" != "200" ]; then
|
|
echo "ERROR: chemavx/${image}:${TAG} not found in registry (HTTP $HTTP_CODE)"
|
|
exit 1
|
|
fi
|
|
echo "OK: chemavx/${image}:${TAG} verified in registry"
|
|
}
|
|
if [ "${{ steps.changes.outputs.build_bot }}" = "true" ]; then
|
|
check_image polymarket-bot
|
|
fi
|
|
if [ "${{ steps.changes.outputs.build_api }}" = "true" ]; then
|
|
check_image polymarket-bot-api
|
|
fi
|
|
if [ "${{ steps.changes.outputs.build_dashboard }}" = "true" ]; then
|
|
check_image polymarket-bot-dashboard
|
|
fi
|
|
|
|
- name: Update k8s manifests
|
|
if: steps.changes.outputs.build_any == 'true'
|
|
run: |
|
|
pip3 install pyyaml -q
|
|
|
|
TAG=${{ steps.tag.outputs.TAG }}
|
|
|
|
git config --global user.email "ci@git.chemavx.xyz"
|
|
git config --global user.name "Gitea CI"
|
|
|
|
git clone ${{ env.K8S_MANIFESTS_REPO }} /tmp/k8s-manifests
|
|
cd /tmp/k8s-manifests
|
|
|
|
# Only bump the tag of images that were actually rebuilt: the others
|
|
# keep their current (still existing) tag in the registry.
|
|
if [ "${{ steps.changes.outputs.build_bot }}" = "true" ]; then
|
|
sed -i "s|image: .*polymarket-bot[^-].*|image: git.chemavx.xyz/chemavx/polymarket-bot:${TAG}|g" \
|
|
polymarket-bot/deployment-bot.yaml
|
|
fi
|
|
if [ "${{ steps.changes.outputs.build_api }}" = "true" ]; then
|
|
sed -i "s|image: .*polymarket-bot-api.*|image: git.chemavx.xyz/chemavx/polymarket-bot-api:${TAG}|g" \
|
|
polymarket-bot/deployment-api.yaml
|
|
fi
|
|
if [ "${{ steps.changes.outputs.build_dashboard }}" = "true" ]; then
|
|
sed -i "s|image: .*polymarket-bot-dashboard.*|image: git.chemavx.xyz/chemavx/polymarket-bot-dashboard:${TAG}|g" \
|
|
polymarket-bot/deployment-dashboard.yaml
|
|
fi
|
|
sed -i "s|imagePullPolicy: Never|imagePullPolicy: Always|g" \
|
|
polymarket-bot/deployment-bot.yaml \
|
|
polymarket-bot/deployment-api.yaml \
|
|
polymarket-bot/deployment-dashboard.yaml
|
|
|
|
python3 -c "
|
|
import yaml, sys
|
|
files = [
|
|
'polymarket-bot/deployment-bot.yaml',
|
|
'polymarket-bot/deployment-api.yaml',
|
|
'polymarket-bot/deployment-dashboard.yaml',
|
|
]
|
|
errors = []
|
|
for f in files:
|
|
try:
|
|
yaml.safe_load(open(f))
|
|
print('OK: ' + f)
|
|
except yaml.YAMLError as e:
|
|
errors.append('FAIL: ' + f + ': ' + str(e))
|
|
if errors:
|
|
for e in errors: print(e, file=sys.stderr)
|
|
sys.exit(1)
|
|
"
|
|
|
|
git add polymarket-bot/deployment-bot.yaml polymarket-bot/deployment-api.yaml polymarket-bot/deployment-dashboard.yaml
|
|
git diff --cached --quiet || git commit -m "ci: update polymarket-bot images to ${TAG} [skip ci]"
|
|
git push
|
|
|
|
- name: Notify Telegram
|
|
if: always()
|
|
env:
|
|
TAG: ${{ steps.tag.outputs.TAG }}
|
|
JOB_STATUS: ${{ job.status }}
|
|
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
|
BUILD_BOT: ${{ steps.changes.outputs.build_bot }}
|
|
BUILD_API: ${{ steps.changes.outputs.build_api }}
|
|
BUILD_DASH: ${{ steps.changes.outputs.build_dashboard }}
|
|
run: |
|
|
TAG="${TAG:-${GITHUB_SHA:0:8}}"
|
|
BUILT=""
|
|
[ "$BUILD_BOT" = "true" ] && BUILT="${BUILT}bot "
|
|
[ "$BUILD_API" = "true" ] && BUILT="${BUILT}api "
|
|
[ "$BUILD_DASH" = "true" ] && BUILT="${BUILT}dashboard "
|
|
if [ "$JOB_STATUS" = "success" ]; then
|
|
if [ -n "$BUILT" ]; then
|
|
MSG="✅ Deploy polymarket-bot:${TAG} completado (imágenes: ${BUILT% })"
|
|
else
|
|
MSG="✅ CI polymarket-bot:${TAG} OK — sin cambios de imagen, nada que desplegar"
|
|
fi
|
|
else
|
|
MSG="❌ Deploy polymarket-bot:${TAG} fallido (status: ${JOB_STATUS})"
|
|
fi
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
|
|
-d "chat_id=${{ env.TELEGRAM_CHAT_ID }}" \
|
|
--data-urlencode "text=${MSG}"
|