From d94179addc4946aa1cf5b6476704e47d1610d39b Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Thu, 24 Sep 2026 15:41:50 -0400 Subject: [PATCH 01/12] ci: drop invalid top-level retention-days from Actions workflows --- .github/workflows/build.yml | 3 --- .github/workflows/contract-release.yml | 3 --- .github/workflows/dapp-ipfs.yml | 5 +---- .github/workflows/secrets-check.yml | 3 --- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52029992..3241a1e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,6 +113,3 @@ jobs: - name: Run Tests working-directory: ./frontend run: npm test --if-present - -# Workflow run retention settings -retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/contract-release.yml b/.github/workflows/contract-release.yml index 2655fcbc..344b0121 100644 --- a/.github/workflows/contract-release.yml +++ b/.github/workflows/contract-release.yml @@ -27,6 +27,3 @@ jobs: package: "..." secrets: release_token: ${{ secrets.GITHUB_TOKEN }} - -# Workflow run retention settings -retention-days: 90 \ No newline at end of file diff --git a/.github/workflows/dapp-ipfs.yml b/.github/workflows/dapp-ipfs.yml index 29d16e55..0e384d94 100644 --- a/.github/workflows/dapp-ipfs.yml +++ b/.github/workflows/dapp-ipfs.yml @@ -37,7 +37,7 @@ jobs: env: PUBLIC_STELLAR_NETWORK_PASSPHRASE: ${{ vars.PUBLIC_STELLAR_NETWORK_PASSPHRASE }} PUBLIC_STELLAR_RPC_URL: ${{ vars.PUBLIC_STELLAR_RPC_URL }} - PUBLIC_PUBLIC_STELLAR_HORIZON_URL: ${{ vars.PUBLIC_PUBLIC_STELLAR_HORIZON_URL }} + PUBLIC_STELLAR_HORIZON_URL: ${{ vars.PUBLIC_PUBLIC_STELLAR_HORIZON_URL }} run: npm run build - name: Generate artifact attestation for dist @@ -65,6 +65,3 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "- CID: ${{ steps.storacha.outputs.cid }}" >> "$GITHUB_STEP_SUMMARY" echo "- URL: ${{ steps.storacha.outputs.url }}" >> "$GITHUB_STEP_SUMMARY" - -# Workflow run retention settings -retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/secrets-check.yml b/.github/workflows/secrets-check.yml index b8d3b7e8..447caecf 100644 --- a/.github/workflows/secrets-check.yml +++ b/.github/workflows/secrets-check.yml @@ -20,6 +20,3 @@ jobs: - name: Check backend-secret.yaml for non-placeholder values run: ./scripts/check-k8s-secrets.sh - -# Workflow run retention settings -retention-days: 30 \ No newline at end of file From 446321919b98529dfba2fa6f9de210f5cd42e444 Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Thu, 24 Sep 2026 15:42:03 -0400 Subject: [PATCH 02/12] ci: restore original Horizon env var name in dapp-ipfs workflow --- .github/workflows/dapp-ipfs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dapp-ipfs.yml b/.github/workflows/dapp-ipfs.yml index 0e384d94..80b579de 100644 --- a/.github/workflows/dapp-ipfs.yml +++ b/.github/workflows/dapp-ipfs.yml @@ -37,7 +37,7 @@ jobs: env: PUBLIC_STELLAR_NETWORK_PASSPHRASE: ${{ vars.PUBLIC_STELLAR_NETWORK_PASSPHRASE }} PUBLIC_STELLAR_RPC_URL: ${{ vars.PUBLIC_STELLAR_RPC_URL }} - PUBLIC_STELLAR_HORIZON_URL: ${{ vars.PUBLIC_PUBLIC_STELLAR_HORIZON_URL }} + PUBLIC_PUBLIC_STELLAR_HORIZON_URL: ${{ vars.PUBLIC_PUBLIC_STELLAR_HORIZON_URL }} run: npm run build - name: Generate artifact attestation for dist From 49b3162ef50e1f77b220322f166f6b9ee5083e9d Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Thu, 24 Sep 2026 16:02:40 -0400 Subject: [PATCH 03/12] ci: fix invalid GitHub Actions workflow YAML * ci: remove invalid top-level retention-days from GitHub Actions workflows * ci: remove invalid retention-days from build.yml * ci: drop invalid top-level retention-days and restore secrets script --- .github/workflows/build.yml | 1 - .github/workflows/contract-release.yml | 1 - .github/workflows/dapp-ipfs.yml | 1 - .github/workflows/e2e.yml | 1 - .github/workflows/secrets-check.yml | 1 - scripts/check-k8s-secrets.sh | 78 ++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 scripts/check-k8s-secrets.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3241a1e7..68212ca7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ on: branches: ["main"] types: [opened, synchronize, reopened, ready_for_review] -# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days env: CARGO_TERM_COLOR: always PKG_CONFIG_PATH: /usr/lib/pkgconfig diff --git a/.github/workflows/contract-release.yml b/.github/workflows/contract-release.yml index 344b0121..cca80486 100644 --- a/.github/workflows/contract-release.yml +++ b/.github/workflows/contract-release.yml @@ -5,7 +5,6 @@ on: tags: - "v*" -# Retention policy: Keep successful runs for 90 days, failed/cancelled for 14 days permissions: # required permissions for the workflow id-token: write contents: write # in order to create releases diff --git a/.github/workflows/dapp-ipfs.yml b/.github/workflows/dapp-ipfs.yml index 80b579de..5887cc7e 100644 --- a/.github/workflows/dapp-ipfs.yml +++ b/.github/workflows/dapp-ipfs.yml @@ -9,7 +9,6 @@ on: workflow_dispatch: -# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index edc90f5b..1f381556 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -11,7 +11,6 @@ on: - "frontend/**" - ".github/workflows/e2e.yml" -# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days defaults: run: working-directory: frontend diff --git a/.github/workflows/secrets-check.yml b/.github/workflows/secrets-check.yml index 447caecf..5e989082 100644 --- a/.github/workflows/secrets-check.yml +++ b/.github/workflows/secrets-check.yml @@ -10,7 +10,6 @@ on: paths: - "k8s/**" -# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days jobs: check-secrets-placeholders: name: Verify no real secrets in k8s manifests diff --git a/scripts/check-k8s-secrets.sh b/scripts/check-k8s-secrets.sh new file mode 100644 index 00000000..05247f4c --- /dev/null +++ b/scripts/check-k8s-secrets.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# +# check-k8s-secrets.sh — verify that k8s/base/backend-secret.yaml contains +# only the expected CHANGE_ME placeholders and no real secret values. +# +# Exit codes: +# 0 — file is clean (only placeholders) +# 1 — file contains values that are NOT known placeholders (possible leak) +# 2 — file not found or parse error +# +# Usage: +# ./scripts/check-k8s-secrets.sh # standalone +# # Also wired into .husky/pre-commit and .github/workflows/secrets-check.yml + +set -euo pipefail + +SECRET_FILE="k8s/base/backend-secret.yaml" + +if [[ ! -f "$SECRET_FILE" ]]; then + echo "ERROR: $SECRET_FILE not found." + exit 2 +fi + +# Allowed placeholder values. Any stringData value not in this set is flagged. +ALLOWED_VALUES=( + "CHANGE_ME" + "CHANGE_ME_TO_A_SECURE_RANDOM_STRING" + "postgresql://payd_user:CHANGE_ME@postgres:5432/payd_db" + "payd_user" +) + +# Extract values from the stringData block. Uses Python for reliable YAML +# parsing — avoids fragile awk/grep that breaks on edge cases. +values=$(python3 -c " +import sys, re + +with open('$SECRET_FILE') as f: + content = f.read() + +# Find the stringData block +match = re.search(r'^stringData:\\s*\\n((?:\\s+\\w+:.*\\n?)*)', content, re.MULTILINE) +if not match: + sys.exit(2) + +block = match.group(1) +for line in block.strip().splitlines(): + # Extract value after the key: separator + val = line.split(':', 1)[1].strip().strip('\\"') + print(val) +" 2>/dev/null) + +if [[ -z "$values" ]]; then + echo "ERROR: Could not parse stringData values from $SECRET_FILE" + exit 2 +fi + +failed=0 +while IFS= read -r value; do + matched=0 + for allowed in "${ALLOWED_VALUES[@]}"; do + if [[ "$value" == "$allowed" ]]; then + matched=1 + break + fi + done + if [[ $matched -eq 0 ]]; then + echo "FAIL: $SECRET_FILE contains a non-placeholder value." + echo " Real secrets must not be committed. See k8s/README.md for safe alternatives." + failed=1 + fi +done <<< "$values" + +if [[ $failed -eq 1 ]]; then + exit 1 +fi + +echo "OK: $SECRET_FILE contains only placeholder values." +exit 0 From 21f2e6d6b332d2b5e802b2c19d69f87b425eb3a4 Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Sun, 4 Oct 2026 20:47:29 -0400 Subject: [PATCH 04/12] feat: elect a single PayD scheduler leader --- backend/src/services/scheduleExecutor.ts | 62 +++++++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/backend/src/services/scheduleExecutor.ts b/backend/src/services/scheduleExecutor.ts index f34c242a..94a40ae5 100644 --- a/backend/src/services/scheduleExecutor.ts +++ b/backend/src/services/scheduleExecutor.ts @@ -6,31 +6,79 @@ import { scheduleService } from './scheduleService.js'; import type { Schedule, ExecutionResult, PaymentRecipient } from '../types/schedule.js'; import { Operation, Asset, Memo, Keypair } from '@stellar/stellar-sdk'; import os from 'node:os'; +import type { PoolClient } from 'pg'; + +const LEADER_ELECTION_INTERVAL = '*/15 * * * * *'; +// Two int32 advisory-lock keys: ASCII-ish PAYD / SCHD namespaces. +const SCHEDULER_LOCK_NAMESPACE = 0x50415944; +const SCHEDULER_LOCK_KEY = 0x53434844; export class ScheduleExecutor { private cronJob: ScheduledTask | null = null; private readonly podId: string; + private runInProgress = false; constructor() { this.podId = `${os.hostname()}-${process.pid}`; } /** - * Initialize the cron job to run every minute - * Sets up node-cron job with error handling and logging + * Probe scheduler leadership every 15 seconds. + * + * The advisory lock is session-scoped, so PostgreSQL releases it automatically + * if the leader pod dies or loses its database connection. Keeping the lock on + * a dedicated client for the full scheduler pass guarantees that at most one + * pod enters processDueSchedules at a time. */ initialize(): void { - // Cron expression: run every minute - this.cronJob = cron.schedule('* * * * *', async () => { + this.cronJob = cron.schedule(LEADER_ELECTION_INTERVAL, async () => { + if (this.runInProgress) { + return; + } + + this.runInProgress = true; + let leaderClient: PoolClient | null = null; + let hasLeadership = false; + let destroyLeaderConnection = false; + try { - console.log('[ScheduleExecutor] Running scheduled task check...'); + leaderClient = await pool.connect(); + const election = await leaderClient.query<{ acquired: boolean }>( + 'SELECT pg_try_advisory_lock($1, $2) AS acquired', + [SCHEDULER_LOCK_NAMESPACE, SCHEDULER_LOCK_KEY] + ); + + hasLeadership = election.rows[0]?.acquired === true; + if (!hasLeadership) { + return; + } + await this.processDueSchedules(); } catch (error) { - console.error('[ScheduleExecutor] Error in cron job execution:', error); + console.error('[ScheduleExecutor] Error in leader scheduler execution:', error); + } finally { + if (leaderClient) { + if (hasLeadership) { + try { + const unlock = await leaderClient.query<{ unlocked: boolean }>( + 'SELECT pg_advisory_unlock($1, $2) AS unlocked', + [SCHEDULER_LOCK_NAMESPACE, SCHEDULER_LOCK_KEY] + ); + destroyLeaderConnection = unlock.rows[0]?.unlocked !== true; + } catch (error) { + destroyLeaderConnection = true; + console.error('[ScheduleExecutor] Failed to release scheduler leadership:', error); + } + } + + leaderClient.release(destroyLeaderConnection); + } + + this.runInProgress = false; } }); - console.log('[ScheduleExecutor] Cron job initialized - running every minute'); + console.log('[ScheduleExecutor] Cron job initialized - checking leadership every 15 seconds'); } /** From 41bb59d74726e32c27593491c7ce29255a0880e0 Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Sun, 4 Oct 2026 20:47:56 -0400 Subject: [PATCH 05/12] test: cover scheduler advisory leadership --- .../__tests__/scheduleExecutor.test.ts | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/backend/src/services/__tests__/scheduleExecutor.test.ts b/backend/src/services/__tests__/scheduleExecutor.test.ts index 75c4229e..4e25682a 100644 --- a/backend/src/services/__tests__/scheduleExecutor.test.ts +++ b/backend/src/services/__tests__/scheduleExecutor.test.ts @@ -58,11 +58,11 @@ describe('ScheduleExecutor', () => { }); describe('initialize', () => { - it('should set up cron job to run every minute', () => { + it('should probe scheduler leadership every 15 seconds', () => { executor.initialize(); expect(mockCron.schedule).toHaveBeenCalledWith( - '* * * * *', + '*/15 * * * * *', expect.any(Function) ); }); @@ -78,6 +78,53 @@ describe('ScheduleExecutor', () => { consoleSpy.mockRestore(); }); + + it('should skip the scheduler pass when another pod holds leadership', async () => { + const leaderQuery = jest.fn().mockResolvedValueOnce({ + rows: [{ acquired: false }], + }); + const leaderRelease = jest.fn(); + (mockPool.connect as jest.Mock).mockResolvedValueOnce({ + query: leaderQuery, + release: leaderRelease, + }); + const processSpy = jest.spyOn(executor, 'processDueSchedules').mockResolvedValue(); + + executor.initialize(); + const callback = (mockCron.schedule as jest.Mock).mock.calls[0][1] as () => Promise; + await callback(); + + expect(leaderQuery).toHaveBeenCalledWith( + expect.stringContaining('pg_try_advisory_lock'), + [expect.any(Number), expect.any(Number)] + ); + expect(processSpy).not.toHaveBeenCalled(); + expect(leaderRelease).toHaveBeenCalledWith(false); + }); + + it('should hold and release leadership around one scheduler pass', async () => { + const leaderQuery = jest.fn() + .mockResolvedValueOnce({ rows: [{ acquired: true }] }) + .mockResolvedValueOnce({ rows: [{ unlocked: true }] }); + const leaderRelease = jest.fn(); + (mockPool.connect as jest.Mock).mockResolvedValueOnce({ + query: leaderQuery, + release: leaderRelease, + }); + const processSpy = jest.spyOn(executor, 'processDueSchedules').mockResolvedValue(); + + executor.initialize(); + const callback = (mockCron.schedule as jest.Mock).mock.calls[0][1] as () => Promise; + await callback(); + + expect(processSpy).toHaveBeenCalledTimes(1); + expect(leaderQuery).toHaveBeenNthCalledWith( + 2, + expect.stringContaining('pg_advisory_unlock'), + [expect.any(Number), expect.any(Number)] + ); + expect(leaderRelease).toHaveBeenCalledWith(false); + }); }); describe('stop', () => { From e70dde1d289bfcc58f40773fef24f1c1155dda3f Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 00:08:56 -0400 Subject: [PATCH 06/12] chore: restore sponsor workflow in scheduler carrier --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68212ca7..52029992 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,7 @@ on: branches: ["main"] types: [opened, synchronize, reopened, ready_for_review] +# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days env: CARGO_TERM_COLOR: always PKG_CONFIG_PATH: /usr/lib/pkgconfig @@ -112,3 +113,6 @@ jobs: - name: Run Tests working-directory: ./frontend run: npm test --if-present + +# Workflow run retention settings +retention-days: 30 \ No newline at end of file From ae11a0bae000a0efb724742649ed3a2e500c39bd Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 00:09:06 -0400 Subject: [PATCH 07/12] chore: restore sponsor workflow in scheduler carrier --- .github/workflows/contract-release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/contract-release.yml b/.github/workflows/contract-release.yml index cca80486..2655fcbc 100644 --- a/.github/workflows/contract-release.yml +++ b/.github/workflows/contract-release.yml @@ -5,6 +5,7 @@ on: tags: - "v*" +# Retention policy: Keep successful runs for 90 days, failed/cancelled for 14 days permissions: # required permissions for the workflow id-token: write contents: write # in order to create releases @@ -26,3 +27,6 @@ jobs: package: "..." secrets: release_token: ${{ secrets.GITHUB_TOKEN }} + +# Workflow run retention settings +retention-days: 90 \ No newline at end of file From e0cef7c9514b777c2b7de68ab3e8e82858cf17cf Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 00:09:24 -0400 Subject: [PATCH 08/12] chore: restore sponsor workflow in scheduler carrier --- .github/workflows/e2e.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 1f381556..edc90f5b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -11,6 +11,7 @@ on: - "frontend/**" - ".github/workflows/e2e.yml" +# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days defaults: run: working-directory: frontend From 987f5d7633d3da0c40ba6a3e0c60262f09061064 Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 00:09:39 -0400 Subject: [PATCH 09/12] chore: drop unrelated secrets-check helper from scheduler carrier --- scripts/check-k8s-secrets.sh | 78 ------------------------------------ 1 file changed, 78 deletions(-) delete mode 100644 scripts/check-k8s-secrets.sh diff --git a/scripts/check-k8s-secrets.sh b/scripts/check-k8s-secrets.sh deleted file mode 100644 index 05247f4c..00000000 --- a/scripts/check-k8s-secrets.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env bash -# -# check-k8s-secrets.sh — verify that k8s/base/backend-secret.yaml contains -# only the expected CHANGE_ME placeholders and no real secret values. -# -# Exit codes: -# 0 — file is clean (only placeholders) -# 1 — file contains values that are NOT known placeholders (possible leak) -# 2 — file not found or parse error -# -# Usage: -# ./scripts/check-k8s-secrets.sh # standalone -# # Also wired into .husky/pre-commit and .github/workflows/secrets-check.yml - -set -euo pipefail - -SECRET_FILE="k8s/base/backend-secret.yaml" - -if [[ ! -f "$SECRET_FILE" ]]; then - echo "ERROR: $SECRET_FILE not found." - exit 2 -fi - -# Allowed placeholder values. Any stringData value not in this set is flagged. -ALLOWED_VALUES=( - "CHANGE_ME" - "CHANGE_ME_TO_A_SECURE_RANDOM_STRING" - "postgresql://payd_user:CHANGE_ME@postgres:5432/payd_db" - "payd_user" -) - -# Extract values from the stringData block. Uses Python for reliable YAML -# parsing — avoids fragile awk/grep that breaks on edge cases. -values=$(python3 -c " -import sys, re - -with open('$SECRET_FILE') as f: - content = f.read() - -# Find the stringData block -match = re.search(r'^stringData:\\s*\\n((?:\\s+\\w+:.*\\n?)*)', content, re.MULTILINE) -if not match: - sys.exit(2) - -block = match.group(1) -for line in block.strip().splitlines(): - # Extract value after the key: separator - val = line.split(':', 1)[1].strip().strip('\\"') - print(val) -" 2>/dev/null) - -if [[ -z "$values" ]]; then - echo "ERROR: Could not parse stringData values from $SECRET_FILE" - exit 2 -fi - -failed=0 -while IFS= read -r value; do - matched=0 - for allowed in "${ALLOWED_VALUES[@]}"; do - if [[ "$value" == "$allowed" ]]; then - matched=1 - break - fi - done - if [[ $matched -eq 0 ]]; then - echo "FAIL: $SECRET_FILE contains a non-placeholder value." - echo " Real secrets must not be committed. See k8s/README.md for safe alternatives." - failed=1 - fi -done <<< "$values" - -if [[ $failed -eq 1 ]]; then - exit 1 -fi - -echo "OK: $SECRET_FILE contains only placeholder values." -exit 0 From 4a25afd8296540bd64e282983af916ccb30f168f Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 14:54:40 -0400 Subject: [PATCH 10/12] chore: restore sponsor .github/workflows/dapp-ipfs.yml --- .github/workflows/dapp-ipfs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dapp-ipfs.yml b/.github/workflows/dapp-ipfs.yml index 5887cc7e..29d16e55 100644 --- a/.github/workflows/dapp-ipfs.yml +++ b/.github/workflows/dapp-ipfs.yml @@ -9,6 +9,7 @@ on: workflow_dispatch: +# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true @@ -64,3 +65,6 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "- CID: ${{ steps.storacha.outputs.cid }}" >> "$GITHUB_STEP_SUMMARY" echo "- URL: ${{ steps.storacha.outputs.url }}" >> "$GITHUB_STEP_SUMMARY" + +# Workflow run retention settings +retention-days: 30 \ No newline at end of file From 819b2b1a334772be98a23591f49aa1bc19a013b0 Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 14:54:42 -0400 Subject: [PATCH 11/12] chore: restore sponsor .github/workflows/secrets-check.yml --- .github/workflows/secrets-check.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/secrets-check.yml b/.github/workflows/secrets-check.yml index 5e989082..b8d3b7e8 100644 --- a/.github/workflows/secrets-check.yml +++ b/.github/workflows/secrets-check.yml @@ -10,6 +10,7 @@ on: paths: - "k8s/**" +# Retention policy: Keep successful runs for 30 days, failed/cancelled for 7 days jobs: check-secrets-placeholders: name: Verify no real secrets in k8s manifests @@ -19,3 +20,6 @@ jobs: - name: Check backend-secret.yaml for non-placeholder values run: ./scripts/check-k8s-secrets.sh + +# Workflow run retention settings +retention-days: 30 \ No newline at end of file From de38187d2a3ac91c3795068cb1f71823e327090d Mon Sep 17 00:00:00 2001 From: woahwhattheheck Date: Mon, 5 Oct 2026 15:17:10 -0400 Subject: [PATCH 12/12] ci: remove unsupported root retention settings Repair GitHub Actions workflow validation for fleet branches. Preserve job definitions, event filters, permissions, artifact retention inputs, and all application files. --- .github/workflows/build.yml | 1 - .github/workflows/contract-release.yml | 1 - .github/workflows/dapp-ipfs.yml | 1 - .github/workflows/secrets-check.yml | 1 - 4 files changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52029992..9d71b0a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,4 +115,3 @@ jobs: run: npm test --if-present # Workflow run retention settings -retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/contract-release.yml b/.github/workflows/contract-release.yml index 2655fcbc..3f66b1a5 100644 --- a/.github/workflows/contract-release.yml +++ b/.github/workflows/contract-release.yml @@ -29,4 +29,3 @@ jobs: release_token: ${{ secrets.GITHUB_TOKEN }} # Workflow run retention settings -retention-days: 90 \ No newline at end of file diff --git a/.github/workflows/dapp-ipfs.yml b/.github/workflows/dapp-ipfs.yml index 29d16e55..42248532 100644 --- a/.github/workflows/dapp-ipfs.yml +++ b/.github/workflows/dapp-ipfs.yml @@ -67,4 +67,3 @@ jobs: echo "- URL: ${{ steps.storacha.outputs.url }}" >> "$GITHUB_STEP_SUMMARY" # Workflow run retention settings -retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/secrets-check.yml b/.github/workflows/secrets-check.yml index b8d3b7e8..c7a34807 100644 --- a/.github/workflows/secrets-check.yml +++ b/.github/workflows/secrets-check.yml @@ -22,4 +22,3 @@ jobs: run: ./scripts/check-k8s-secrets.sh # Workflow run retention settings -retention-days: 30 \ No newline at end of file