From fd5b44ab448420481f3aaa5abbe4af1e7c139393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:22:40 -0300 Subject: [PATCH 1/6] ci: remove static analysis job from main analysis workflow --- .github/workflows/main-analysis.yml | 118 +--------------------------- 1 file changed, 1 insertion(+), 117 deletions(-) diff --git a/.github/workflows/main-analysis.yml b/.github/workflows/main-analysis.yml index 83f8dcc..3826f13 100644 --- a/.github/workflows/main-analysis.yml +++ b/.github/workflows/main-analysis.yml @@ -103,120 +103,10 @@ jobs: path: coverage/ retention-days: 3 - - name: Upload build artifacts - uses: actions/upload-artifact@v7 - with: - name: build-artifacts - path: | - .build/debug/index/store - retention-days: 3 - - # Static Analysis - static-analysis: - runs-on: macos-26 - needs: test-and-coverage - timeout-minutes: 15 - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - - - name: Verify Swift version - run: swift --version - - - name: Cache Homebrew - uses: actions/cache@v5 - with: - path: ~/Library/Caches/Homebrew - key: ${{ runner.os }}-${{ runner.arch }}-brew-swiftlint-gitleaks-periphery - restore-keys: | - ${{ runner.os }}-${{ runner.arch }}-brew- - - - name: Install analysis tools - run: | - brew install swiftlint gitleaks periphery - - - name: Prepare directories - run: | - mkdir -p reports - - - name: Download build artifacts - uses: actions/download-artifact@v8 - with: - name: build-artifacts - path: .build/debug - - - name: Run SwiftLint - run: | - swiftlint lint \ - --reporter sonarqube \ - > reports/linter-report.json || true - - - name: Run Periphery - run: | - periphery scan \ - --skip-build \ - --index-store-path "$PWD/.build/debug/index/store" \ - --format json \ - > reports/periphery.json || true - - # Convert to SonarCloud Clean Code format - # Periphery location is a string "path:line:column" that must be parsed - if [ -f reports/periphery.json ]; then - jq --arg pwd "$PWD" '{ - rules: [{ - id: "unused-code", - name: "Unused Code", - description: "Code that is never used and can be removed", - engineId: "periphery", - cleanCodeAttribute: "FOCUSED", - impacts: [{ - softwareQuality: "MAINTAINABILITY", - severity: "HIGH" - }] - }], - issues: [.[] | - (.location | split(":")) as $loc | - ($loc[0] | ltrimstr($pwd + "/")) as $path | - ($loc[1] | tonumber) as $line | - ($loc[2] | tonumber) as $col | - { - ruleId: "unused-code", - effortMinutes: 10, - primaryLocation: { - message: "\(.kind) \(.name) is \(.hints | join(", "))", - filePath: $path, - textRange: { - startLine: $line, - startColumn: $col, - endLine: $line, - endColumn: ($col + 1) - } - } - } - ] - }' reports/periphery.json > reports/dead-code-report.json - fi - - - name: Run Gitleaks - run: | - gitleaks detect \ - --report-path reports/leaks-report.sarif \ - --report-format sarif || true - - - name: Upload analysis reports - uses: actions/upload-artifact@v7 - with: - name: reports - path: reports/ - retention-days: 3 - # Publish Code Analysis publish-code-analysis: runs-on: ubuntu-latest - needs: [test-and-coverage, static-analysis] + needs: test-and-coverage env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -232,12 +122,6 @@ jobs: name: coverage path: coverage - - name: Download reports artifacts - uses: actions/download-artifact@v8 - with: - name: reports - path: reports - - name: Compute project version run: | TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) From fdd8b302855b14f48977f77a4847f7d76b4e9fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:22:44 -0300 Subject: [PATCH 2/6] ci: simplify pull request workflow to run swift test only --- .github/workflows/pull-request-analysis.yml | 259 +------------------- 1 file changed, 3 insertions(+), 256 deletions(-) diff --git a/.github/workflows/pull-request-analysis.yml b/.github/workflows/pull-request-analysis.yml index 94b1737..2a98ded 100644 --- a/.github/workflows/pull-request-analysis.yml +++ b/.github/workflows/pull-request-analysis.yml @@ -13,11 +13,9 @@ concurrency: permissions: contents: read - pull-requests: write jobs: - # Build And Test - test-and-coverage: + test: runs-on: macos-26 timeout-minutes: 30 if: github.actor != 'dependabot[bot]' @@ -38,256 +36,5 @@ jobs: restore-keys: | ${{ runner.os }}-${{ runner.arch }}-spm- - - name: Run tests and generate coverage - run: | - set -e - - echo "Running tests with coverage..." - swift test --enable-code-coverage --quiet - - BIN_PATH=$(swift build --show-bin-path) - - TEST_BINARY=$(find "$BIN_PATH" -type f \ - -path "*Tests.xctest/Contents/MacOS/*" \ - ! -path "*.dSYM/*" \ - | head -n 1) - - if [ -z "$TEST_BINARY" ]; then - echo "Test binary not found" - exit 1 - fi - - PROFDATA="$BIN_PATH/codecov/default.profdata" - - mkdir -p coverage - - xcrun llvm-cov export \ - "$TEST_BINARY" \ - -instr-profile "$PROFDATA" \ - --sources "$(pwd)/Sources" \ - --format=lcov \ - | sed "s|$(pwd)/||g" \ - > coverage/lcov.info - - TOTAL_LINES=$(grep -c "^DA:" coverage/lcov.info || echo 0) - COVERED_LINES=$(grep "^DA:" coverage/lcov.info | awk -F',' '$2 > 0' | wc -l | tr -d ' ') - - if [ "$TOTAL_LINES" -gt 0 ]; then - echo "scale=2; $COVERED_LINES * 100 / $TOTAL_LINES" | bc - else - echo "0.00" - fi > coverage/lines-percent.txt - - - name: Upload coverage artifact - uses: actions/upload-artifact@v7 - with: - name: coverage - path: | - coverage/lcov.info - coverage/lines-percent.txt - retention-days: 1 - - - name: Upload build artifacts - uses: actions/upload-artifact@v7 - with: - name: build-artifacts - path: | - .build/debug/index/store - retention-days: 1 - - # Static Analysis - static-analysis: - runs-on: macos-26 - needs: test-and-coverage - timeout-minutes: 15 - if: github.actor != 'dependabot[bot]' - - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 1 - - - name: Verify Swift version - run: swift --version - - - name: Cache Homebrew - uses: actions/cache@v5 - with: - path: ~/Library/Caches/Homebrew - key: ${{ runner.os }}-${{ runner.arch }}-brew-swiftlint-gitleaks-periphery-swift-cpd - restore-keys: | - ${{ runner.os }}-${{ runner.arch }}-brew- - - - name: Install analysis tools - run: | - brew install swiftlint gitleaks periphery - brew tap ericodx/homebrew-tools - brew install swift-cpd - - - name: Download build artifacts - uses: actions/download-artifact@v8 - with: - name: build-artifacts - path: .build/debug - - - name: SwiftLint - run: | - mkdir -p reports - swiftlint lint --reporter json > reports/swiftlint.json || true - - - name: Run Periphery - run: | - periphery scan \ - --skip-build \ - --index-store-path "$PWD/.build/debug/index/store" \ - --format json \ - > reports/periphery.json || true - - - name: Gitleaks (secrets) - run: | - gitleaks detect \ - --report-path reports/gitleaks.sarif \ - --report-format sarif || true - - - name: Code duplication (swift-cpd) - run: | - swift-cpd \ - || true - - - name: Upload reports artifact - uses: actions/upload-artifact@v7 - with: - name: reports - path: reports/ - retention-days: 1 - - # Quality Gate - quality-gate: - runs-on: ubuntu-latest - needs: [test-and-coverage, static-analysis] - if: github.actor != 'dependabot[bot]' - - steps: - - uses: actions/download-artifact@v8 - with: - name: coverage - path: coverage - - - uses: actions/download-artifact@v8 - with: - name: reports - path: reports - - - name: Evaluate quality gate - env: - COVERAGE_THRESHOLD: ${{ vars.COVERAGE_THRESHOLD }} - FAIL_ON_SECRETS: ${{ vars.FAIL_ON_SECRETS }} - MAX_LINT_VIOLATIONS: ${{ vars.MAX_LINT_VIOLATIONS }} - MAX_DEAD_CODE: ${{ vars.MAX_DEAD_CODE }} - MAX_DUPLICATION: ${{ vars.MAX_DUPLICATION }} - STRICT_MODE: ${{ vars.STRICT_MODE }} - run: | - THRESHOLD=${COVERAGE_THRESHOLD:-95} - FAIL_SECRETS=${FAIL_ON_SECRETS:-true} - MAX_LINT=${MAX_LINT_VIOLATIONS:-10} - MAX_DEAD=${MAX_DEAD_CODE:-0} - MAX_DUP=${MAX_DUPLICATION:-5} - STRICT=${STRICT_MODE:-false} - - FAIL=0 - - if [ -f coverage/lines-percent.txt ]; then - COVERAGE=$(cat coverage/lines-percent.txt) - echo "DEBUG: Lines Coverage = $COVERAGE%" - else - COVERAGE="0.00" - fi - - if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then - COVERAGE_STATUS="x" - FAIL=1 - else - COVERAGE_STATUS="✓" - fi - - LINT_COUNT=$(jq -r 'length // 0' reports/swiftlint.json 2>/dev/null || echo 0) - DEAD_CODE_COUNT=$(jq -r 'length // 0' reports/periphery.json 2>/dev/null || echo 0) - SECRETS_COUNT=$(jq -r '.runs[0].results | length // 0' reports/gitleaks.sarif 2>/dev/null || echo 0) - - # Code duplication analysis - if [ -f reports/cpd-report.json ]; then - DUPLICATION=$(jq -r '.summary.duplicationPercentage // 0' reports/cpd-report.json 2>/dev/null || echo 0) - DUPLICATION_CLONES=$(jq -r '.summary.totalClones // 0' reports/cpd-report.json 2>/dev/null || echo 0) - else - DUPLICATION="0" - DUPLICATION_CLONES="0" - fi - - if [ "$SECRETS_COUNT" -gt 0 ] && [ "$FAIL_SECRETS" = "true" ]; then - SECRETS_STATUS="x" - FAIL=1 - else - SECRETS_STATUS="✓" - fi - - # Duplication status - if (( $(echo "$DUPLICATION > $MAX_DUP" | bc -l) )); then - DUPLICATION_STATUS="x" - if [ "$STRICT" = "true" ]; then FAIL=1; fi - else - DUPLICATION_STATUS="✓" - fi - - if [ "$STRICT" = "true" ]; then - if [ "$LINT_COUNT" -gt "$MAX_LINT" ]; then FAIL=1; fi - if [ "$DEAD_CODE_COUNT" -gt "$MAX_DEAD" ]; then FAIL=1; fi - fi - - REPORT_FILE=quality-report.md - - cat > "$REPORT_FILE" << EOF - ## Pull Request Quality Report - - | Metric | Value | Status | Threshold | - |--------|-------|--------|----------| - | Coverage | $COVERAGE% | $COVERAGE_STATUS | >= $THRESHOLD% | - | Code duplication | ${DUPLICATION}% ($DUPLICATION_CLONES clones) | $DUPLICATION_STATUS | <= $MAX_DUP% | - | Linter violations | $LINT_COUNT | ~ | <= $MAX_LINT | - | Dead code | $DEAD_CODE_COUNT | ~ | <= $MAX_DEAD | - | Secrets | $SECRETS_COUNT | $SECRETS_STATUS | 0 | - - Strict mode: $STRICT - EOF - - cat "$REPORT_FILE" >> "$GITHUB_STEP_SUMMARY" - - if [ "$FAIL" -eq 1 ]; then - echo "Quality gate failed" - exit 1 - fi - - echo "Quality gate passed" - - - name: Comment PR with quality report - if: github.event_name == 'pull_request' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - export GH_TOKEN="$GITHUB_TOKEN" - - MARKER="" - BODY="$MARKER - $(cat quality-report.md)" - - COMMENT_ID=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \ - --jq ".[] | select(.body | contains(\"$MARKER\")) | .id" | head -n 1) - - if [ -n "$COMMENT_ID" ]; then - gh api repos/${{ github.repository }}/issues/comments/$COMMENT_ID \ - -X PATCH \ - -f body="$BODY" - else - gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \ - -X POST \ - -f body="$BODY" - fi + - name: Run tests + run: swift test From 0c684bf91b89ea9b9e7b2fc3c1a31a265f67bbac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:22:48 -0300 Subject: [PATCH 3/6] ci: restore hardcoded URI exclusion rule in sonar properties --- sonar-project.properties | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index a3d824e..583d52e 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -16,8 +16,7 @@ sonar.sourceEncoding=UTF-8 # Coverage sonar.coverageReportPaths=coverage/code-coverage-report.xml -# External analyzers -sonar.externalIssuesReportPaths=reports/linter-report.json,reports/dead-code-report.json - -# Security -sonar.sarifReportPaths=reports/leaks-report.sarif +# Ignore hardcoded URI's issues +sonar.issue.ignore.multicriteria=e1 +sonar.issue.ignore.multicriteria.e1.ruleKey=swift:S1075 +sonar.issue.ignore.multicriteria.e1.resourceKey=**/* From 24188305c1ec764f84a1d67555fc52b1db36b54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:32:18 -0300 Subject: [PATCH 4/6] ci: remove docs bypass workflow --- .../workflows/pull-request-docs-bypass.yml | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/pull-request-docs-bypass.yml diff --git a/.github/workflows/pull-request-docs-bypass.yml b/.github/workflows/pull-request-docs-bypass.yml deleted file mode 100644 index 3a512a5..0000000 --- a/.github/workflows/pull-request-docs-bypass.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Pull Request Analysis - -on: - pull_request: - branches: [ main ] - types: [ opened, synchronize, reopened ] - paths: - - '**/*.md' - -jobs: - test-and-coverage: - runs-on: ubuntu-latest - steps: - - run: echo "Documentation-only change — analysis skipped" - - static-analysis: - runs-on: ubuntu-latest - steps: - - run: echo "Documentation-only change — analysis skipped" - - quality-gate: - runs-on: ubuntu-latest - steps: - - run: echo "Documentation-only change — analysis skipped" From ee914d4ef97d1739ecfa95bc63e89cd59c4f3675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:33:03 -0300 Subject: [PATCH 5/6] ci: remove dependabot --- .github/workflows/pull-request-analysis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pull-request-analysis.yml b/.github/workflows/pull-request-analysis.yml index 2a98ded..53b016d 100644 --- a/.github/workflows/pull-request-analysis.yml +++ b/.github/workflows/pull-request-analysis.yml @@ -15,10 +15,9 @@ permissions: contents: read jobs: - test: + validade-and-test: runs-on: macos-26 timeout-minutes: 30 - if: github.actor != 'dependabot[bot]' steps: - uses: actions/checkout@v6 From 84cb4110e74f6d1f202f2ec0fc1fc74d34508755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Sat, 28 Mar 2026 21:35:45 -0300 Subject: [PATCH 6/6] fix: code spell --- .github/workflows/pull-request-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-analysis.yml b/.github/workflows/pull-request-analysis.yml index 53b016d..f178704 100644 --- a/.github/workflows/pull-request-analysis.yml +++ b/.github/workflows/pull-request-analysis.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - validade-and-test: + validate-and-test: runs-on: macos-26 timeout-minutes: 30