name: build on: push: branches: - 'master' jobs: quality-checks: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: lts/* - name: Enable pnpm run: corepack enable - name: Install frontend dependencies working-directory: ui run: pnpm install --frozen-lockfile - name: Run frontend lint working-directory: ui run: pnpm run lint - name: Build frontend working-directory: ui run: pnpm run build - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.13' - name: Run backend smoke checks run: python -m compileall app - name: Run backend tests run: python -m unittest discover -s app/tests -p "test_*.py" - name: Run Trivy filesystem scan uses: aquasecurity/trivy-action@0.35.0 with: scan-type: fs scan-ref: . format: table severity: CRITICAL,HIGH dockerhub-build-push: needs: quality-checks runs-on: ubuntu-latest steps: - name: Get current date id: date run: echo "date=$(date +'%Y.%m.%d')" >> "$GITHUB_OUTPUT" - name: Checkout uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Login to DockerHub uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64 push: true build-args: | VERSION=${{ steps.date.outputs.date }} tags: | ${{ secrets.DOCKERHUB_REPOSITORY }}:latest ${{ secrets.DOCKERHUB_REPOSITORY }}:${{ steps.date.outputs.date }} ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:${{ steps.date.outputs.date }} dockerhub-sync-readme: needs: dockerhub-build-push runs-on: ubuntu-latest steps: - name: Sync README uses: docker://lsiodev/readme-sync:latest env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} GIT_REPOSITORY: ${{ github.repository }} DOCKER_REPOSITORY: ${{ secrets.DOCKERHUB_REPOSITORY }} GIT_BRANCH: master with: entrypoint: node args: /opt/docker-readme-sync/sync create-release: needs: dockerhub-build-push runs-on: ubuntu-latest steps: - name: Get current date id: date run: echo "date=$(date +'%Y.%m.%d')" >> $GITHUB_OUTPUT - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Get commits since last release id: commits run: | # Fetch all tags git fetch --tags # Get the last tag (sorted by version, using date format YYYY.MM.DD) LAST_TAG=$(git tag -l --sort=-version:refname | grep -E '^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$' | head -n 1) if [ -z "$LAST_TAG" ]; then # No previous release, skip commits for first release COMMITS="" echo "has_commits=false" >> $GITHUB_OUTPUT else # Get commits since last tag COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges) if [ -z "$COMMITS" ]; then echo "has_commits=false" >> $GITHUB_OUTPUT else echo "has_commits=true" >> $GITHUB_OUTPUT fi fi # Escape for use in YAML/multiline output { echo 'commits<> $GITHUB_OUTPUT # Also output for debugging echo "Last tag: ${LAST_TAG:-none}" echo "Commits since last release:" echo "$COMMITS" - name: Generate release body id: release_body env: DOCKERHUB_REPO: ${{ secrets.DOCKERHUB_REPOSITORY }} GHCR_REPO: ghcr.io/${{ github.repository }} DATE: ${{ steps.date.outputs.date }} HAS_COMMITS: ${{ steps.commits.outputs.has_commits }} COMMITS: ${{ steps.commits.outputs.commits }} run: | { echo '## Docker Images' echo '' echo 'Docker images have been built and pushed:' echo '' echo '**Docker Hub:**' echo "- \`${DOCKERHUB_REPO}:latest\`" echo "- \`${DOCKERHUB_REPO}:${DATE}\`" echo '' echo '**GitHub Container Registry:**' echo "- \`${GHCR_REPO}:latest\`" echo "- \`${GHCR_REPO}:${DATE}\`" if [ "$HAS_COMMITS" = "true" ] && [ -n "$COMMITS" ]; then echo '' echo '## Changes' echo '' echo "$COMMITS" fi } > release_body.txt { echo 'body<> $GITHUB_OUTPUT - name: Delete existing release if present env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG_NAME: ${{ steps.date.outputs.date }} run: | # Check if release exists and delete it if gh release view "$TAG_NAME" &>/dev/null; then echo "Release $TAG_NAME already exists, deleting it..." gh release delete "$TAG_NAME" --yes || true fi # Fetch tags to check remote git fetch --tags # Check if tag exists (locally or remotely) and delete it if git rev-parse "$TAG_NAME" &>/dev/null 2>&1 || git ls-remote --tags origin "$TAG_NAME" | grep -q "$TAG_NAME"; then echo "Tag $TAG_NAME already exists, deleting it..." git tag -d "$TAG_NAME" 2>/dev/null || true git push origin ":refs/tags/$TAG_NAME" || true fi - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.date.outputs.date }} name: Release ${{ steps.date.outputs.date }} body_path: release_body.txt draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}