name: CI on: push: branches: - main - develop pull_request: branches: - main - develop workflow_dispatch: permissions: contents: read packages: write concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: verify: name: Lint, Test, Build runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: home_server ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 env: DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/home_server steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js 22 uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" - name: Install dependencies run: npm ci --ignore-scripts - name: Lint run: npm run lint - name: Unit tests run: npm run test:coverage - name: Build run: NODE_ENV=production npm run build release: name: Create GitHub Release runs-on: ubuntu-latest needs: verify if: github.ref == 'refs/heads/main' && github.event_name == 'push' permissions: contents: write outputs: version: ${{ steps.read.outputs.version }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Configure Git identity run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Read version from package.json id: read run: echo "version=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT - name: Check if tag already exists id: tag_check run: | git fetch --tags if git rev-parse "v${{ steps.read.outputs.version }}" >/dev/null 2>&1; then echo "exists=true" >> $GITHUB_OUTPUT else echo "exists=false" >> $GITHUB_OUTPUT fi - name: Create and push tag if: steps.tag_check.outputs.exists == 'false' run: | git tag -a "v${{ steps.read.outputs.version }}" \ -m "Release v${{ steps.read.outputs.version }}" git push origin "v${{ steps.read.outputs.version }}" - name: Get merged PR info id: pr if: steps.tag_check.outputs.exists == 'false' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PR=$(gh api "/repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \ -H "Accept: application/vnd.github+json" | jq -c '.[0] // null') if [[ "$PR" != "null" && "$PR" != "" ]]; then echo "title=$(echo "$PR" | jq -r '.title')" >> $GITHUB_OUTPUT { echo "body<> $GITHUB_OUTPUT else echo "title=$(git log -1 --pretty=format:'%s' ${{ github.sha }})" >> $GITHUB_OUTPUT echo "body=" >> $GITHUB_OUTPUT fi - name: Create GitHub Release if: steps.tag_check.outputs.exists == 'false' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_NOTES: ${{ steps.pr.outputs.body }} run: | printf '%s' "$RELEASE_NOTES" > /tmp/release-notes.md gh release create "v${{ steps.read.outputs.version }}" \ --title "Homeio v${{ steps.read.outputs.version }}" \ --notes-file /tmp/release-notes.md \ --latest docker: name: Build & Push Docker Image runs-on: ubuntu-latest needs: [verify, release] if: github.ref == 'refs/heads/main' && github.event_name == 'push' # The arm64 half builds under QEMU, where `npm ci` sometimes hangs. A good # run takes about 20 minutes; without a limit a hang holds the runner for # the 6-hour maximum (1.9.3 and 1.9.7 got no image that way). Re-run the # job if it times out. timeout-minutes: 45 steps: - name: Checkout uses: actions/checkout@v4 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: | ghcr.io/doctor-io/homeio:latest ghcr.io/doctor-io/homeio:${{ needs.release.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max