diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9701d96..fd664b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,32 +8,173 @@ on: types: [opened, reopened, synchronize] jobs: + meta: + name: Generate and check metadata + runs-on: ubuntu-latest + outputs: + environment: ${{ steps.environment.outputs.environment }} + labels: ${{ steps.meta.outputs.labels }} + tag: ${{ steps.meta.outputs.tags }} + tag_names: ${{ steps.meta.outputs.tag_names }} + tag_exists: ${{ steps.check-image.outputs.tag_exists }} + json: ${{ steps.meta.outputs.json }} + version: ${{ steps.meta.outputs.version }} + steps: + - name: Set environment variable based on branch + id: environment + run: | + if [[ "${{ github.ref_name }}" == "main" ]]; then + echo "environment=main" >> "$GITHUB_OUTPUT" + elif [[ "${{ github.ref_name }}" == "dev" ]]; then + echo "environment=dev" >> "$GITHUB_OUTPUT" + fi + - name: Docker meta + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ vars.REGISTRY_IMAGE }} + tags: | + type=sha,format=long + - name: Login to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Check if image exists already + id: check-image + run: | + if docker manifest inspect ${{ steps.meta.outputs.tags }} > /dev/null 2>&1; then + echo "tag_exists=true" >> "$GITHUB_OUTPUT" + else + echo "tag_exists=false" >> "$GITHUB_OUTPUT" + fi + test: name: Test + needs: meta runs-on: ubuntu-latest - container: node:24.14.1-trixie + if: ${{ needs.meta.outputs.tag_exists == 'false' }} + container: node:22.19.0-bookworm services: db: - image: postgres:18.3 + image: postgres:17.6 env: POSTGRES_PASSWORD: postgres minio: image: minio/minio:RELEASE.2025-09-07T16-13-09Z steps: - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up npm cache - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 22 cache: 'npm' - name: Install Dependencies run: npm ci - name: Generate Prisma client - run: npm run prisma:generate -w server - env: - DATABASE_URL: postgresql://postgres:postgres@db:5432/app + run: DATABASE_URL=test npm run prisma:generate -w server - name: Run ESLint - run: npm run lint + run: npm run lint:check - name: Run Tests run: cp server/example.env server/.env && npm test + - name: Build client + run: npm run build -w client + + build: + name: Build Image + needs: [ meta, test ] + strategy: + fail-fast: false + matrix: + runner: + - ubuntu-24.04 + - ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + if: ${{ github.ref_name == 'dev' || github.ref_name == 'main' }} + steps: + - name: Prepare + run: | + if [[ ${{ matrix.runner }} == "ubuntu-24.04-arm" ]]; then + platform=linux/arm64 + else + platform=linux/amd64 + fi + echo "PLATFORM=${platform}" >> $GITHUB_ENV + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + - name: Set up docker buildx + uses: docker/setup-buildx-action@v3 + - name: Login to docker hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + platforms: ${{ env.PLATFORM }} + labels: ${{ needs.meta.outputs.labels }} + tags: ${{ vars.REGISTRY_IMAGE }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: [ meta, build ] + env: + DOCKER_METADATA_OUTPUT_VERSION: ${{ needs.meta.outputs.version }} + DOCKER_METADATA_OUTPUT_TAGS: ${{ needs.meta.outputs.tag }} + DOCKER_METADATA_OUTPUT_TAG_NAMES: ${{ needs.meta.outputs.tag_names }} + DOCKER_METADATA_OUTPUT_LABELS: ${{ needs.meta.outputs.labels }} + DOCKER_METADATA_OUTPUT_JSON: ${{ needs.meta.outputs.json }} + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ vars.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ needs.meta.outputs.tag }} + + deploy: + name: Deploy + needs: [ meta, merge ] + if: | + ${{ always() + && (needs.meta.outputs.tag_exists == 'true' || needs.merge.result == 'success') + && vars.DOKKU_GIT_REMOTE_URL != '' }} + runs-on: ubuntu-latest + environment: ${{ needs.meta.outputs.environment }} + steps: + - name: Push to dokku + uses: dokku/github-action@master + with: + git_remote_url: ${{ vars.DOKKU_GIT_REMOTE_URL }} + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + deploy_docker_image: ${{ needs.meta.outputs.tag }} diff --git a/client/package.json b/client/package.json index e44a698..1b95a47 100644 --- a/client/package.json +++ b/client/package.json @@ -9,6 +9,7 @@ "build:client": "vite build --outDir dist/client", "build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server", "lint": "eslint --fix", + "lint:check": "eslint", "preview": "vite preview", "test": "" }, diff --git a/package.json b/package.json index 0c53328..48502b3 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "scripts": { "dev": "cp -n server/example.env server/.env; cp server/.env client/.env; nf -j Procfile.dev start", "lint": "npm run lint --workspaces", + "lint:check": "npm run lint:check --workspaces", "test": "npm test --workspaces" }, "devDependencies": { diff --git a/server/package.json b/server/package.json index 002f5f5..fbd9184 100644 --- a/server/package.json +++ b/server/package.json @@ -9,6 +9,7 @@ "scripts": { "dev": "prisma migrate deploy; prisma db push --accept-data-loss; npm run prisma:generate; fastify start -w -l info -P app.js", "lint": "eslint --fix", + "lint:check": "eslint", "pretest": "eslint", "prisma:generate": "prisma generate; tsc", "prisma:studio": "prisma studio",