Restructure Project Directory Layout #56
Workflow file for this run
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 | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - "source/**" | |
| types: [opened, ready_for_review, reopened, synchronize] | |
| concurrency: | |
| # Cancel old runs when a new commit is pushed to the same branch | |
| group: ci-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| env: | |
| CHANGES_DIR: changes | |
| SCRATCH_ORG_DOMAIN: apex-database-layer.com | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| SCAN_OUTPUTS: results.txt | |
| SCAN_SUMMARY: summary.md | |
| SFCA_RESULTS: sf-code-analyzer-results.json | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| claude-review: | |
| name: Claude Code Review | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| issues: read | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Run Claude Code Review | |
| id: claude-review | |
| uses: anthropics/claude-code-action@beta | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| direct_prompt: | | |
| Please review this pull request and provide feedback on: | |
| - Code quality and best practices | |
| - Potential bugs or issues | |
| - Performance considerations | |
| - Security concerns | |
| - Test coverage | |
| scan: | |
| name: Run Static Analysis | |
| if: ${{ '! github.event.pull_request.draft' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - name: Setup SF CLI | |
| uses: ./.github/actions/setup-sf-cli | |
| with: | |
| install-plugins: "true" | |
| - name: Get Changed Files | |
| run: | | |
| # Ensure this check only runs against changed files in the current PR; not the entire codebase | |
| set -euo pipefail | |
| mkdir "$CHANGES_DIR" | |
| sf sgd source delta --generate-delta --from "HEAD^" --to "HEAD" --output-dir "$CHANGES_DIR"/ --source-dir "source/" | |
| echo "Changed files:" | |
| ls "$CHANGES_DIR" | |
| - name: Scan | |
| id: scan | |
| run: | | |
| # Perform the scanning logic, via a .py script that creates various artifacts: | |
| export PATH="$HOME/sf/bin:$PATH" | |
| python3 .github/scripts/static_analysis.py \ | |
| --results-file "$SCAN_OUTPUTS" \ | |
| --sfca-output-file "$SFCA_RESULTS" \ | |
| --summary-file "$SCAN_SUMMARY" \ | |
| --target "$CHANGES_DIR" \ | |
| --threshold 5 | |
| # Save the output variables to be referenced in later steps: | |
| cat "$SCAN_OUTPUTS" >> $GITHUB_OUTPUT | |
| - name: Summarize | |
| run: | | |
| # Add the .md Summary to a PR Review, and the Step Summary; | |
| SUMMARY=$(cat "$SCAN_SUMMARY") | |
| echo "$SUMMARY" >> "$GITHUB_STEP_SUMMARY" | |
| gh pr review "$PR_NUMBER" --body "$SUMMARY" --comment | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Raw Results | |
| path: | | |
| ${{ env.SFCA_RESULTS }} | |
| - name: Report Violations | |
| if: ${{ steps.scan.outputs.num-violations-above-threshold > 0 }} | |
| run: | | |
| # If any violations above the specified threshold, post the results to the PR and fail the check: | |
| NUM_VIOLATIONS=${{ steps.scan.outputs.num-violations-above-threshold}} | |
| echo "⚠️ Detected $NUM_VIOLATIONS violations that meet/exceed the set threshold. View the job summary for more details:" | |
| echo "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| exit 1 | |
| run-unit-tests: | |
| name: Run Unit Tests | |
| if: ${{ '! github.event.pull_request.draft' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup SF CLI | |
| uses: ./.github/actions/setup-sf-cli | |
| - name: Authenticate Devhub | |
| env: | |
| CLIENT_ID: ${{ secrets.SALESFORCE_CONSUMER_KEY }} | |
| JWT_KEY: ${{ secrets.SALESFORCE_JWT_KEY }} | |
| USERNAME: ${{ secrets.SALESFORCE_DEVHUB_USERNAME }} | |
| run: | | |
| set -euo pipefail | |
| echo "${JWT_KEY}" > server.key | |
| sf org login jwt \ | |
| --client-id "$CLIENT_ID" \ | |
| --jwt-key-file server.key \ | |
| --set-default-dev-hub \ | |
| --username "$USERNAME" | |
| - name: Get Existing Scratch Org | |
| id: get-existing | |
| continue-on-error: true | |
| env: | |
| CLIENT_ID: ${{ secrets.SALESFORCE_CONSUMER_KEY }} | |
| DEV_HUB: ${{ secrets.SALESFORCE_DEVHUB_USERNAME }} | |
| JWT_KEY: ${{ secrets.SALESFORCE_JWT_KEY }} | |
| run: | | |
| # Re-use scratch orgs to prevent exceeding new scratch orgs/day limits: | |
| set -euo pipefail | |
| QUERY="SELECT LoginUrl, SignupUsername FROM ScratchOrgInfo WHERE SignupUsername LIKE '%$SCRATCH_ORG_DOMAIN' AND Status = 'Active'" | |
| echo "$QUERY" | |
| RESPONSE=$(sf data query --target-org "$DEV_HUB" -q "$QUERY" --json 2>/dev/null || true) | |
| echo "$RESPONSE" | |
| LOGIN_URL=$(echo "$RESPONSE" | jq -r '.result.records[0]?.LoginUrl // empty') | |
| USERNAME=$(echo "$RESPONSE" | jq -r '.result.records[0]?.SignupUsername // empty') | |
| if [[ -n "$USERNAME" && "$USERNAME" != "null" ]]; then | |
| echo "Signing into scratch org with username $USERNAME..." | |
| sf org login jwt \ | |
| --client-id "$CLIENT_ID" \ | |
| --instance-url "$LOGIN_URL" \ | |
| --jwt-key-file server.key \ | |
| --set-default \ | |
| --username "$USERNAME" | |
| echo "success=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create Scratch Org | |
| if: ${{ steps.get-existing.outputs.success != 'true' }} | |
| run: | | |
| # Create a new scratch org, with a dynamic username ending in the repo's domain: | |
| set -euo pipefail | |
| DATE=$(date +%y%m%d) | |
| USERNAME="$DATE-ci@$SCRATCH_ORG_DOMAIN" | |
| echo "Creating scratch org with username $USERNAME..." | |
| sf org create scratch \ | |
| --definition-file config/project-scratch-def.json \ | |
| --duration-days 2 \ | |
| --set-default \ | |
| --username "$USERNAME" \ | |
| --wait 10 | |
| - name: Run Tests | |
| run: | | |
| sf project deploy start \ | |
| --coverage-formatters json-summary \ | |
| --dry-run \ | |
| --ignore-conflicts \ | |
| --source-dir source \ | |
| --test-level RunLocalTests \ | |
| --verbose \ | |
| --wait 30 | |
| - name: Check Code Coverage | |
| run: | | |
| # Parse the JSON coverage summary file to extract overall coverage percentage | |
| COVERAGE_PERCENT=$(cat coverage/coverage/coverage-summary.json | jq -r '.total.lines.pct') | |
| echo "Code coverage: ${COVERAGE_PERCENT}%" | |
| if [ "$COVERAGE_PERCENT" != "100" ]; then | |
| echo "❌ Code coverage is ${COVERAGE_PERCENT}%, but 100% is required" | |
| exit 1 | |
| fi | |
| - name: Post Authenticate Devhub | |
| if: ${{ always() }} | |
| run: rm -f server.key |