name: Docker images on: push: branches: - main workflow_dispatch: env: REGISTRY: git.shx.one GITEA_SERVER_URL: https://git.shx.one jobs: prepare-release: runs-on: ubuntu-latest outputs: should_release: ${{ steps.release.outputs.should_release }} version: ${{ steps.release.outputs.version }} tag: ${{ steps.release.outputs.tag }} bump: ${{ steps.release.outputs.bump }} release_url: ${{ steps.release.outputs.release_url }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Compute release metadata id: release shell: bash env: GITEA_REPOSITORY: ${{ gitea.repository }} GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }} BASE_VERSION: "1.0.0" RELEASE_OUTPUT_DIR: .ci/release run: | set -euo pipefail node .ci/scripts/compute-release.mjs test -f .ci/release/release-manifest.json backend-image: needs: prepare-release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Configure backend image name shell: bash run: | set -euo pipefail owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]') name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]') stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//') echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-backend" >> "${GITHUB_ENV}" - name: Prepare backend build context shell: bash run: | set -euo pipefail STAGING=".ci/docker/backend" rm -rf "$STAGING" mkdir -p "$STAGING/packages/contracts" "$STAGING/backend" cp package.json package-lock.json "$STAGING/" cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/" cp -R packages/contracts/src "$STAGING/packages/contracts/" cp backend/package.json backend/tsconfig.json "$STAGING/backend/" cp -R backend/src "$STAGING/backend/" if [ -d backend/drizzle ]; then cp -R backend/drizzle "$STAGING/backend/" fi - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Gitea Registry uses: docker/login-action@v3 with: registry: git.shx.one username: ${{ gitea.actor }} password: ${{ secrets.ACTIONS_PAT }} - name: Build and push backend image uses: docker/build-push-action@v5 with: context: .ci/docker/backend file: backend/Dockerfile platforms: linux/amd64 push: true build-args: | APP_VERSION=${{ needs.prepare-release.outputs.version }} tags: | ${{ env.IMAGE_NAME }}:latest ${{ env.IMAGE_NAME }}:${{ gitea.sha }} ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }} cache-from: type=gha,scope=backend cache-to: type=gha,mode=max,scope=backend labels: | org.opencontainers.image.title=mmapp-backend org.opencontainers.image.description=MikroTik Manager backend (Fastify) org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }} org.opencontainers.image.revision=${{ gitea.sha }} org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }} frontend-image: needs: prepare-release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Compute release metadata shell: bash env: GITEA_REPOSITORY: ${{ gitea.repository }} GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }} BASE_VERSION: "1.0.0" RELEASE_OUTPUT_DIR: .ci/release run: | set -euo pipefail node .ci/scripts/compute-release.mjs test -f .ci/release/release-manifest.json - name: Configure frontend image name shell: bash run: | set -euo pipefail owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]') name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]') stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//') echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-frontend" >> "${GITHUB_ENV}" - name: Prepare frontend build context shell: bash run: | set -euo pipefail STAGING=".ci/docker/frontend" rm -rf "$STAGING" mkdir -p "$STAGING/packages/contracts" "$STAGING/public" cp package-lock.json next.config.ts tsconfig.json postcss.config.mjs eslint.config.mjs components.json "$STAGING/" node <<'NODE' const fs = require("node:fs") const pkg = JSON.parse(fs.readFileSync("package.json", "utf8")) pkg.workspaces = ["packages/*"] fs.writeFileSync(".ci/docker/frontend/package.json", `${JSON.stringify(pkg, null, 2)}\n`) NODE cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/" cp -R packages/contracts/src "$STAGING/packages/contracts/" for dir in app components lib shared entities public hooks; do if [ -d "$dir" ]; then cp -R "$dir" "$STAGING/" fi done cp .ci/release/release-manifest.json "$STAGING/public/release-manifest.json" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Gitea Registry uses: docker/login-action@v3 with: registry: git.shx.one username: ${{ gitea.actor }} password: ${{ secrets.ACTIONS_PAT }} - name: Build and push frontend image uses: docker/build-push-action@v5 with: context: .ci/docker/frontend file: Dockerfile.frontend platforms: linux/amd64 push: true build-args: | BACKEND_INTERNAL_URL=http://backend:8000 NEXT_PUBLIC_BACKEND_URL=same-origin NEXT_PUBLIC_DEFAULT_DATA_SOURCE=live NEXT_PUBLIC_ALLOW_MOCK_DATA=false NEXT_PUBLIC_APP_VERSION=${{ needs.prepare-release.outputs.version }} NEXT_PUBLIC_RELEASE_URL=${{ needs.prepare-release.outputs.release_url }} tags: | ${{ env.IMAGE_NAME }}:latest ${{ env.IMAGE_NAME }}:${{ gitea.sha }} ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }} cache-from: type=gha,scope=frontend cache-to: type=gha,mode=max,scope=frontend labels: | org.opencontainers.image.title=mmapp-frontend org.opencontainers.image.description=MikroTik Manager frontend (Next.js) org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }} org.opencontainers.image.revision=${{ gitea.sha }} org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }} updater-image: needs: prepare-release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Configure updater image name shell: bash run: | set -euo pipefail owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]') name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]') stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//') echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-updater" >> "${GITHUB_ENV}" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Gitea Registry uses: docker/login-action@v3 with: registry: git.shx.one username: ${{ gitea.actor }} password: ${{ secrets.ACTIONS_PAT }} - name: Build and push updater image uses: docker/build-push-action@v5 with: context: deploy/updater file: deploy/updater/Dockerfile platforms: linux/amd64 push: true tags: | ${{ env.IMAGE_NAME }}:latest ${{ env.IMAGE_NAME }}:${{ gitea.sha }} ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }} cache-from: type=gha,scope=updater cache-to: type=gha,mode=max,scope=updater labels: | org.opencontainers.image.title=mmapp-updater org.opencontainers.image.description=MikroTik Manager autonomous Docker updater org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }} org.opencontainers.image.revision=${{ gitea.sha }} org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }} publish-release: needs: - prepare-release - backend-image - frontend-image - updater-image if: needs.prepare-release.outputs.should_release == 'true' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Compute release metadata shell: bash env: GITEA_REPOSITORY: ${{ gitea.repository }} GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }} BASE_VERSION: "1.0.0" RELEASE_OUTPUT_DIR: .ci/release run: | set -euo pipefail node .ci/scripts/compute-release.mjs test -f .ci/release/release_notes.md - name: Configure git author shell: bash run: | set -euo pipefail git config user.name "gitea-actions[bot]" git config user.email "gitea-actions[bot]@users.noreply.gitea" - name: Create and push release tag shell: bash env: TAG: ${{ needs.prepare-release.outputs.tag }} run: | set -euo pipefail if git rev-parse "$TAG" >/dev/null 2>&1; then echo "Tag $TAG already exists, skipping tag push" exit 0 fi git tag -a "$TAG" -m "Release $TAG" git push origin "$TAG" - name: Publish Gitea release shell: bash env: TAG: ${{ needs.prepare-release.outputs.tag }} VERSION: ${{ needs.prepare-release.outputs.version }} REPOSITORY: ${{ gitea.repository }} TOKEN: ${{ secrets.ACTIONS_PAT }} run: | set -euo pipefail owner="${REPOSITORY%%/*}" repo="${REPOSITORY#*/}" payload=$(node <<'NODE' const fs = require("node:fs") const body = fs.readFileSync(".ci/release/release_notes.md", "utf8") const payload = { tag_name: process.env.TAG, target_commitish: "main", name: process.env.VERSION, body, draft: false, prerelease: false, } process.stdout.write(JSON.stringify(payload)) NODE ) status=$(curl --silent --show-error --output /tmp/gitea-release.json --write-out "%{http_code}" \ --request POST \ --header "Authorization: token ${TOKEN}" \ --header "Content-Type: application/json" \ --data "$payload" \ "${{ env.GITEA_SERVER_URL }}/api/v1/repos/${owner}/${repo}/releases") if [ "$status" = "409" ] || [ "$status" = "422" ]; then echo "Release for ${TAG} already exists (${status}), skipping" exit 0 fi if [ "$status" -lt 200 ] || [ "$status" -ge 300 ]; then cat /tmp/gitea-release.json exit 1 fi notify-webhook: if: ${{ secrets.DEPLOY_WEBHOOK_URL != '' }} needs: - prepare-release - backend-image - frontend-image runs-on: ubuntu-latest steps: - name: Notify deploy webhook shell: bash env: WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }} run: | set -euo pipefail curl --fail --silent --show-error --request POST \ --header "Content-Type: application/json" \ --data "{\"repository\":\"${{ gitea.repository }}\",\"sha\":\"${{ gitea.sha }}\",\"ref\":\"${{ gitea.ref }}\",\"version\":\"${{ needs.prepare-release.outputs.version }}\",\"tag\":\"${{ needs.prepare-release.outputs.tag }}\",\"releaseUrl\":\"${{ needs.prepare-release.outputs.release_url }}\"}" \ "$WEBHOOK_URL"