name: Nightly Build on: schedule: # Run every night at 2 AM UTC - cron: "0 2 * * *" workflow_dispatch: inputs: force_build: description: "Force build even if no changes" required: false default: "false" type: boolean env: REGISTRY: gitea-http.taildb3494.ts.net IMAGE_NAME: will/unitforge jobs: check-changes: runs-on: ubuntu-latest outputs: should_build: ${{ steps.changes.outputs.should_build }} commit_sha: ${{ steps.changes.outputs.commit_sha }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 2 - name: Check for changes id: changes run: | # Get the latest commit from the last 24 hours YESTERDAY=$(date -d "24 hours ago" --iso-8601) RECENT_COMMITS=$(git log --since="$YESTERDAY" --format="%H" | wc -l) FORCE_BUILD="${{ github.event.inputs.force_build }}" if [[ "$FORCE_BUILD" == "true" ]] || [[ $RECENT_COMMITS -gt 0 ]]; then echo "should_build=true" >> $GITHUB_OUTPUT echo "Found $RECENT_COMMITS commits in the last 24 hours or force build requested" else echo "should_build=false" >> $GITHUB_OUTPUT echo "No changes in the last 24 hours, skipping build" fi echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT nightly-tests: needs: check-changes runs-on: ubuntu-latest if: needs.check-changes.outputs.should_build == 'true' strategy: matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout code uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v3 with: version: "latest" - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} - name: Install dependencies run: | uv venv --python ${{ matrix.python-version }} uv pip install -e ".[dev]" - name: Run comprehensive tests run: | source .venv/bin/activate # Run all checks make lint make type-check make security-check make test-cov # Additional nightly-specific tests echo "Running extended test suite..." python -m pytest tests/ -v --durations=10 --tb=short - name: Upload coverage for Python ${{ matrix.python-version }} uses: codecov/codecov-action@v3 if: matrix.python-version == '3.11' with: file: ./htmlcov/coverage.xml flags: nightly-${{ matrix.python-version }} build-nightly: needs: [check-changes, nightly-tests] runs-on: ubuntu-latest if: needs.check-changes.outputs.should_build == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: driver-opts: network=host - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} password: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - name: Generate nightly tags id: tags run: | COMMIT_SHA="${{ needs.check-changes.outputs.commit_sha }}" DATE=$(date +%Y%m%d) SHORT_SHA=${COMMIT_SHA:0:7} echo "nightly_tag=nightly-${DATE}-${SHORT_SHA}" >> $GITHUB_OUTPUT echo "nightly_latest=nightly-latest" >> $GITHUB_OUTPUT - name: Verify vendor assets run: | assets=( "frontend/static/vendor/bootstrap/css/bootstrap.min.css" "frontend/static/vendor/bootstrap/js/bootstrap.bundle.min.js" "frontend/static/vendor/fontawesome/css/all.min.css" "frontend/static/vendor/fontawesome/webfonts/fa-solid-900.woff2" "frontend/static/img/osi-logo.svg" ) for asset in "${assets[@]}"; do if [ ! -f "$asset" ]; then echo "Error: Missing required asset: $asset" exit 1 fi done echo "All vendor assets verified" - name: Build and push nightly image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tags.outputs.nightly_tag }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tags.outputs.nightly_latest }} labels: | org.opencontainers.image.title=UnitForge Nightly org.opencontainers.image.description=Nightly build of UnitForge org.opencontainers.image.version=nightly-${{ steps.tags.outputs.nightly_tag }} org.opencontainers.image.revision=${{ needs.check-changes.outputs.commit_sha }} org.opencontainers.image.created=${{ github.event.repository.pushed_at }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | BUILDKIT_INLINE_CACHE=1 performance-test: needs: [check-changes, build-nightly] runs-on: ubuntu-latest if: needs.check-changes.outputs.should_build == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Run performance tests run: | # Pull the nightly image docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-latest # Start the container docker run -d --name unitforge-perf \ -p 8000:8000 \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-latest # Wait for startup sleep 15 # Basic performance test echo "Running basic performance test..." for i in {1..10}; do curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \ http://localhost:8000/ done # Memory usage check echo "Checking memory usage..." docker stats unitforge-perf --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" # Cleanup docker stop unitforge-perf docker rm unitforge-perf security-scan-nightly: needs: [check-changes, build-nightly] runs-on: ubuntu-latest if: needs.check-changes.outputs.should_build == 'true' steps: - name: Run comprehensive security scan uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-latest format: "sarif" output: "trivy-nightly.sarif" - name: Upload security scan results uses: github/codeql-action/upload-sarif@v2 if: always() with: sarif_file: "trivy-nightly.sarif" - name: Generate security report uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-latest format: "json" output: "security-report.json" - name: Upload security report uses: actions/upload-artifact@v3 with: name: nightly-security-report path: security-report.json cleanup-old-nightlies: needs: [check-changes, build-nightly] runs-on: ubuntu-latest if: needs.check-changes.outputs.should_build == 'true' steps: - name: Clean up old nightly images run: | echo "Cleaning up nightly images older than 7 days..." # Note: This would require registry API access or container registry-specific tools # For now, we'll just log what would be cleaned CUTOFF_DATE=$(date -d "7 days ago" +%Y%m%d) echo "Would clean images tagged before: nightly-${CUTOFF_DATE}" # Add actual cleanup logic here based on your registry # Examples: # - Use registry API to list and delete old tags # - Use container registry CLI tools # - Use registry-specific cleanup policies notify-results: needs: [ check-changes, nightly-tests, build-nightly, performance-test, security-scan-nightly, ] runs-on: ubuntu-latest if: always() && needs.check-changes.outputs.should_build == 'true' steps: - name: Generate build report run: | echo "## Nightly Build Report - $(date)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Component | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Tests | ${{ needs.nightly-tests.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY echo "| Build | ${{ needs.build-nightly.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY echo "| Performance | ${{ needs.performance-test.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY echo "| Security | ${{ needs.security-scan-nightly.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [[ "${{ needs.nightly-tests.result }}" == "success" && "${{ needs.build-nightly.result }}" == "success" ]]; then echo "🌙 Nightly build completed successfully!" >> $GITHUB_STEP_SUMMARY echo "📦 Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly-latest" >> $GITHUB_STEP_SUMMARY else echo "❌ Nightly build encountered issues. Check failed jobs above." >> $GITHUB_STEP_SUMMARY fi - name: Send notification if: failure() run: | echo "🚨 Nightly build failed!" echo "Check the workflow run for details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" # Add notification logic here (webhook, email, Slack, etc.)