diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 19afaec..e1ac90c 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -54,11 +54,17 @@ jobs: - name: Build eventhub image run: | set -euo pipefail + APP_VERSION="$(tr -d '[:space:]' < VERSION 2>/dev/null || echo 0.0)" + GIT_SHA="${GITHUB_SHA:0:12}" + BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" bash scripts/retry-docker-build.sh 5 60 -- \ docker buildx build \ --network=host \ --pull \ --file docker/Dockerfile \ + --build-arg "EVENTHUB_VERSION=${APP_VERSION}" \ + --build-arg "EVENTHUB_GIT_SHA=${GIT_SHA}" \ + --build-arg "EVENTHUB_BUILT_AT=${BUILT_AT}" \ --tag eventhub:latest \ --load \ --provenance=false \ diff --git a/.gitea/workflows/deploy-stage.yml b/.gitea/workflows/deploy-stage.yml index 0627971..54b7d19 100644 --- a/.gitea/workflows/deploy-stage.yml +++ b/.gitea/workflows/deploy-stage.yml @@ -1,40 +1,45 @@ name: Deploy stage (core) +# Auto после успешного CI на master: тот же sha-*, плюс floating :stage. on: - push: - tags: - - 'v*' + workflow_run: + workflows: [CI] + types: [completed] + branches: [master, main] workflow_dispatch: - inputs: - release_tag: - description: 'Release tag (например v0.1.1)' - required: true - default: 'v0.1.1' - sha_tag: - description: 'Образ sha-* (например sha-3f5dde498652)' - required: true - default: 'sha-3f5dde498652' + +concurrency: + group: eventhub-back-deploy-stage + cancel-in-progress: false env: REGISTRY: git.sabilin.com/eventhub + BUILDKIT_PROGRESS: quiet + DOCKER_BUILDKIT: 1 jobs: deploy-stage-core: + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest + timeout-minutes: 90 steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} + fetch-depth: 2 - - name: Compute image tags + - name: Compute image tag id: meta run: | set -euo pipefail - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "sha_tag=${{ github.event.inputs.sha_tag }}" >> "$GITHUB_OUTPUT" - echo "release_tag=${{ github.event.inputs.release_tag }}" >> "$GITHUB_OUTPUT" + if [ "${{ github.event_name }}" = "workflow_run" ]; then + SHA="${{ github.event.workflow_run.head_sha }}" else - echo "sha_tag=sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" - echo "release_tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + SHA="${GITHUB_SHA}" fi + echo "tag=sha-${SHA:0:12}" >> "$GITHUB_OUTPUT" - name: Wait for Docker daemon run: bash scripts/ensure-docker-daemon.sh 240 @@ -49,14 +54,24 @@ jobs: username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASSWORD }} - - name: Promote images (no rebuild) + - name: Verify core image from CI + run: | + set -euo pipefail + TAG="${{ steps.meta.outputs.tag }}" + if ! docker buildx imagetools inspect "${REGISTRY}/eventhub:${TAG}" >/dev/null 2>&1; then + echo "Образ eventhub:${TAG} не найден — CI push не завершился." + exit 1 + fi + echo "OK: eventhub:${TAG}" + + - name: Promote sha → :stage (no rebuild) env: REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: | bash scripts/clone-devops-run.sh scripts/promote-images.sh \ - "${{ steps.meta.outputs.sha_tag }}" \ - "${{ steps.meta.outputs.release_tag }}" \ + "${{ steps.meta.outputs.tag }}" \ + "${{ steps.meta.outputs.tag }}" \ stage \ eventhub traefik-coraza fallback bot-emulator-users observer-web logrotate @@ -69,7 +84,7 @@ jobs: script: | export REGISTRY_USER='${{ secrets.REGISTRY_USER }}' export REGISTRY_PASSWORD='${{ secrets.REGISTRY_PASSWORD }}' - bash /opt/eventhub-stage/devops/scripts/deploy-service.sh stage core ${{ steps.meta.outputs.release_tag }} + bash /opt/eventhub-stage/devops/scripts/deploy-service.sh stage core ${{ steps.meta.outputs.tag }} - name: Prune old registry images continue-on-error: true diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..49d5957 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1 diff --git a/docker/Dockerfile b/docker/Dockerfile index 35348ca..0d2b150 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -35,6 +35,14 @@ RUN collect-runtime-libs.sh /app/release /opt/runtime-libs # ============================================================ FROM ${ERLANG_RUNTIME_IMAGE} +ARG EVENTHUB_VERSION=0.0 +ARG EVENTHUB_GIT_SHA=unknown +ARG EVENTHUB_BUILT_AT= + +ENV EVENTHUB_VERSION=${EVENTHUB_VERSION} \ + EVENTHUB_GIT_SHA=${EVENTHUB_GIT_SHA} \ + EVENTHUB_BUILT_AT=${EVENTHUB_BUILT_AT} + COPY --from=builder /app/release /app # Доп. .so, если версия apk-openssl на runtime не совпала с builder (страховка) COPY --from=builder /opt/runtime-libs/ / diff --git a/src/eventhub_app.erl b/src/eventhub_app.erl index 201c476..3ac444b 100644 --- a/src/eventhub_app.erl +++ b/src/eventhub_app.erl @@ -119,6 +119,7 @@ start_admin_http() -> {'_', [ % ================== БАЗОВЫЕ ================== {"/admin/health", admin_handler_health, []}, + {"/v1/admin/health", admin_handler_health, []}, {"/v1/admin/stats", admin_handler_stats, []}, {"/v1/admin/nodes/metrics", admin_handler_node_metrics, []}, {"/v1/admin/login", admin_handler_login, []}, diff --git a/src/handlers/admin/admin_handler_health.erl b/src/handlers/admin/admin_handler_health.erl index 9722999..976c087 100644 --- a/src/handlers/admin/admin_handler_health.erl +++ b/src/handlers/admin/admin_handler_health.erl @@ -8,7 +8,7 @@ init(Req, _State) -> case cowboy_req:method(Req) of <<"GET">> -> - handler_utils:send_json(Req, 200, #{status => <<"ok">>}); + handler_utils:send_json(Req, 200, eventhub_build_info:info()); _ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>) end. @@ -19,7 +19,7 @@ trails() -> #{ path => <<"/admin/health">>, method => <<"GET">>, - description => <<"Admin API health check">>, + description => <<"Admin API health check with build identity">>, tags => [<<"Health">>], responses => #{ 200 => #{ @@ -27,10 +27,14 @@ trails() -> content => #{<<"application/json">> => #{schema => #{ type => object, properties => #{ - status => #{type => string} + status => #{type => string}, + service => #{type => string}, + version => #{type => string}, + git_sha => #{type => string}, + built_at => #{type => string} } }}} } } } - ]. \ No newline at end of file + ]. diff --git a/src/handlers/handler_health.erl b/src/handlers/handler_health.erl index f19c273..017ea5c 100644 --- a/src/handlers/handler_health.erl +++ b/src/handlers/handler_health.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @doc Обработчик проверки здоровья клиентского API. -%%% GET – возвращает статус сервера. +%%% GET – возвращает статус сервера и build identity. %%% @end %%%------------------------------------------------------------------- -module(handler_health). @@ -21,7 +21,7 @@ trails() -> #{ path => <<"/health">>, method => <<"GET">>, - description => <<"API health check">>, + description => <<"API health check with build identity">>, tags => [<<"Health">>], responses => #{ 200 => #{ @@ -29,7 +29,11 @@ trails() -> content => #{<<"application/json">> => #{schema => #{ type => object, properties => #{ - status => #{type => string} + status => #{type => string}, + service => #{type => string}, + version => #{type => string}, + git_sha => #{type => string}, + built_at => #{type => string} } }}} } @@ -46,7 +50,7 @@ trails() -> handle(Req, _Opts) -> case cowboy_req:method(Req) of <<"GET">> -> - handler_utils:send_json(Req, 200, #{status => <<"ok">>}); + handler_utils:send_json(Req, 200, eventhub_build_info:info()); _ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>) - end. \ No newline at end of file + end. diff --git a/src/infra/eventhub_build_info.erl b/src/infra/eventhub_build_info.erl new file mode 100644 index 0000000..221bac9 --- /dev/null +++ b/src/infra/eventhub_build_info.erl @@ -0,0 +1,29 @@ +-module(eventhub_build_info). +-export([info/0, version/0, git_sha/0, built_at/0]). + +%% @doc Runtime build identity from env (baked at image build or set in compose). +-spec info() -> map(). +info() -> + #{ + status => <<"ok">>, + service => <<"eventhub">>, + version => version(), + git_sha => git_sha(), + built_at => built_at() + }. + +-spec version() -> binary(). +version() -> env("EVENTHUB_VERSION", <<"0.0">>). + +-spec git_sha() -> binary(). +git_sha() -> env("EVENTHUB_GIT_SHA", <<"unknown">>). + +-spec built_at() -> binary(). +built_at() -> env("EVENTHUB_BUILT_AT", <<"">>). + +env(Name, Default) -> + case os:getenv(Name) of + false -> Default; + [] -> Default; + Value -> unicode:characters_to_binary(Value) + end.