fix(server): provision first-login git provider users (#971) #2228
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["**"] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| detect-changes: | |
| name: "Detect changes" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| contents: read | |
| outputs: | |
| webapp: ${{ steps.filter.outputs.webapp }} | |
| application-server: ${{ steps.filter.outputs.application-server }} | |
| intelligence-service: ${{ steps.filter.outputs.intelligence-service }} | |
| webhook-ingest: ${{ steps.filter.outputs.webhook-ingest }} | |
| agent-images: ${{ steps.filter.outputs.agent-images }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| ci-config: ${{ steps.filter.outputs.ci-config }} | |
| any-code: ${{ steps.filter.outputs.webapp == 'true' || steps.filter.outputs.application-server == 'true' || steps.filter.outputs.intelligence-service == 'true' || steps.filter.outputs.webhook-ingest == 'true' || steps.filter.outputs.agent-images == 'true' }} | |
| should_skip: ${{ steps.skip_check.outputs.should_skip }} | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - id: skip_check | |
| uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5 | |
| with: | |
| do_not_skip: '["workflow_dispatch", "push", "merge_group"]' | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 | |
| id: filter | |
| with: | |
| filters: | | |
| webapp: | |
| - 'webapp/**' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - '.node-version' | |
| application-server: | |
| - 'server/application-server/**' | |
| - 'scripts/db-utils.sh' | |
| intelligence-service: | |
| - 'server/intelligence-service/**' | |
| - 'scripts/install-platform-binaries.mjs' | |
| - 'scripts/generate-mermaid-erd.ts' | |
| - 'scripts/postprocess-openapi-java.ts' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| webhook-ingest: | |
| - 'server/webhook-ingest/**' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| agent-images: | |
| - 'docker/agents/**' | |
| docs: | |
| - 'docs/**' | |
| ci-config: | |
| - '.github/workflows/**' | |
| - '.github/actions/**' | |
| # Post preview links immediately (no waiting for other jobs) | |
| preview-links: | |
| name: "Preview / Coolify" | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| permissions: | |
| statuses: write | |
| timeout-minutes: 1 | |
| steps: | |
| - name: Post Coolify preview link | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| with: | |
| script: | | |
| const sha = context.payload.pull_request.head.sha; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: sha, | |
| state: 'success', | |
| target_url: 'https://coolify.hephaestus.aet.cit.tum.de/project/lc8sows0ok8g880sg4o4o84w/environment/ek8o8800g4wks84c8w8wcckc/application/wg44k0ccsoscsgcco8swc4ks/preview-deployments', | |
| description: 'Click Details to view Coolify preview deployments', | |
| context: 'Preview / Coolify' | |
| }); | |
| Quality: | |
| uses: ./.github/workflows/ci-quality-gates.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| intelligence_service_changed: ${{ (needs.detect-changes.outputs.intelligence-service == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| webhook_ingest_changed: ${{ (needs.detect-changes.outputs.webhook-ingest == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| Security: | |
| uses: ./.github/workflows/ci-security-scan.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| Test: | |
| uses: ./.github/workflows/ci-tests.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| intelligence_service_changed: ${{ (needs.detect-changes.outputs.intelligence-service == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| webhook_ingest_changed: ${{ (needs.detect-changes.outputs.webhook-ingest == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| Docker: | |
| uses: ./.github/workflows/ci-docker-build.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| intelligence_service_changed: ${{ (needs.detect-changes.outputs.intelligence-service == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| webhook_ingest_changed: ${{ (needs.detect-changes.outputs.webhook-ingest == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| agent_images_changed: ${{ (needs.detect-changes.outputs.agent-images == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| all-ci-passed: | |
| name: "CI Status Gate" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| statuses: write | |
| needs: [detect-changes, Quality, Security, Test, Docker] | |
| if: always() | |
| steps: | |
| - name: Generate workflow timeline | |
| uses: Kesin11/actions-timeline@7c7e0821d38f27460f4a71ef874a1c8cf23602e4 # v2 | |
| with: | |
| show-waiting-runner: true | |
| - name: Evaluate CI results | |
| id: evaluate | |
| run: | | |
| echo "detect-changes: ${{ needs.detect-changes.result }}" | |
| echo "Quality: ${{ needs.Quality.result }}" | |
| echo "Security: ${{ needs.Security.result }}" | |
| echo "Test: ${{ needs.Test.result }}" | |
| echo "Docker: ${{ needs.Docker.result }}" | |
| if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then | |
| echo "status=failure" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| echo "status=cancelled" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| echo "status=success" >> $GITHUB_OUTPUT | |
| - name: Generate CI Summary | |
| if: always() | |
| run: | | |
| # Header | |
| echo "## 🔍 CI Pipeline Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Overall status | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "success" ]]; then | |
| echo "✅ **All checks passed!**" >> $GITHUB_STEP_SUMMARY | |
| elif [[ "${{ steps.evaluate.outputs.status }}" == "failure" ]]; then | |
| echo "❌ **Some checks failed.** See details below." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **CI was cancelled or encountered an issue.**" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Workflow results table | |
| echo "### Workflow Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Workflow | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY | |
| # Map result to emoji | |
| result_to_emoji() { | |
| case "$1" in | |
| success) echo "✅ Passed" ;; | |
| failure) echo "❌ Failed" ;; | |
| skipped) echo "⏭️ Skipped" ;; | |
| cancelled) echo "🚫 Cancelled" ;; | |
| *) echo "❓ Unknown" ;; | |
| esac | |
| } | |
| echo "| Quality | $(result_to_emoji '${{ needs.Quality.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test | $(result_to_emoji '${{ needs.Test.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Security | $(result_to_emoji '${{ needs.Security.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Docker | $(result_to_emoji '${{ needs.Docker.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Path filtering info | |
| echo "### 📁 Components Changed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Component | Changed |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----------|---------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp | ${{ needs.detect-changes.outputs.webapp == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application Server | ${{ needs.detect-changes.outputs.application-server == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Intelligence Service | ${{ needs.detect-changes.outputs.intelligence-service == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webhook Ingest | ${{ needs.detect-changes.outputs.webhook-ingest == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Agent Images | ${{ needs.detect-changes.outputs.agent-images == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| CI Config | ${{ needs.detect-changes.outputs.ci-config == 'true' && '⚙️ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Tips section for failures - specific to what failed | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "failure" ]]; then | |
| echo "### 💡 Troubleshooting Guide" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Quality failures | |
| if [[ "${{ needs.Quality.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Quality Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Issue | Fix Command |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Formatting errors | \`npm run format\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| TypeScript errors | \`npm run typecheck:webapp\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Biome lint errors | \`npm run lint:fix\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Java formatting | \`npm run format:java\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| OpenAPI out of sync | \`npm run generate:api\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Database schema drift | \`npm run db:draft-changelog\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Test failures | |
| if [[ "${{ needs.Test.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Tests Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test Suite | Run Locally |" >> $GITHUB_STEP_SUMMARY | |
| echo "|------------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp unit | \`npm run test:webapp\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp Storybook | \`npm -w webapp run test:storybook\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application server | \`cd server/application-server && ./mvnw test\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Intelligence service | \`npm -w server/intelligence-service run test\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webhook ingest | \`npm -w server/webhook-ingest run test\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Tip:** Check the **Test Results** tab above for specific failures." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Docker failures | |
| if [[ "${{ needs.Docker.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Docker Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Common causes:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build errors in the application code" >> $GITHUB_STEP_SUMMARY | |
| echo "- Missing dependencies" >> $GITHUB_STEP_SUMMARY | |
| echo "- Dockerfile syntax errors" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Test locally: \`docker build -f <component>/Dockerfile .\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Security failures | |
| if [[ "${{ needs.Security.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Security Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Check the **Security** tab for details on vulnerabilities." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Quick fix:** Run \`npm run format && npm run check\` before pushing." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Performance note for successful runs | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "success" ]]; then | |
| echo "### ⚡ Performance" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| SKIPPED_COUNT=0 | |
| [[ "${{ needs.Quality.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Test.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Security.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Docker.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| if [[ $SKIPPED_COUNT -gt 0 ]]; then | |
| echo "🚀 **Path-based filtering saved time!** $SKIPPED_COUNT workflow(s) skipped because no relevant files changed." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "All workflows ran (CI config or main branch push)." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "*Generated by CI Status Gate • [View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})*" >> $GITHUB_STEP_SUMMARY | |
| - name: Create commit status | |
| if: always() | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| with: | |
| script: | | |
| const sha = context.payload.pull_request?.head?.sha || context.sha; | |
| const state = '${{ steps.evaluate.outputs.status }}' || 'failure'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: sha, | |
| state: state, | |
| description: state === 'success' ? 'All CI checks passed' : 'One or more CI checks failed', | |
| context: 'All CI Passed' | |
| }); |