From 7a27c89067ee54b90f914c92d62ef054771ff932 Mon Sep 17 00:00:00 2001 From: ningning Date: Fri, 26 Jun 2026 12:02:15 +0800 Subject: [PATCH] feat: add local cicd demo app --- .env.example | 3 ++ .gitea/workflows/deploy.yml | 86 ++++++++++++++++++++++++++++++++ Dockerfile | 9 ++++ __pycache__/app.cpython-314.pyc | Bin 0 -> 3442 bytes app.py | 51 +++++++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 .env.example create mode 100644 .gitea/workflows/deploy.yml create mode 100644 Dockerfile create mode 100644 __pycache__/app.cpython-314.pyc create mode 100644 app.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b3967e7 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +APP_NAME=demo-app +APP_VERSION=local +PORT=8080 diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..7ab5ad4 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,86 @@ +name: Build and deploy demo app + +on: + push: + branches: + - main + workflow_dispatch: + +env: + APP_NAME: demo-app + DEPLOY_DIR: /workspace/apps/demo-app + REGISTRY: localhost:5001 + IMAGE_NAME: localhost:5001/${{ github.repository }} + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Show context + run: | + set -eu + echo "repository=${{ github.repository }}" + echo "sha=${{ github.sha }}" + docker version + + - name: Build image + run: | + set -eu + docker build \ + --label "org.opencontainers.image.revision=${{ github.sha }}" \ + -t "$IMAGE_NAME:${{ github.sha }}" \ + -t "$IMAGE_NAME:latest" \ + . + + - name: Push image + run: | + set -eu + docker push "$IMAGE_NAME:${{ github.sha }}" + docker push "$IMAGE_NAME:latest" + + - name: Deploy with local Docker Compose + run: | + set -eu + mkdir -p "$DEPLOY_DIR" + cat > "$DEPLOY_DIR/docker-compose.yml" < "$DEPLOY_DIR/.env" <> "$DEPLOY_DIR/.env" + fi + fi + docker compose -f "$DEPLOY_DIR/docker-compose.yml" pull + docker compose -f "$DEPLOY_DIR/docker-compose.yml" up -d + + - name: Health check + run: | + set -eu + for i in $(seq 1 30); do + if curl -fsS http://host.docker.internal:8088/health; then + exit 0 + fi + sleep 2 + done + docker compose -f "$DEPLOY_DIR/docker-compose.yml" ps + docker logs local-demo-app --tail=100 || true + exit 1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2207ed2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM docker.m.daocloud.io/library/python:3.12-slim + +WORKDIR /app +COPY app.py /app/app.py + +ENV PORT=8080 +EXPOSE 8080 + +CMD ["python", "/app/app.py"] diff --git a/__pycache__/app.cpython-314.pyc b/__pycache__/app.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4990737f49998e01e9472419ee86d8413624530 GIT binary patch literal 3442 zcmbVOO>7&-6`tkpl1p+)s+KLwQX)}N!gXaq_z<9%9(>H92MQu2d19aea>~uIg0^shqHmU5l2Q}{ z$q;-q@6DTe^WJ>#&AuFsgb=j<{_FDUB|k#na>i@GyX~MyK&XuFAWgW1XrmXX0KD&( z@1{t_n-Y}<(V~!X_wtrE`b^|7&pA!hq_nRgr-7fbPS*Ubb;@FJ=;zoBXo|Ns4gT;( zL#NOX(t@Xv7Frb3VpAH{!lw~c7bBWF`3^!k@aj3Vs1BiYLjes!B!=C8GV&dl%_e=U z=XHaaH|ORun)zYTB=n|X=PVPm?zt6g8o9i^%qQP6@kbz3Udv?k+1K8jDJeN~%^5KY z1s3MA`5Eo4TeoMKY&luOVp8U|HpisNvB|M))41C;9Y6<@z#4Eli--yuqP{y}D)t}; z4hJqsnh&U~i9r3D1T>(@RGCLKsQHIcGQa}OQ)ayl`R+mZLoAeo|G}V)=23=Ak2DRr zu^HyE0gYztI+gBxr3so(6Bi^;s!0nTrcg@sZUdqz5C4=EziZ`1Zl%4CinKs`C*YKE zsUW~9_{op7J73KUKeBYpX|O_eQ@C)AJm54 zt*h@nR8M@<)D-LC{zp9npTAohOn>>_{Wl++sC8f4xYk~LvaTk$zM@uQo3W}`UEcb% z_WZ=Il0E>FDBH|;K1STf07sYxgECrZq3?Jkd3iS_z+I!aPn>U|2q7*T7rl&bWM=5>X7U=_0Mvs(8HNDqAy4^pz+dp=H_;2TG(aXEaG=2`Ivze1owfKUb>xMx zxAQ`)p3k?U?%8PP8Q6+>E84wxkFK4rw%i+Ky1h}mZ(D)8*q{z3 zPvs(A8ku~mUUh70+H_=YykIk%+s1!>72y2@h!PlF$!wLVy6yV%?R}pGwu2?ylBj z7xrRTwqsWwoZgAu+K~1Ybx-NrR{E;HsVgtIT<^BhTOFt+FV>YyEkW%}^4`QfazFjW z`WMr6WvVq%SNdRF32lb<6nEvxU1i`I09IW9&-*g1#Dya^n}$3%xS;GzfTqR~P#ylJ@lJ7QpB9Y0)1rdH(D9KQfHe+4f?Owo1QSTl7U zo-=*T$rUX=R(1WuqG2^>V!FPR$AnsW+q4~B$GoWG7dYWLfnC2&=}@;Jk+Zr%p?<$# zq$Yu#ex>VK%OE6|&(f@ES-K8&A<`j)WgMVa8ac-A9&^I~WB|y2K7+74@rC49pY#gy z@1FSm^5DO_fILP(T>QHWvY^{)9Eu#&2Y&$4`F3e&p;gBxeC@9l4}b;K0a*SPxD8%3 zp}p?#TLyr9CSWxmKu;_M#zx&=sV8pb2~Ss|5n+cLmsuT%$TMX$`Q9XJE~F_!Ev#s7I0bR`T9lZD{<<8#|FpCNftxvkZm@GE6yKiXAU-CV8Mn|5{a?v0&jx*UEO z2yKkFG!t8iop5p|aIP%<;2#hMYtdi-fS`Z!x*!Vuk5we88>PB<=Aoqg@%r~7Y6jr{ Dm=dBg literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..b706dc7 --- /dev/null +++ b/app.py @@ -0,0 +1,51 @@ +import json +import os +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + + +APP_NAME = os.environ.get("APP_NAME", "demo-app") +APP_VERSION = os.environ.get("APP_VERSION", "local") +PORT = int(os.environ.get("PORT", "8080")) + + +class Handler(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/health": + self._send_json(200, {"status": "ok", "app": APP_NAME, "version": APP_VERSION}) + return + + if self.path == "/": + body = f"{APP_NAME} is running\nversion={APP_VERSION}\n" + self._send_text(200, body) + return + + self._send_json(404, {"error": "not found"}) + + def log_message(self, fmt, *args): + print("%s - %s" % (self.address_string(), fmt % args)) + + def _send_json(self, status, payload): + body = json.dumps(payload, separators=(",", ":")).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _send_text(self, status, body_text): + body = body_text.encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "text/plain; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + +def main(): + server = ThreadingHTTPServer(("0.0.0.0", PORT), Handler) + print(f"{APP_NAME} listening on 0.0.0.0:{PORT}") + server.serve_forever() + + +if __name__ == "__main__": + main()