also looking at this
fix(ci): restore dependency bot scheduled job selection
#309
3 files
+72
-10
| @@ -87,14 +87,22 @@ jobs: | ||
| 87 | 87 | steps: |
| 88 | 88 | - name: Determine which jobs to run |
| 89 | 89 | id: determine |
| 90 | env: | |
| 91 | EVENT_NAME: ${{ github.event_name }} | |
| 92 | EVENT_SCHEDULE: ${{ github.event.schedule }} | |
| 93 | REQUESTED_JOB: ${{ github.event.inputs.job }} | |
| 90 | 94 | run: | |
| 91 | 95 | # Default to not running any jobs |
| 92 | 96 | echo "run_check=false" >> $GITHUB_OUTPUT |
| 93 | 97 | echo "run_update=false" >> $GITHUB_OUTPUT |
| 94 | 98 | echo "run_dashboard=false" >> $GITHUB_OUTPUT |
| 95 | 99 | |
| 96 | if [ "$" = "workflow_dispatch" ]; then | |
| 97 | JOB="${{ github.event.inputs.job || 'all' }}" | |
| 100 | if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| 101 | JOB="${REQUESTED_JOB:-all}" | |
| 102 | case "$JOB" in | |
| 103 | all|check|update|dashboard) ;; | |
| 104 | *) echo "::error::Unknown Buddy Bot job: $JOB"; exit 1 ;; | |
| 105 | esac | |
| 98 | 106 | if [ "$JOB" = "all" ] || [ "$JOB" = "check" ]; then |
| 99 | 107 | echo "run_check=true" >> $GITHUB_OUTPUT |
| 100 | 108 | fi |
| @@ -104,14 +112,17 @@ jobs: | ||
| 104 | 112 | if [ "$JOB" = "all" ] || [ "$JOB" = "dashboard" ]; then |
| 105 | 113 | echo "run_dashboard=true" >> $GITHUB_OUTPUT |
| 106 | 114 | fi |
| 107 | elif [ "$" = "schedule" ]; then | |
| 115 | elif [ "$EVENT_NAME" = "schedule" ]; then | |
| 108 | 116 | # Determine based on cron schedule |
| 109 | if [ "$" = "*/1 * * * *" ]; then | |
| 117 | if [ "$EVENT_SCHEDULE" = "0 * * * *" ]; then | |
| 110 | 118 | echo "run_check=true" >> $GITHUB_OUTPUT |
| 111 | elif [ "$" = "0 */2 * * *" ]; then | |
| 119 | elif [ "$EVENT_SCHEDULE" = "0 3,15 * * *" ]; then | |
| 112 | 120 | echo "run_update=true" >> $GITHUB_OUTPUT |
| 113 | elif [ "$" = "15 */2 * * *" ]; then | |
| 121 | elif [ "$EVENT_SCHEDULE" = "15 3,15 * * *" ]; then | |
| 114 | 122 | echo "run_dashboard=true" >> $GITHUB_OUTPUT |
| 123 | else | |
| 124 | echo "::error::Unknown Buddy Bot schedule: $EVENT_SCHEDULE" | |
| 125 | exit 1 | |
| 115 | 126 | fi |
| 116 | 127 | fi |
| 117 | 128 | |
| @@ -362,7 +373,6 @@ jobs: | ||
| 362 | 373 | - name: Display dashboard configuration |
| 363 | 374 | run: | |
| 364 | 375 | echo "📊 **Buddy Bot Dashboard Management**" |
| 365 | echo "Pin Dashboard: $" | |
| 366 | 376 | echo "Custom Title: $" |
| 367 | 377 | echo "Issue Number: $" |
| 368 | 378 | echo "Verbose: $" |
| @@ -398,14 +408,12 @@ jobs: | ||
| 398 | 408 | |
| 399 | 409 | - name: Update Dependency Dashboard |
| 400 | 410 | run: | |
| 401 | PIN="$" | |
| 402 | 411 | TITLE="$" |
| 403 | 412 | ISSUE_NUMBER="$" |
| 404 | 413 | VERBOSE="$" |
| 405 | 414 | DRY_RUN="$" |
| 406 | 415 | |
| 407 | 416 | echo "📊 Updating dependency dashboard..." |
| 408 | echo "Pin: $PIN" | |
| 409 | 417 | echo "Title: ${TITLE:-default}" |
| 410 | 418 | echo "Issue Number: ${ISSUE_NUMBER:-auto-detect}" |
| 411 | 419 | echo "Verbose: $VERBOSE" |
| @@ -485,7 +493,6 @@ jobs: | ||
| 485 | 493 | run: | |
| 486 | 494 | echo "## 📊 Dependency Dashboard Summary" >> $GITHUB_STEP_SUMMARY |
| 487 | 495 | echo "" >> $GITHUB_STEP_SUMMARY |
| 488 | echo "- **Pin Dashboard**: $" >> $GITHUB_STEP_SUMMARY | |
| 489 | 496 | echo "- **Custom Title**: $" >> $GITHUB_STEP_SUMMARY |
| 490 | 497 | echo "- **Issue Number**: $" >> $GITHUB_STEP_SUMMARY |
| 491 | 498 | echo "- **Triggered by**: $" >> $GITHUB_STEP_SUMMARY |
| @@ -286,6 +286,9 @@ jobs: | ||
| 286 | 286 | - name: Verify API documentation links |
| 287 | 287 | run: bun run verify:docs |
| 288 | 288 | |
| 289 | - name: Test dependency-bot job selection | |
| 290 | run: bun test scripts/buddy-schedule.test.ts | |
| 291 | ||
| 289 | 292 | - name: Test merged SBOM inventory |
| 290 | 293 | run: bun test scripts/merge-sbom.test.ts |
| 291 | 294 | |
| @@ -0,0 +1,52 @@ | ||
| 1 | import { expect, test } from 'bun:test' | |
| 2 | import { mkdtempSync, readFileSync, rmSync } from 'node:fs' | |
| 3 | import { tmpdir } from 'node:os' | |
| 4 | import { join } from 'node:path' | |
| 5 | ||
| 6 | const workflow = Bun.YAML.parse(readFileSync(join(import.meta.dir, '../.github/workflows/buddy-bot.yml'), 'utf8')) as any | |
| 7 | const selector = workflow.jobs['determine-jobs'].steps.find((step: any) => step.id === 'determine') | |
| 8 | ||
| 9 | function select(event: string, schedule = '', job = '') { | |
| 10 | const directory = mkdtempSync(join(tmpdir(), 'craft-buddy-schedule-')) | |
| 11 | try { | |
| 12 | const output = join(directory, 'output') | |
| 13 | // Substitute the old workflow's expressions too, so this executes the | |
| 14 | // actual selector before and after its move to environment variables. | |
| 15 | const script = selector.run | |
| 16 | .replaceAll('$', event) | |
| 17 | .replaceAll('$', schedule) | |
| 18 | .replaceAll("$", job || 'all') | |
| 19 | const result = Bun.spawnSync(['/bin/bash', '-eo', 'pipefail', '-c', script], { | |
| 20 | env: { ...process.env, GITHUB_OUTPUT: output, EVENT_NAME: event, EVENT_SCHEDULE: schedule, REQUESTED_JOB: job }, | |
| 21 | stdout: 'pipe', stderr: 'pipe', | |
| 22 | }) | |
| 23 | const values = Object.fromEntries(readFileSync(output, 'utf8').trim().split('\n').map(line => line.split('='))) | |
| 24 | return { exitCode: result.exitCode, values } | |
| 25 | } | |
| 26 | finally { rmSync(directory, { recursive: true, force: true }) } | |
| 27 | } | |
| 28 | ||
| 29 | test('every declared schedule selects exactly its intended job', () => { | |
| 30 | const expected = { '0 * * * *': 'check', '0 3,15 * * *': 'update', '15 3,15 * * *': 'dashboard' } | |
| 31 | expect(workflow.on.schedule.map((item: any) => item.cron).sort()).toEqual(Object.keys(expected).sort()) | |
| 32 | for (const [cron, job] of Object.entries(expected)) { | |
| 33 | const result = select('schedule', cron) | |
| 34 | expect(result.exitCode).toBe(0) | |
| 35 | expect(result.values).toEqual({ run_check: String(job === 'check'), run_update: String(job === 'update'), run_dashboard: String(job === 'dashboard') }) | |
| 36 | } | |
| 37 | }) | |
| 38 | ||
| 39 | test('manual dispatch preserves individual jobs and the all/default choice', () => { | |
| 40 | expect(selector.env).toEqual({ EVENT_NAME: '$', EVENT_SCHEDULE: '$', REQUESTED_JOB: '$' }) | |
| 41 | expect(readFileSync(join(import.meta.dir, '../.github/workflows/buddy-bot.yml'), 'utf8')).not.toContain('github.event.inputs.pin') | |
| 42 | for (const job of ['', 'all', 'check', 'update', 'dashboard']) { | |
| 43 | const result = select('workflow_dispatch', '', job) | |
| 44 | expect(result.exitCode).toBe(0) | |
| 45 | expect(result.values).toEqual(Object.fromEntries(['check', 'update', 'dashboard'].map(name => [`run_${name}`, String(!job || job === 'all' || name === job)]))) | |
| 46 | } | |
| 47 | }) | |
| 48 | ||
| 49 | test('unknown schedules and jobs fail visibly instead of silently doing nothing', () => { | |
| 50 | expect(select('schedule', '*/1 * * * *').exitCode).not.toBe(0) | |
| 51 | expect(select('workflow_dispatch', '', 'typo').exitCode).not.toBe(0) | |
| 52 | }) | |