diff --git a/.github/workflows/cache-cleanup-scheduled.yml b/.github/workflows/cache-cleanup-scheduled.yml new file mode 100644 index 00000000..011cb156 --- /dev/null +++ b/.github/workflows/cache-cleanup-scheduled.yml @@ -0,0 +1,53 @@ +name: Cache Cleanup (Scheduled) + +on: + schedule: + - cron: '0 0 * * 0' # Sunday midnight UTC + workflow_dispatch: # Manual trigger + +permissions: + actions: write # Required to delete caches + +jobs: + cleanup: + name: Cleanup Old Caches + runs-on: ubuntu-latest + steps: + - name: Cleanup Old Caches + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + run: | + echo "Fetching cache list for $REPO..." + + # Get all caches sorted by last accessed (oldest first) + gh api "repos/$REPO/actions/caches" \ + --jq '.actions_caches | sort_by(.last_accessed_at) | .[] | "\(.id) \(.key) \(.size_in_bytes)"' \ + > /tmp/caches.txt + + TOTAL=$(wc -l < /tmp/caches.txt | tr -d ' ') + echo "Found $TOTAL caches" + + # Keep newest 3 caches, delete rest + KEEP=3 + DELETE=$((TOTAL - KEEP)) + + if [ "$DELETE" -gt 0 ]; then + echo "Deleting $DELETE old caches (keeping newest $KEEP)" + + head -n "$DELETE" /tmp/caches.txt | while read -r id key size; do + size_mb=$((size / 1024 / 1024)) + echo "Deleting cache $id (${size_mb}MB): $key" + gh api --method DELETE "repos/$REPO/actions/caches/$id" || true + done + + echo "✅ Deleted $DELETE old caches" + else + echo "Only $TOTAL caches exist, nothing to delete (keeping $KEEP)" + fi + + # Show remaining caches + echo "" + echo "=== Remaining caches ===" + gh api "repos/$REPO/actions/caches" \ + --jq '.actions_caches[] | "\(.key): \(.size_in_bytes / 1024 / 1024 | floor)MB"' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d892ec7..08e2bd6f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,11 @@ env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true +# Top-level permissions for the workflow +permissions: + contents: write # For committing version changes and pushing tags + id-token: write # For OIDC trusted publishing (inherited by reusable workflows) + jobs: version: name: Calculate Version @@ -202,4 +207,6 @@ jobs: uses: ./.github/workflows/nuget-publish.yml with: tag: v${{ needs.version.outputs.semver }} - # No secrets needed - workflow uses OIDC trusted publishing + # Note: No secrets needed - nuget-publish.yml uses OIDC trusted publishing + # which relies on id-token permission (declared in the called workflow), + # not repository secrets diff --git a/.github/workflows/reusable-quality.yml b/.github/workflows/reusable-quality.yml index 30496f16..b5e27a8e 100644 --- a/.github/workflows/reusable-quality.yml +++ b/.github/workflows/reusable-quality.yml @@ -139,6 +139,8 @@ jobs: fi - name: SonarCloud Analysis + # Only run on main/develop pushes - PR analysis not supported by current SonarCloud plan + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') env: GITHUB_TOKEN: ${{ github.token }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -146,7 +148,7 @@ jobs: dotnet-sonarscanner begin /k:"whizbang-lib_whizbang" /o:"whizbang-lib" \ /d:sonar.token="${SONAR_TOKEN}" \ /d:sonar.host.url="https://sonarcloud.io" \ - /d:sonar.exclusions="**/samples/**,**/benchmarks/**,**/*Generated.cs,**/.whizbang-generated/**" \ + /d:sonar.exclusions="**/samples/**,**/benchmarks/**,**/node_modules/**,**/*Generated.cs,**/.whizbang-generated/**" \ /d:sonar.cpd.exclusions="**/tests/**,**/tools/**,**/samples/**,**/benchmarks/**,src/Whizbang.Generators/**,src/Whizbang.Generators.Shared/**,src/Whizbang.Data.EFCore.Postgres.Generators/**,src/Whizbang.Transports.HotChocolate.Generators/**,src/Whizbang.Transports.FastEndpoints.Generators/**" \ /d:sonar.coverage.exclusions="**/samples/**,**/benchmarks/**,**/tests/**,**/tools/**,src/Whizbang.Generators/**,src/Whizbang.Generators.Shared/**,src/Whizbang.Data.Schema/**,src/Whizbang.Data.EFCore.Postgres.Generators/**,src/Whizbang.Transports.HotChocolate.Generators/**,src/Whizbang.Transports.FastEndpoints.Generators/**,src/Whizbang.Hosting.Azure.ServiceBus/**,src/Whizbang.Hosting.RabbitMQ/**,src/Whizbang.Data.Dapper.Postgres/**,src/Whizbang.Transports.RabbitMQ/**,src/Whizbang.Data.EFCore.Postgres/**,src/Whizbang.Transports.AzureServiceBus/**,src/Whizbang.Data.Dapper.Sqlite/**,src/Whizbang.Data.Dapper.Custom/**,src/Whizbang.Data.EFCore.Custom/**,src/Whizbang.Data.Postgres/**,src/Whizbang.Testing/**,src/Whizbang.Observability/**,src/Whizbang.SignalR/**" \ /d:sonar.coverageReportPaths="coverage/sonarqube/SonarQube.xml" \