From bf94daa5510e715a1a9b14d5c790dfd94cb7b3c5 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 22:13:42 +0000 Subject: [PATCH 01/71] seed watch-lintdiff.yaml --- .github/workflows/watch-lintdiff.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/watch-lintdiff.yaml diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml new file mode 100644 index 000000000000..333990deb237 --- /dev/null +++ b/.github/workflows/watch-lintdiff.yaml @@ -0,0 +1,19 @@ +# Use ~ to sort the workflow to the bottom of the list +name: ~Watch - LintDiff + +on: + check_run: + types: [completed] + +jobs: + check-lintdiff: + runs-on: ubuntu-latest + steps: + # Output the JSON of the triggering event using github script + - name: Output check_run event + uses: actions/github-script@v7 + with: + script: | + console.log(JSON.stringify(github.event)); + return JSON.stringify(github.event.check_run); + result-encoding: string From 672c896e9c49d00c0aff9ec4eb6cfa6d9ed8ddd6 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 22:18:16 +0000 Subject: [PATCH 02/71] add watch-lintdiff.yaml --- .github/workflows/watch-lintdiff.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 333990deb237..0fe693399882 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -17,3 +17,4 @@ jobs: console.log(JSON.stringify(github.event)); return JSON.stringify(github.event.check_run); result-encoding: string + \ No newline at end of file From f532f21327869071929f585ace5d76746ba5ed44 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 15:28:51 -0700 Subject: [PATCH 03/71] checks: read --- .github/workflows/watch-lintdiff.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 0fe693399882..9ce00ab1c993 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -5,6 +5,9 @@ on: check_run: types: [completed] +permissions: + checks: read + jobs: check-lintdiff: runs-on: ubuntu-latest From 60c3e5562521f13c5da8e1a9c5c638372977cf60 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 15:33:02 -0700 Subject: [PATCH 04/71] All check_runs --- .github/workflows/watch-lintdiff.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 9ce00ab1c993..1e3485a19db3 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -3,7 +3,6 @@ name: ~Watch - LintDiff on: check_run: - types: [completed] permissions: checks: read From e2e35df1e607cc91afce71f065dbbd016726fe09 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 15:44:11 -0700 Subject: [PATCH 05/71] job permissions? --- .github/workflows/watch-lintdiff.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 1e3485a19db3..9098f29f00e9 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -4,11 +4,12 @@ name: ~Watch - LintDiff on: check_run: -permissions: - checks: read + jobs: check-lintdiff: + permissions: + checks: read runs-on: ubuntu-latest steps: # Output the JSON of the triggering event using github script From f85011452d5563677e8596bc3023163ce5aa997e Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 15:56:04 -0700 Subject: [PATCH 06/71] hello world --- .github/workflows/watch-lintdiff.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 9098f29f00e9..122b7b173254 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -3,8 +3,7 @@ name: ~Watch - LintDiff on: check_run: - - + types: [completed] jobs: check-lintdiff: @@ -17,7 +16,6 @@ jobs: uses: actions/github-script@v7 with: script: | - console.log(JSON.stringify(github.event)); - return JSON.stringify(github.event.check_run); + console.log("hello world"); result-encoding: string \ No newline at end of file From fb24b6706a970a617e237a0c28ac4d5e1e8ea7eb Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 16:06:13 -0700 Subject: [PATCH 07/71] on: check_run --- .github/workflows/watch-lintdiff.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 122b7b173254..5859568a4746 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -1,9 +1,7 @@ # Use ~ to sort the workflow to the bottom of the list name: ~Watch - LintDiff -on: - check_run: - types: [completed] +on: check_run jobs: check-lintdiff: From 78a893a3795e7954431c66d21907f23196f58cc4 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 16:14:42 -0700 Subject: [PATCH 08/71] check_run, check_suite --- .github/workflows/watch-lintdiff.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 5859568a4746..cf0daf8a5aeb 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -1,7 +1,7 @@ # Use ~ to sort the workflow to the bottom of the list name: ~Watch - LintDiff -on: check_run +on: [check_run, check_suite] jobs: check-lintdiff: @@ -9,8 +9,7 @@ jobs: checks: read runs-on: ubuntu-latest steps: - # Output the JSON of the triggering event using github script - - name: Output check_run event + - name: Say hello uses: actions/github-script@v7 with: script: | From 09414a375f2635a1576d587ae5023a9e04de7b0a Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 16:20:25 -0700 Subject: [PATCH 09/71] write checks --- .github/workflows/watch-lintdiff.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index cf0daf8a5aeb..4f44f40c8402 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -1,12 +1,16 @@ # Use ~ to sort the workflow to the bottom of the list name: ~Watch - LintDiff -on: [check_run, check_suite] +on: + check_run: + types: completed + check_suite: + +permissions: + checks: write jobs: check-lintdiff: - permissions: - checks: read runs-on: ubuntu-latest steps: - name: Say hello From 12c4ec6aa5a54a7382e99b0e9ff38865cc5f5428 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Wed, 2 Apr 2025 16:30:53 -0700 Subject: [PATCH 10/71] check_suite --- .github/workflows/watch-lintdiff.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 4f44f40c8402..595e5c549d9f 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -1,10 +1,9 @@ # Use ~ to sort the workflow to the bottom of the list name: ~Watch - LintDiff -on: - check_run: - types: completed +on: check_suite: + types: completed permissions: checks: write From 57964cdf9f9bddb83f59e3061eed50942329e2fd Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:37:40 -0700 Subject: [PATCH 11/71] workflow_run --- .github/workflows/watch-lintdiff.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 595e5c549d9f..8092ed84f13c 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -2,7 +2,11 @@ name: ~Watch - LintDiff on: - check_suite: + check_run: + types: completed + + workflow_run: + workflows: "[TEST-IGNORE] Swagger LintDiff" types: completed permissions: From 7658bfd9b8061ef0e821fc6e303590ea6f0459ee Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:41:53 -0700 Subject: [PATCH 12/71] workflow_run --- .github/workflows/watch-lintdiff.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 8092ed84f13c..4635fbf8dc40 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -6,8 +6,6 @@ on: types: completed workflow_run: - workflows: "[TEST-IGNORE] Swagger LintDiff" - types: completed permissions: checks: write From c6dc574bae02ac92ac596b51f25e1fc17107332e Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:44:19 -0700 Subject: [PATCH 13/71] workflows --- .github/workflows/watch-lintdiff.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 4635fbf8dc40..8a746ee8fdbd 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -6,6 +6,7 @@ on: types: completed workflow_run: + workflows: ["[TEST-IGNORE] Swagger LintDiff"] permissions: checks: write From 8b768aa9cda561e9a4bc65f838f261d3d595c9e0 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:49:13 -0700 Subject: [PATCH 14/71] is it a parsing problem? --- .github/workflows/watch-lintdiff.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 8a746ee8fdbd..5298cc47f17a 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -1,12 +1,13 @@ # Use ~ to sort the workflow to the bottom of the list -name: ~Watch - LintDiff +name: "~Watch - LintDiff" on: check_run: types: completed workflow_run: - workflows: ["[TEST-IGNORE] Swagger LintDiff"] + workflows: + - "[TEST-IGNORE] Swagger LintDiff" permissions: checks: write From cd7e696473d936b53bd2c3df93890ec9bd1b5838 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:50:06 -0700 Subject: [PATCH 15/71] Remove brackets from name --- .github/workflows/lintdiff.yaml | 2 +- .github/workflows/watch-lintdiff.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lintdiff.yaml b/.github/workflows/lintdiff.yaml index 576a33c2496b..e8beb69a08bb 100644 --- a/.github/workflows/lintdiff.yaml +++ b/.github/workflows/lintdiff.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger LintDiff" +name: "Swagger LintDiff" on: pull_request diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 5298cc47f17a..76c5f1e8b031 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -7,7 +7,7 @@ on: workflow_run: workflows: - - "[TEST-IGNORE] Swagger LintDiff" + - "Swagger LintDiff" permissions: checks: write From e15d77f0c07a1cfff44c46e9712d484988dd3fdb Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:53:13 -0700 Subject: [PATCH 16/71] Add brackets back in --- .github/workflows/lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lintdiff.yaml b/.github/workflows/lintdiff.yaml index e8beb69a08bb..576a33c2496b 100644 --- a/.github/workflows/lintdiff.yaml +++ b/.github/workflows/lintdiff.yaml @@ -1,4 +1,4 @@ -name: "Swagger LintDiff" +name: "[TEST-IGNORE] Swagger LintDiff" on: pull_request From 1780c1698389a1b8af3792c96edcaacac59b652c Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:54:08 -0700 Subject: [PATCH 17/71] Escape attempt --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 76c5f1e8b031..7e70bc10dcec 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -7,7 +7,7 @@ on: workflow_run: workflows: - - "Swagger LintDiff" + - "\[TEST-IGNORE\] Swagger LintDiff" permissions: checks: write From 2b812cf3f9a8cf3ed3fefbadf253564694c0a3c2 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:56:07 -0700 Subject: [PATCH 18/71] double slashes --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 7e70bc10dcec..d036dfd26fe7 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -7,7 +7,7 @@ on: workflow_run: workflows: - - "\[TEST-IGNORE\] Swagger LintDiff" + - "\\[TEST-IGNORE\\] Swagger LintDiff" permissions: checks: write From db492bef82f2a71371778bf827fc3f13c27c71d8 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:58:10 -0700 Subject: [PATCH 19/71] types: completed --- .github/workflows/watch-lintdiff.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index d036dfd26fe7..18e900cf2cdc 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -6,6 +6,7 @@ on: types: completed workflow_run: + types: completed workflows: - "\\[TEST-IGNORE\\] Swagger LintDiff" From 036762c5a39ee23d602a4a7c4081a8cee7de6902 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 06:59:44 -0700 Subject: [PATCH 20/71] log workflow_run object --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 18e900cf2cdc..567e363b7850 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -21,6 +21,6 @@ jobs: uses: actions/github-script@v7 with: script: | - console.log("hello world"); + console.log(github.event.workflow_run); result-encoding: string \ No newline at end of file From f9aa86d129d6a574ee2dd0621cecaa98ee729a4e Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 07:00:00 -0700 Subject: [PATCH 21/71] remove completed, more runs --- .github/workflows/watch-lintdiff.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 567e363b7850..f0c7bd178b62 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -6,7 +6,6 @@ on: types: completed workflow_run: - types: completed workflows: - "\\[TEST-IGNORE\\] Swagger LintDiff" From baf194927e1f6441bbe613ddf2fe8e74cd2f305d Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 07:01:23 -0700 Subject: [PATCH 22/71] context.payload --- .github/workflows/watch-lintdiff.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index f0c7bd178b62..dd06fbe9d92e 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -14,12 +14,12 @@ permissions: jobs: check-lintdiff: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Say hello uses: actions/github-script@v7 with: script: | - console.log(github.event.workflow_run); + console.log(context.payload); result-encoding: string \ No newline at end of file From 0edb76ba1878f9bfcb85b70395e4175e04bf7257 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 07:20:48 -0700 Subject: [PATCH 23/71] More logging --- .github/workflows/watch-lintdiff.yaml | 36 ++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index dd06fbe9d92e..d26f14ba69be 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -16,10 +16,40 @@ jobs: check-lintdiff: runs-on: ubuntu-24.04 steps: - - name: Say hello + # *** IMPORTANT *** + # For workflows that are triggered by the pull_request_target event, the workflow runs in the + # context of the base of the pull request. You should make sure that you do not check out, + # build, or run untrusted code from the head of the pull request. + - uses: actions/checkout@v4 + with: + # Only needs .github folder for automation, not the files in the PR (analyzed in a + # separate workflow). + # + # Uses the .github folder from the PR base branch (pull_request_target trigger), + # or the repo default branch (other triggers). + sparse-checkout: | + .github + + - name: Verify Workflow Status + if: github.event_name == 'workflow_run' uses: actions/github-script@v7 with: script: | - console.log(context.payload); - result-encoding: string + console.log("Checking workflow status"); + console.log(JSON.stringify(context.payload)); + console.log(`State: ${context.payload.workflow.state}`); + + // TODO: Get status of the check run status and compare + + - name: Verify Check Run Status + if: github.event_name == 'check_run' + uses: actions/github-script@v7 + with: + script: | + console.log("Checking check status") + console.log(JSON.stringify(context.payload)); + console.log(`State: ${context.payload.check_run.conclusion}`); + + // TODO: Get the status of the workflow run and compare + \ No newline at end of file From 3c1d1e67e8189b53bd30b091372c51d8266bc33c Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 07:24:26 -0700 Subject: [PATCH 24/71] Formatting --- .github/workflows/watch-lintdiff.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index d26f14ba69be..129f35b906fb 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -6,6 +6,7 @@ on: types: completed workflow_run: + types: completed workflows: - "\\[TEST-IGNORE\\] Swagger LintDiff" @@ -36,7 +37,7 @@ jobs: with: script: | console.log("Checking workflow status"); - console.log(JSON.stringify(context.payload)); + console.log(JSON.stringify(context.payload, null, 2)); console.log(`State: ${context.payload.workflow.state}`); // TODO: Get status of the check run status and compare @@ -47,7 +48,7 @@ jobs: with: script: | console.log("Checking check status") - console.log(JSON.stringify(context.payload)); + console.log(JSON.stringify(context.payload, null, 2)); console.log(`State: ${context.payload.check_run.conclusion}`); // TODO: Get the status of the workflow run and compare From 34e4f57367e92e322e3edd17209124804fba9c99 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 10:42:40 -0700 Subject: [PATCH 25/71] basic api calls to get more outputs --- .github/workflows/watch-lintdiff.yaml | 33 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 129f35b906fb..ebe774ee92d4 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -16,6 +16,8 @@ permissions: jobs: check-lintdiff: runs-on: ubuntu-24.04 + # TODO: Only run if check_run or workflow_run is initiated by pull_request + steps: # *** IMPORTANT *** # For workflows that are triggered by the pull_request_target event, the workflow runs in the @@ -37,10 +39,21 @@ jobs: with: script: | console.log("Checking workflow status"); - console.log(JSON.stringify(context.payload, null, 2)); - console.log(`State: ${context.payload.workflow.state}`); + console.log(JSON.stringify(context.payload)); + console.log(`State: ${context.payload.workflow_run.conclusion}`); + + // Get status of check run named "Swagger LintDiff" + const checkRunName = "Swagger LintDiff"; + // TODO: Paginate + const checkRuns = await github.checks.listForRef({ + ...context.repo, + ref: context.payload.workflow_run.head_sha, + check_name: checkRunName, + status: "completed", + }); - // TODO: Get status of the check run status and compare + console.log("Check runs:"); + console.log(JSON.stringify(checkRuns.data.check_runs, null, 2)); - name: Verify Check Run Status if: github.event_name == 'check_run' @@ -48,9 +61,19 @@ jobs: with: script: | console.log("Checking check status") - console.log(JSON.stringify(context.payload, null, 2)); + console.log(JSON.stringify(context.payload)); console.log(`State: ${context.payload.check_run.conclusion}`); - // TODO: Get the status of the workflow run and compare + // Get status of the workflow run named "[TEST-IGNORE] Swagger LintDiff" + const workflowRunName = "[TEST-IGNORE] Swagger LintDiff"; + // TODO: Paginate + const workflowRuns = await github.actions.listWorkflowRunsForRepo({ + ...context.repo, + head_sha: context.payload.check_run.head_sha, + status: "completed", + }); + + console.log("Workflow runs:"); + console.log(JSON.stringify(workflowRuns.data.workflow_runs, null, 2)); \ No newline at end of file From 75ec3d23d8565685eda6c759247aec60a0eca7c3 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 10:45:41 -0700 Subject: [PATCH 26/71] .rest --- .github/workflows/watch-lintdiff.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index ebe774ee92d4..7996a9be5a14 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -45,7 +45,7 @@ jobs: // Get status of check run named "Swagger LintDiff" const checkRunName = "Swagger LintDiff"; // TODO: Paginate - const checkRuns = await github.checks.listForRef({ + const checkRuns = await github.rest.checks.listForRef({ ...context.repo, ref: context.payload.workflow_run.head_sha, check_name: checkRunName, @@ -67,7 +67,7 @@ jobs: // Get status of the workflow run named "[TEST-IGNORE] Swagger LintDiff" const workflowRunName = "[TEST-IGNORE] Swagger LintDiff"; // TODO: Paginate - const workflowRuns = await github.actions.listWorkflowRunsForRepo({ + const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({ ...context.repo, head_sha: context.payload.check_run.head_sha, status: "completed", From d9ed12b2eb78e1f6355c7128cc47666f9c2086c6 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 11:23:31 -0700 Subject: [PATCH 27/71] Filter based on check run name --- .github/workflows/watch-lintdiff.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 7996a9be5a14..afbc5633e0ea 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -15,6 +15,11 @@ permissions: jobs: check-lintdiff: + if: >- + github.event_name == 'workflow_run' || ( + github.event_name == 'check_run' && + github.event.check_run.name == 'Swagger LintDiff' + ) runs-on: ubuntu-24.04 # TODO: Only run if check_run or workflow_run is initiated by pull_request From 0a7b02d33eb894e4c8d40acc7ed6fd0e910b0ce1 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 11:35:36 -0700 Subject: [PATCH 28/71] Output check_run.name --- .github/workflows/watch-lintdiff.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index afbc5633e0ea..83e40cc3f0bc 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -17,9 +17,7 @@ jobs: check-lintdiff: if: >- github.event_name == 'workflow_run' || ( - github.event_name == 'check_run' && - github.event.check_run.name == 'Swagger LintDiff' - ) + github.event_name == 'check_run' ) runs-on: ubuntu-24.04 # TODO: Only run if check_run or workflow_run is initiated by pull_request @@ -60,6 +58,11 @@ jobs: console.log("Check runs:"); console.log(JSON.stringify(checkRuns.data.check_runs, null, 2)); + - name: Output Check Run Name + if: github.event_name == 'check_run' + run: | + echo "|${{ github.event.check_run.name}}|" + - name: Verify Check Run Status if: github.event_name == 'check_run' uses: actions/github-script@v7 @@ -80,5 +83,3 @@ jobs: console.log("Workflow runs:"); console.log(JSON.stringify(workflowRuns.data.workflow_runs, null, 2)); - - \ No newline at end of file From 0124dcb655c8928402067fa41abec29ae3e793e1 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 11:45:45 -0700 Subject: [PATCH 29/71] Filter check_run.name --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 83e40cc3f0bc..34870dbde685 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -17,7 +17,7 @@ jobs: check-lintdiff: if: >- github.event_name == 'workflow_run' || ( - github.event_name == 'check_run' ) + github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff' ) runs-on: ubuntu-24.04 # TODO: Only run if check_run or workflow_run is initiated by pull_request From af175ceb48b146e8ac21135f174a9c2372423fc7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 20:53:16 +0000 Subject: [PATCH 30/71] First attempt at JS --- .github/workflows/src/verify-run-status.js | 97 ++++++++++++++++++++++ .github/workflows/watch-lintdiff.yaml | 51 ++---------- 2 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/src/verify-run-status.js diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js new file mode 100644 index 000000000000..40119191d42c --- /dev/null +++ b/.github/workflows/src/verify-run-status.js @@ -0,0 +1,97 @@ +import { PER_PAGE_MAX } from "./github.js" +import { extractInputs } from "./context.js"; + +/** + * + * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @param {string} checkRunName + * @param {string} workflowName + */ +export default async function({github, context, core}, checkRunName, workflowName) { + const inputs = await extractInputs(github, context, core); + + const checkRun = context.eventName == "check_run" + ? context.payload.check_run + : await getCheckRunStatus({github, context, core}, checkRunName, inputs.head_sha); + const workflow = context.eventName == "workflow_run" + ? context.payload.workflow_run + : await getWorkflowRun({github, context, core}, workflowName, inputs.head_sha); + + core.info(`Check run: ${JSON.stringify(checkRun)}`); + core.info(`Workflow run: ${JSON.stringify(workflow)}`); + + if (!checkRun || checkRun.status !== "completed") { + core.info(`No completed check run with name: ${checkRunName}`); + return; + } + + if (!checkRun || workflow.status !== "completed") { + core.info(`No completed workflow run with name: ${workflowName}`); + return; + } + + core.info(`Check run conclusion: ${checkRun.conclusion}`); + core.info(`Workflow run conclusion: ${workflow.conclusion}`); + + if (checkRun.conclusion !== workflow.conclusion) { + core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflow.conclusion})`); + return false; + } + +} + +export async function getCheckRunStatus({github, context, core}, checkRunName, headSha) { + const checkRuns = await github.paginate( + github.rest.checks.listForRef, + { + ...context.repo, + ref: headSha, + check_name: checkRunName, + status: "completed", + per_page: PER_PAGE_MAX, + } + ); + core.info(`Check runs: ${JSON.stringify(checkRuns)}`); + + if (checkRuns.length === 0) { + core.info(`No completed check run with name: ${checkRunName}`); + return null; + } + + if (checkRuns.length > 1) { + core.setFailed(`Multiple completed check runs with name: ${checkRunName}`); + core.info(`Check runs: ${JSON.stringify(checkRuns)}`); + throw new Error(`Multiple completed check runs with name: ${checkRunName}`); + } + + return checkRuns[0]; +} + +export async function getWorkflowRun({github, context, core}, workflowName, head_sha) { + const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({ + ...context.repo, + head_sha, + status: "completed", + per_page: PER_PAGE_MAX, + }); + core.info(`Workflow runs: ${JSON.stringify(workflowRuns)}`); + + if (workflowRuns.length === 0) { + core.info(`No completed workflow runs`); + return null; + } + + const matchingWorkflowRuns = workflowRuns.filter((run) => run.name === workflowName); + + if (matchingWorkflowRuns.length === 0) { + core.info(`No completed workflow run with name: ${workflowName}`); + return null; + } + + if (matchingWorkflowRuns.length > 1) { + core.setFailed(`Multiple completed workflow runs with name: ${workflowName}`); + throw new Error(`Multiple completed workflow runs with name: ${workflowName}`); + } + + return matchingWorkflowRuns[0]; +} \ No newline at end of file diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 34870dbde685..f9580d344e07 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -36,50 +36,13 @@ jobs: sparse-checkout: | .github - - name: Verify Workflow Status - if: github.event_name == 'workflow_run' + - name: Verify matching status uses: actions/github-script@v7 with: script: | - console.log("Checking workflow status"); - console.log(JSON.stringify(context.payload)); - console.log(`State: ${context.payload.workflow_run.conclusion}`); - - // Get status of check run named "Swagger LintDiff" - const checkRunName = "Swagger LintDiff"; - // TODO: Paginate - const checkRuns = await github.rest.checks.listForRef({ - ...context.repo, - ref: context.payload.workflow_run.head_sha, - check_name: checkRunName, - status: "completed", - }); - - console.log("Check runs:"); - console.log(JSON.stringify(checkRuns.data.check_runs, null, 2)); - - - name: Output Check Run Name - if: github.event_name == 'check_run' - run: | - echo "|${{ github.event.check_run.name}}|" - - - name: Verify Check Run Status - if: github.event_name == 'check_run' - uses: actions/github-script@v7 - with: - script: | - console.log("Checking check status") - console.log(JSON.stringify(context.payload)); - console.log(`State: ${context.payload.check_run.conclusion}`); - - // Get status of the workflow run named "[TEST-IGNORE] Swagger LintDiff" - const workflowRunName = "[TEST-IGNORE] Swagger LintDiff"; - // TODO: Paginate - const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({ - ...context.repo, - head_sha: context.payload.check_run.head_sha, - status: "completed", - }); - - console.log("Workflow runs:"); - console.log(JSON.stringify(workflowRuns.data.workflow_runs, null, 2)); + const { default: verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); + return await verifyRunStatus( + { github, context, core }, + checkRunName: "Swagger LintDiff", + workflowRunName: "[TEST-IGNORE] Swagger LintDiff" + ); From 82b20a76990fb2453753a8d3fb4b62e5b3a26224 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 21:02:36 +0000 Subject: [PATCH 31/71] check_suite --- .github/workflows/src/verify-run-status.js | 4 +--- .github/workflows/watch-lintdiff.yaml | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 40119191d42c..67ebc962007f 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -10,9 +10,7 @@ import { extractInputs } from "./context.js"; export default async function({github, context, core}, checkRunName, workflowName) { const inputs = await extractInputs(github, context, core); - const checkRun = context.eventName == "check_run" - ? context.payload.check_run - : await getCheckRunStatus({github, context, core}, checkRunName, inputs.head_sha); + const checkRun = await getCheckRunStatus({github, context, core}, checkRunName, inputs.head_sha); const workflow = context.eventName == "workflow_run" ? context.payload.workflow_run : await getWorkflowRun({github, context, core}, workflowName, inputs.head_sha); diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index f9580d344e07..8ec36416978d 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -2,7 +2,7 @@ name: "~Watch - LintDiff" on: - check_run: + check_suite: types: completed workflow_run: @@ -15,9 +15,6 @@ permissions: jobs: check-lintdiff: - if: >- - github.event_name == 'workflow_run' || ( - github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff' ) runs-on: ubuntu-24.04 # TODO: Only run if check_run or workflow_run is initiated by pull_request From 067da1be2b99a1698c2cc753fe4ed6ea483b2446 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 22:28:40 +0000 Subject: [PATCH 32/71] Types --- .github/workflows/src/verify-run-status.js | 43 ++++++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 67ebc962007f..57a02e45a979 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -10,10 +10,10 @@ import { extractInputs } from "./context.js"; export default async function({github, context, core}, checkRunName, workflowName) { const inputs = await extractInputs(github, context, core); - const checkRun = await getCheckRunStatus({github, context, core}, checkRunName, inputs.head_sha); + const checkRun = await getCheckRunStatus(github, context, core, checkRunName, inputs.head_sha); const workflow = context.eventName == "workflow_run" ? context.payload.workflow_run - : await getWorkflowRun({github, context, core}, workflowName, inputs.head_sha); + : await getWorkflowRun(github, context, core, workflowName, inputs.head_sha); core.info(`Check run: ${JSON.stringify(checkRun)}`); core.info(`Workflow run: ${JSON.stringify(workflow)}`); @@ -35,15 +35,23 @@ export default async function({github, context, core}, checkRunName, workflowNam core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflow.conclusion})`); return false; } - } -export async function getCheckRunStatus({github, context, core}, checkRunName, headSha) { +/** + * + * @param {import('github-script').AsyncFunctionArguments['github']} github + * @param {import('github-script').AsyncFunctionArguments['context']} context + * @param {import('github-script').AsyncFunctionArguments['core']} core + * @param {string} checkRunName + * @param {string} head_sha + * @returns + */ +export async function getCheckRunStatus(github, context, core, checkRunName, head_sha) { const checkRuns = await github.paginate( github.rest.checks.listForRef, { ...context.repo, - ref: headSha, + ref: head_sha, check_name: checkRunName, status: "completed", per_page: PER_PAGE_MAX, @@ -65,13 +73,24 @@ export async function getCheckRunStatus({github, context, core}, checkRunName, h return checkRuns[0]; } -export async function getWorkflowRun({github, context, core}, workflowName, head_sha) { - const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({ - ...context.repo, - head_sha, - status: "completed", - per_page: PER_PAGE_MAX, - }); +/** + * @param {import('github-script').AsyncFunctionArguments['github']} github + * @param {import('github-script').AsyncFunctionArguments['context']} context + * @param {import('github-script').AsyncFunctionArguments['core']} core + * @param {string} workflowName + * @param {string} head_sha + * @returns + */ +export async function getWorkflowRun(github, context, core, workflowName, head_sha) { + + const workflowRuns = await github.paginate( + github.rest.actions.listWorkflowRunsForRepo, + { + ...context.repo, + head_sha, + status: "completed", + per_page: PER_PAGE_MAX, + }); core.info(`Workflow runs: ${JSON.stringify(workflowRuns)}`); if (workflowRuns.length === 0) { From cf840bd3c7efd63437de15870d79673c9291bb29 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 22:32:39 +0000 Subject: [PATCH 33/71] Parameter names --- .github/workflows/watch-lintdiff.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 8ec36416978d..5c0f6f7ee27f 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -40,6 +40,6 @@ jobs: const { default: verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); return await verifyRunStatus( { github, context, core }, - checkRunName: "Swagger LintDiff", - workflowRunName: "[TEST-IGNORE] Swagger LintDiff" + "Swagger LintDiff", + "[TEST-IGNORE] Swagger LintDiff" ); From 01fdbd5e128c8f33d4f223f3114febc4a1ef4ba7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Thu, 3 Apr 2025 22:38:12 +0000 Subject: [PATCH 34/71] core.debug --- .github/workflows/src/verify-run-status.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 57a02e45a979..160079aa8370 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -15,8 +15,8 @@ export default async function({github, context, core}, checkRunName, workflowNam ? context.payload.workflow_run : await getWorkflowRun(github, context, core, workflowName, inputs.head_sha); - core.info(`Check run: ${JSON.stringify(checkRun)}`); - core.info(`Workflow run: ${JSON.stringify(workflow)}`); + core.debug(`Check run: ${JSON.stringify(checkRun)}`); + core.debug(`Workflow run: ${JSON.stringify(workflow)}`); if (!checkRun || checkRun.status !== "completed") { core.info(`No completed check run with name: ${checkRunName}`); @@ -57,7 +57,7 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea per_page: PER_PAGE_MAX, } ); - core.info(`Check runs: ${JSON.stringify(checkRuns)}`); + core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); if (checkRuns.length === 0) { core.info(`No completed check run with name: ${checkRunName}`); @@ -66,7 +66,7 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea if (checkRuns.length > 1) { core.setFailed(`Multiple completed check runs with name: ${checkRunName}`); - core.info(`Check runs: ${JSON.stringify(checkRuns)}`); + core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); throw new Error(`Multiple completed check runs with name: ${checkRunName}`); } @@ -91,7 +91,7 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s status: "completed", per_page: PER_PAGE_MAX, }); - core.info(`Workflow runs: ${JSON.stringify(workflowRuns)}`); + core.debug(`Workflow runs: ${JSON.stringify(workflowRuns)}`); if (workflowRuns.length === 0) { core.info(`No completed workflow runs`); @@ -111,4 +111,4 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s } return matchingWorkflowRuns[0]; -} \ No newline at end of file +} From 2d903f5ea817747f1423dad674a58826e0f14ccc Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Fri, 4 Apr 2025 20:53:38 +0000 Subject: [PATCH 35/71] Use check_run, fix permission, reduce logging --- .github/workflows/src/verify-run-status.js | 6 +++--- .github/workflows/watch-lintdiff.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 160079aa8370..4027c7940f41 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -10,7 +10,9 @@ import { extractInputs } from "./context.js"; export default async function({github, context, core}, checkRunName, workflowName) { const inputs = await extractInputs(github, context, core); - const checkRun = await getCheckRunStatus(github, context, core, checkRunName, inputs.head_sha); + const checkRun = context.eventName == "check_run" + ? context.payload.check_run + : await getCheckRunStatus(github, context, core, checkRunName, inputs.head_sha); const workflow = context.eventName == "workflow_run" ? context.payload.workflow_run : await getWorkflowRun(github, context, core, workflowName, inputs.head_sha); @@ -60,7 +62,6 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); if (checkRuns.length === 0) { - core.info(`No completed check run with name: ${checkRunName}`); return null; } @@ -101,7 +102,6 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s const matchingWorkflowRuns = workflowRuns.filter((run) => run.name === workflowName); if (matchingWorkflowRuns.length === 0) { - core.info(`No completed workflow run with name: ${workflowName}`); return null; } diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 5c0f6f7ee27f..182da8155139 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -2,7 +2,7 @@ name: "~Watch - LintDiff" on: - check_suite: + check_run: types: completed workflow_run: @@ -11,7 +11,7 @@ on: - "\\[TEST-IGNORE\\] Swagger LintDiff" permissions: - checks: write + checks: read jobs: check-lintdiff: From 340bf6cf579303a77aec8716b7cf2ec62f1502a7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Sat, 5 Apr 2025 05:03:10 +0000 Subject: [PATCH 36/71] Refactoring and test coverage --- .github/workflows/src/verify-run-status.js | 26 +- .github/workflows/test/mocks.js | 1 + .../workflows/test/verify-run-status.test.js | 337 ++++++++++++++++++ .github/workflows/watch-lintdiff.yaml | 2 +- 4 files changed, 353 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/test/verify-run-status.test.js diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 4027c7940f41..d9cc828f2801 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -7,36 +7,38 @@ import { extractInputs } from "./context.js"; * @param {string} checkRunName * @param {string} workflowName */ -export default async function({github, context, core}, checkRunName, workflowName) { - const inputs = await extractInputs(github, context, core); +export async function verifyRunStatus({github, context, core}, checkRunName, workflowName) { + const { head_sha } = await extractInputs(github, context, core); const checkRun = context.eventName == "check_run" ? context.payload.check_run - : await getCheckRunStatus(github, context, core, checkRunName, inputs.head_sha); - const workflow = context.eventName == "workflow_run" + : await getCheckRunStatus(github, context, core, checkRunName, head_sha); + const workflowRun = context.eventName == "workflow_run" ? context.payload.workflow_run - : await getWorkflowRun(github, context, core, workflowName, inputs.head_sha); + : await getWorkflowRun(github, context, core, workflowName, head_sha); core.debug(`Check run: ${JSON.stringify(checkRun)}`); - core.debug(`Workflow run: ${JSON.stringify(workflow)}`); + core.debug(`Workflow run: ${JSON.stringify(workflowRun)}`); - if (!checkRun || checkRun.status !== "completed") { + if (!checkRun) { core.info(`No completed check run with name: ${checkRunName}`); return; } - if (!checkRun || workflow.status !== "completed") { + if (!workflowRun) { core.info(`No completed workflow run with name: ${workflowName}`); return; } core.info(`Check run conclusion: ${checkRun.conclusion}`); - core.info(`Workflow run conclusion: ${workflow.conclusion}`); + core.info(`Workflow run conclusion: ${workflowRun.conclusion}`); - if (checkRun.conclusion !== workflow.conclusion) { - core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflow.conclusion})`); - return false; + if (checkRun.conclusion !== workflowRun.conclusion) { + core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflowRun.conclusion})`); + return; } + + core.info("Checks match"); } /** diff --git a/.github/workflows/test/mocks.js b/.github/workflows/test/mocks.js index ed845f6f8d5b..e4f8ae24e7ee 100644 --- a/.github/workflows/test/mocks.js +++ b/.github/workflows/test/mocks.js @@ -54,6 +54,7 @@ export function createMockCore() { setOutput: vi.fn((name, value) => console.log(`setOutput('${name}', '${value}')`), ), + setFailed: vi.fn((msg) => console.log(`setFailed('${msg}')`)), }; } diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js new file mode 100644 index 000000000000..6c26692500b8 --- /dev/null +++ b/.github/workflows/test/verify-run-status.test.js @@ -0,0 +1,337 @@ +import { describe, expect, it, vi } from "vitest"; +import { + createMockGithub, + createMockContext, + createMockCore, +} from "./mocks.js"; +import { getCheckRunStatus, getWorkflowRun, verifyRunStatus } from "../src/verify-run-status.js"; + +vi.mock("../src/context.js", () => { + return { + extractInputs: vi.fn().mockResolvedValue({ + head_sha: "head_sha", + }), + }; +}); + +describe("getCheckRunStatus", () => { + it("returns matching check_run", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [ + { + name: "checkRunName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + const actual = await getCheckRunStatus( + githubMock, + createMockContext(), + createMockCore(), + "checkRunName", + "head_sha" + ); + + expect(actual).toEqual(expect.objectContaining({ + name: "checkRunName", + status: "completed", + conclusion: "success", + })); + }); + + it("returns null when no check matches", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [], + }, + }); + + const actual = await getCheckRunStatus( + githubMock, + createMockContext(), + createMockCore(), + "checkRunName", + "head_sha" + ); + + expect(actual).toBeNull(); + }); + + it("throws when multiple checks match", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [ + { + name: "checkRunName", + status: "completed", + conclusion: "success", + }, + { + name: "checkRunName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + await expect(async () => await getCheckRunStatus( + githubMock, + createMockContext(), + createMockCore(), + "checkRunName", + "head_sha" + )).rejects.toThrow(/Multiple completed check runs with name/); + }); +}); + +describe("getWorkflowRun", () => { + it("returns matching workflow_run", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + const actual = await getWorkflowRun( + githubMock, + createMockContext(), + createMockCore(), + "workflowName", + "head_sha" + ); + + expect(actual).toEqual(expect.objectContaining({ + name: "workflowName", + status: "completed", + conclusion: "success", + })); + }); + + it("returns null when no workflow matches", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "otherWorkflowName" + } + ], + }, + }); + + const actual = await getWorkflowRun( + githubMock, + createMockContext(), + createMockCore(), + "workflowName", + "head_sha" + ); + + expect(actual).toBeNull(); + }); + + it("throws when multiple workflows match", async ()=> { + const githubMock = createMockGithub(); + githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + await expect(async () => await getWorkflowRun( + githubMock, + createMockContext(), + createMockCore(), + "workflowName", + "head_sha" + )).rejects.toThrow(/Multiple completed workflow runs with name/); + }); +}); + +describe("verifyRunStatus", () => { + it("verifies status when check_run event fires", async () => { + const github = createMockGithub(); + github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + const context = { + eventName: "check_run", + payload: { + check_run: { + name: "checkRunName", + conclusion: "success", + }, + } + }; + + const core = createMockCore(); + + await verifyRunStatus( + { github, context, core }, + "checkRunName", + "workflowName" + ); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(core.info).toHaveBeenCalledWith("Checks match"); + }); + + it("verifies status when workflow_run event fires", async () => { + const github = createMockGithub(); + github.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [ + { + name: "checkRunName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + const context = { + eventName: "workflow_run", + payload: { + workflow_run: { + name: "workflowName", + conclusion: "success", + } + }, + }; + + const core = createMockCore(); + + await verifyRunStatus( + { github, context, core }, + "checkRunName", + "workflowName" + ); + + expect(core.setFailed).not.toHaveBeenCalled(); + }); + + it("returns early during workflow_run event when no matching check_run is found", async () => { + const github = createMockGithub(); + github.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [], + }, + }); + + const context = { + eventName: "workflow_run", + payload: { + workflow_run: { + name: "workflowName", + conclusion: "success", + } + }, + }; + const core = createMockCore(); + await verifyRunStatus( + { github, context, core }, + "checkRunName", + "workflowName" + ); + expect(core.setFailed).not.toHaveBeenCalled(); + expect(core.info).toHaveBeenCalledWith("No completed check run with name: checkRunName"); + }); + + it("returns early during check_run event when no matching workflow_run is found", async () => { + const github = createMockGithub(); + github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [], + }, + }); + + const context = { + eventName: "check_run", + payload: { + check_run: { + name: "checkRunName", + conclusion: "success", + }, + } + }; + const core = createMockCore(); + await verifyRunStatus( + { github, context, core }, + "checkRunName", + "workflowName" + ); + expect(core.setFailed).not.toHaveBeenCalled(); + expect(core.info).toHaveBeenCalledWith("No completed workflow run with name: workflowName"); + }); + + it("throws if check_run conclusion does not match workflow_run conclusion", async () => { + const github = createMockGithub(); + github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "failure", + }, + ], + }, + }); + + const context = { + eventName: "check_run", + payload: { + check_run: { + name: "checkRunName", + conclusion: "success", + }, + } + }; + const core = createMockCore(); + await verifyRunStatus( + { github, context, core }, + "checkRunName", + "workflowName" + ); + expect(core.setFailed).toHaveBeenCalledWith( + "Check run conclusion (success) does not match workflow run conclusion (failure)" + ); + }); + +}); \ No newline at end of file diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 182da8155139..c39e3418e529 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -37,7 +37,7 @@ jobs: uses: actions/github-script@v7 with: script: | - const { default: verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); + const { verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); return await verifyRunStatus( { github, context, core }, "Swagger LintDiff", From 05faf7e4f1947424e937e384a6fafd1588995f0b Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Sat, 5 Apr 2025 05:15:00 +0000 Subject: [PATCH 37/71] Output event info --- .github/workflows/src/verify-run-status.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index d9cc828f2801..1328b77c619a 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -68,9 +68,10 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea } if (checkRuns.length > 1) { - core.setFailed(`Multiple completed check runs with name: ${checkRunName}`); + const message = `Multiple completed check runs with name: ${checkRunName}`; + core.setFailed(message); core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); - throw new Error(`Multiple completed check runs with name: ${checkRunName}`); + throw new Error(message); } return checkRuns[0]; @@ -108,8 +109,10 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s } if (matchingWorkflowRuns.length > 1) { - core.setFailed(`Multiple completed workflow runs with name: ${workflowName}`); - throw new Error(`Multiple completed workflow runs with name: ${workflowName}`); + const message = `Multiple completed workflow runs with name: ${workflowName}`; + core.debug(`Workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); + core.setFailed(message); + throw new Error(message); } return matchingWorkflowRuns[0]; From 0d5705ba3a6d56845220edc1218a79ac92a545c1 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Sun, 6 Apr 2025 04:28:55 +0000 Subject: [PATCH 38/71] In the case of multiple matching workflow runs, use the latest. --- .github/workflows/src/verify-run-status.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 1328b77c619a..56409f31c443 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -109,10 +109,16 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s } if (matchingWorkflowRuns.length > 1) { - const message = `Multiple completed workflow runs with name: ${workflowName}`; - core.debug(`Workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); - core.setFailed(message); - throw new Error(message); + // TODO: workflowRuns have event: "pull_request" but the "pull_requests" + // field is an empty array. Consider matching on "repository" in the case of + // "pull_request" events. + core.warning(`Multiple matching workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); + + matchingWorkflowRuns.sort((a, b) => + new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() + ); + + core.debug(`Multiple matching workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); } return matchingWorkflowRuns[0]; From c5f44c8d6eb53adbda54583768a6692dc7d2926d Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 04:41:51 +0000 Subject: [PATCH 39/71] Adjust flow to use fewer API calls --- .github/workflows/src/verify-run-status.js | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 56409f31c443..8ed256f8747f 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -10,28 +10,39 @@ import { extractInputs } from "./context.js"; export async function verifyRunStatus({github, context, core}, checkRunName, workflowName) { const { head_sha } = await extractInputs(github, context, core); + // Exit early when context is a check_run event and the check run does not + // match the checkRunName. + if (context.eventName == "check_run") { + const contextRunName = context.payload.check_run.name; + if (contextRunName !== checkRunName) { + core.info(`Check run name (${contextRunName}) does not match input: ${checkRunName}`); + return; + } + } + const checkRun = context.eventName == "check_run" ? context.payload.check_run : await getCheckRunStatus(github, context, core, checkRunName, head_sha); - const workflowRun = context.eventName == "workflow_run" - ? context.payload.workflow_run - : await getWorkflowRun(github, context, core, workflowName, head_sha); - - core.debug(`Check run: ${JSON.stringify(checkRun)}`); - core.debug(`Workflow run: ${JSON.stringify(workflowRun)}`); if (!checkRun) { core.info(`No completed check run with name: ${checkRunName}`); return; } + core.info(`Check run name: ${checkRun.name} conclusion: ${checkRun.conclusion}`); + core.debug(`Check run: ${JSON.stringify(checkRun)}`); + + const workflowRun = context.eventName == "workflow_run" + ? context.payload.workflow_run + : await getWorkflowRun(github, context, core, workflowName, head_sha); + if (!workflowRun) { core.info(`No completed workflow run with name: ${workflowName}`); return; } - core.info(`Check run conclusion: ${checkRun.conclusion}`); - core.info(`Workflow run conclusion: ${workflowRun.conclusion}`); + core.info(`Workflow run name: ${workflowRun.name} conclusion: ${workflowRun.conclusion}`); + core.debug(`Workflow run: ${JSON.stringify(workflowRun)}`); if (checkRun.conclusion !== workflowRun.conclusion) { core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflowRun.conclusion})`); From d4b52961c934ec078e0e8326892efe5bb7dfa124 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 04:45:51 +0000 Subject: [PATCH 40/71] Only run on "check_run" if name matches --- .github/workflows/watch-lintdiff.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index c39e3418e529..cb70144d52c3 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -16,7 +16,12 @@ permissions: jobs: check-lintdiff: runs-on: ubuntu-24.04 - # TODO: Only run if check_run or workflow_run is initiated by pull_request + + # TODO: Parameterize github.event.check_run.name + if: >- + github.event_name == 'workflow_run' || + (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') || + (github.event_name == 'check_suite') steps: # *** IMPORTANT *** From dcea1cc09cac43697733be3a598821c6b98a594a Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 04:46:03 +0000 Subject: [PATCH 41/71] Remove comment, already filtering on repo --- .github/workflows/src/verify-run-status.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 8ed256f8747f..792c04f1aa44 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -120,9 +120,6 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s } if (matchingWorkflowRuns.length > 1) { - // TODO: workflowRuns have event: "pull_request" but the "pull_requests" - // field is an empty array. Consider matching on "repository" in the case of - // "pull_request" events. core.warning(`Multiple matching workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); matchingWorkflowRuns.sort((a, b) => From 5ca2f7152f4876fe9c4b337318222be6f8255f28 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 04:51:57 +0000 Subject: [PATCH 42/71] Logging cleanup --- .github/workflows/src/verify-run-status.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 792c04f1aa44..12e2ca3e6d4b 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -79,9 +79,13 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea } if (checkRuns.length > 1) { + core.info(`Multiple check runs:`); + checkRuns.forEach((cr) => { + core.info(`- ${cr.name}: ${cr.conclusion}`); + }); + const message = `Multiple completed check runs with name: ${checkRunName}`; - core.setFailed(message); - core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); + core.setFailed(message); throw new Error(message); } @@ -120,13 +124,15 @@ export async function getWorkflowRun(github, context, core, workflowName, head_s } if (matchingWorkflowRuns.length > 1) { - core.warning(`Multiple matching workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); + core.warning(`Multiple matching workflow runs, selecting the most recent run`); + matchingWorkflowRuns.forEach((wf) => { + core.info(`- ${wf.name}: ${wf.conclusion || wf.status}`); + }); + // Sort by "updated_at" descending, so most recent run is at index 0 matchingWorkflowRuns.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() ); - - core.debug(`Multiple matching workflow runs: ${JSON.stringify(matchingWorkflowRuns)}`); } return matchingWorkflowRuns[0]; From e6ee4e65725729eee852a263a2f173f485335d53 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 04:53:16 +0000 Subject: [PATCH 43/71] Formatting --- .github/workflows/src/verify-run-status.js | 122 +++++++---- .../workflows/test/verify-run-status.test.js | 205 ++++++++++-------- 2 files changed, 189 insertions(+), 138 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 12e2ca3e6d4b..6611b917d3d1 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -1,51 +1,65 @@ -import { PER_PAGE_MAX } from "./github.js" +import { PER_PAGE_MAX } from "./github.js"; import { extractInputs } from "./context.js"; /** - * - * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments - * @param {string} checkRunName + * + * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @param {string} checkRunName * @param {string} workflowName */ -export async function verifyRunStatus({github, context, core}, checkRunName, workflowName) { +export async function verifyRunStatus( + { github, context, core }, + checkRunName, + workflowName, +) { const { head_sha } = await extractInputs(github, context, core); - // Exit early when context is a check_run event and the check run does not + // Exit early when context is a check_run event and the check run does not // match the checkRunName. if (context.eventName == "check_run") { const contextRunName = context.payload.check_run.name; if (contextRunName !== checkRunName) { - core.info(`Check run name (${contextRunName}) does not match input: ${checkRunName}`); + core.info( + `Check run name (${contextRunName}) does not match input: ${checkRunName}`, + ); return; } } - const checkRun = context.eventName == "check_run" - ? context.payload.check_run - : await getCheckRunStatus(github, context, core, checkRunName, head_sha); + const checkRun = + context.eventName == "check_run" + ? context.payload.check_run + : await getCheckRunStatus(github, context, core, checkRunName, head_sha); - if (!checkRun) { + if (!checkRun) { core.info(`No completed check run with name: ${checkRunName}`); return; } - core.info(`Check run name: ${checkRun.name} conclusion: ${checkRun.conclusion}`); + core.info( + `Check run name: ${checkRun.name} conclusion: ${checkRun.conclusion}`, + ); core.debug(`Check run: ${JSON.stringify(checkRun)}`); - const workflowRun = context.eventName == "workflow_run" - ? context.payload.workflow_run - : await getWorkflowRun(github, context, core, workflowName, head_sha); + const workflowRun = + context.eventName == "workflow_run" + ? context.payload.workflow_run + : await getWorkflowRun(github, context, core, workflowName, head_sha); - if (!workflowRun) { + if (!workflowRun) { core.info(`No completed workflow run with name: ${workflowName}`); return; } - core.info(`Workflow run name: ${workflowRun.name} conclusion: ${workflowRun.conclusion}`); + core.info( + `Workflow run name: ${workflowRun.name} conclusion: ${workflowRun.conclusion}`, + ); core.debug(`Workflow run: ${JSON.stringify(workflowRun)}`); if (checkRun.conclusion !== workflowRun.conclusion) { - core.setFailed(`Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflowRun.conclusion})`); + core.setFailed( + `Check run conclusion (${checkRun.conclusion}) does not match workflow run conclusion (${workflowRun.conclusion})`, + ); return; } @@ -53,25 +67,28 @@ export async function verifyRunStatus({github, context, core}, checkRunName, wor } /** - * + * * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context * @param {import('github-script').AsyncFunctionArguments['core']} core - * @param {string} checkRunName - * @param {string} head_sha - * @returns + * @param {string} checkRunName + * @param {string} head_sha + * @returns */ -export async function getCheckRunStatus(github, context, core, checkRunName, head_sha) { - const checkRuns = await github.paginate( - github.rest.checks.listForRef, - { - ...context.repo, - ref: head_sha, - check_name: checkRunName, - status: "completed", - per_page: PER_PAGE_MAX, - } - ); +export async function getCheckRunStatus( + github, + context, + core, + checkRunName, + head_sha, +) { + const checkRuns = await github.paginate(github.rest.checks.listForRef, { + ...context.repo, + ref: head_sha, + check_name: checkRunName, + status: "completed", + per_page: PER_PAGE_MAX, + }); core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); if (checkRuns.length === 0) { @@ -83,9 +100,9 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea checkRuns.forEach((cr) => { core.info(`- ${cr.name}: ${cr.conclusion}`); }); - + const message = `Multiple completed check runs with name: ${checkRunName}`; - core.setFailed(message); + core.setFailed(message); throw new Error(message); } @@ -96,42 +113,53 @@ export async function getCheckRunStatus(github, context, core, checkRunName, hea * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context * @param {import('github-script').AsyncFunctionArguments['core']} core - * @param {string} workflowName - * @param {string} head_sha - * @returns + * @param {string} workflowName + * @param {string} head_sha + * @returns */ -export async function getWorkflowRun(github, context, core, workflowName, head_sha) { - +export async function getWorkflowRun( + github, + context, + core, + workflowName, + head_sha, +) { const workflowRuns = await github.paginate( - github.rest.actions.listWorkflowRunsForRepo, + github.rest.actions.listWorkflowRunsForRepo, { ...context.repo, head_sha, status: "completed", per_page: PER_PAGE_MAX, - }); + }, + ); core.debug(`Workflow runs: ${JSON.stringify(workflowRuns)}`); - if (workflowRuns.length === 0) { + if (workflowRuns.length === 0) { core.info(`No completed workflow runs`); return null; } - const matchingWorkflowRuns = workflowRuns.filter((run) => run.name === workflowName); + const matchingWorkflowRuns = workflowRuns.filter( + (run) => run.name === workflowName, + ); if (matchingWorkflowRuns.length === 0) { return null; } if (matchingWorkflowRuns.length > 1) { - core.warning(`Multiple matching workflow runs, selecting the most recent run`); + core.warning( + `Multiple matching workflow runs, selecting the most recent run`, + ); matchingWorkflowRuns.forEach((wf) => { core.info(`- ${wf.name}: ${wf.conclusion || wf.status}`); }); // Sort by "updated_at" descending, so most recent run is at index 0 - matchingWorkflowRuns.sort((a, b) => - new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() + matchingWorkflowRuns.sort( + (a, b) => + new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), ); } diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index 6c26692500b8..4d25ab1752d9 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -4,7 +4,11 @@ import { createMockContext, createMockCore, } from "./mocks.js"; -import { getCheckRunStatus, getWorkflowRun, verifyRunStatus } from "../src/verify-run-status.js"; +import { + getCheckRunStatus, + getWorkflowRun, + verifyRunStatus, +} from "../src/verify-run-status.js"; vi.mock("../src/context.js", () => { return { @@ -15,7 +19,7 @@ vi.mock("../src/context.js", () => { }); describe("getCheckRunStatus", () => { - it("returns matching check_run", async ()=> { + it("returns matching check_run", async () => { const githubMock = createMockGithub(); githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ data: { @@ -31,20 +35,22 @@ describe("getCheckRunStatus", () => { const actual = await getCheckRunStatus( githubMock, - createMockContext(), + createMockContext(), createMockCore(), "checkRunName", - "head_sha" + "head_sha", ); - expect(actual).toEqual(expect.objectContaining({ - name: "checkRunName", - status: "completed", - conclusion: "success", - })); + expect(actual).toEqual( + expect.objectContaining({ + name: "checkRunName", + status: "completed", + conclusion: "success", + }), + ); }); - it("returns null when no check matches", async ()=> { + it("returns null when no check matches", async () => { const githubMock = createMockGithub(); githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ data: { @@ -54,16 +60,16 @@ describe("getCheckRunStatus", () => { const actual = await getCheckRunStatus( githubMock, - createMockContext(), + createMockContext(), createMockCore(), "checkRunName", - "head_sha" + "head_sha", ); expect(actual).toBeNull(); }); - it("throws when multiple checks match", async ()=> { + it("throws when multiple checks match", async () => { const githubMock = createMockGithub(); githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ data: { @@ -82,95 +88,109 @@ describe("getCheckRunStatus", () => { }, }); - await expect(async () => await getCheckRunStatus( - githubMock, - createMockContext(), - createMockCore(), - "checkRunName", - "head_sha" - )).rejects.toThrow(/Multiple completed check runs with name/); + await expect( + async () => + await getCheckRunStatus( + githubMock, + createMockContext(), + createMockCore(), + "checkRunName", + "head_sha", + ), + ).rejects.toThrow(/Multiple completed check runs with name/); }); }); describe("getWorkflowRun", () => { - it("returns matching workflow_run", async ()=> { + it("returns matching workflow_run", async () => { const githubMock = createMockGithub(); - githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ - data: { - workflow_runs: [ - { - name: "workflowName", - status: "completed", - conclusion: "success", - }, - ], - }, - }); + githubMock.rest.actions.listWorkflowRunsForRepo = vi + .fn() + .mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); const actual = await getWorkflowRun( githubMock, - createMockContext(), + createMockContext(), createMockCore(), "workflowName", - "head_sha" + "head_sha", ); - expect(actual).toEqual(expect.objectContaining({ - name: "workflowName", - status: "completed", - conclusion: "success", - })); + expect(actual).toEqual( + expect.objectContaining({ + name: "workflowName", + status: "completed", + conclusion: "success", + }), + ); }); - it("returns null when no workflow matches", async ()=> { + it("returns null when no workflow matches", async () => { const githubMock = createMockGithub(); - githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ - data: { - workflow_runs: [ - { - name: "otherWorkflowName" - } - ], - }, - }); + githubMock.rest.actions.listWorkflowRunsForRepo = vi + .fn() + .mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "otherWorkflowName", + }, + ], + }, + }); const actual = await getWorkflowRun( githubMock, - createMockContext(), + createMockContext(), createMockCore(), "workflowName", - "head_sha" + "head_sha", ); expect(actual).toBeNull(); }); - it("throws when multiple workflows match", async ()=> { + it("throws when multiple workflows match", async () => { const githubMock = createMockGithub(); - githubMock.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ - data: { - workflow_runs: [ - { - name: "workflowName", - status: "completed", - conclusion: "success", - }, - { - name: "workflowName", - status: "completed", - conclusion: "success", - }, - ], - }, - }); - - await expect(async () => await getWorkflowRun( - githubMock, - createMockContext(), - createMockCore(), - "workflowName", - "head_sha" - )).rejects.toThrow(/Multiple completed workflow runs with name/); + githubMock.rest.actions.listWorkflowRunsForRepo = vi + .fn() + .mockResolvedValue({ + data: { + workflow_runs: [ + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + { + name: "workflowName", + status: "completed", + conclusion: "success", + }, + ], + }, + }); + + await expect( + async () => + await getWorkflowRun( + githubMock, + createMockContext(), + createMockCore(), + "workflowName", + "head_sha", + ), + ).rejects.toThrow(/Multiple completed workflow runs with name/); }); }); @@ -196,7 +216,7 @@ describe("verifyRunStatus", () => { name: "checkRunName", conclusion: "success", }, - } + }, }; const core = createMockCore(); @@ -204,7 +224,7 @@ describe("verifyRunStatus", () => { await verifyRunStatus( { github, context, core }, "checkRunName", - "workflowName" + "workflowName", ); expect(core.setFailed).not.toHaveBeenCalled(); @@ -231,7 +251,7 @@ describe("verifyRunStatus", () => { workflow_run: { name: "workflowName", conclusion: "success", - } + }, }, }; @@ -240,7 +260,7 @@ describe("verifyRunStatus", () => { await verifyRunStatus( { github, context, core }, "checkRunName", - "workflowName" + "workflowName", ); expect(core.setFailed).not.toHaveBeenCalled(); @@ -260,17 +280,19 @@ describe("verifyRunStatus", () => { workflow_run: { name: "workflowName", conclusion: "success", - } + }, }, }; const core = createMockCore(); await verifyRunStatus( { github, context, core }, "checkRunName", - "workflowName" + "workflowName", ); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith("No completed check run with name: checkRunName"); + expect(core.info).toHaveBeenCalledWith( + "No completed check run with name: checkRunName", + ); }); it("returns early during check_run event when no matching workflow_run is found", async () => { @@ -288,16 +310,18 @@ describe("verifyRunStatus", () => { name: "checkRunName", conclusion: "success", }, - } + }, }; const core = createMockCore(); await verifyRunStatus( { github, context, core }, "checkRunName", - "workflowName" + "workflowName", ); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith("No completed workflow run with name: workflowName"); + expect(core.info).toHaveBeenCalledWith( + "No completed workflow run with name: workflowName", + ); }); it("throws if check_run conclusion does not match workflow_run conclusion", async () => { @@ -321,17 +345,16 @@ describe("verifyRunStatus", () => { name: "checkRunName", conclusion: "success", }, - } + }, }; const core = createMockCore(); await verifyRunStatus( { github, context, core }, "checkRunName", - "workflowName" + "workflowName", ); expect(core.setFailed).toHaveBeenCalledWith( - "Check run conclusion (success) does not match workflow run conclusion (failure)" + "Check run conclusion (success) does not match workflow run conclusion (failure)", ); }); - -}); \ No newline at end of file +}); From a98ef4befbb8d80dd8ce87d3294f302d4d0b8a7f Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:02:03 +0000 Subject: [PATCH 44/71] Coverage and cleanup --- .github/workflows/src/verify-run-status.js | 16 ++---- .../workflows/test/verify-run-status.test.js | 55 +++++++++++++++---- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 6611b917d3d1..323f465c79a8 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -89,7 +89,6 @@ export async function getCheckRunStatus( status: "completed", per_page: PER_PAGE_MAX, }); - core.debug(`Check runs: ${JSON.stringify(checkRuns)}`); if (checkRuns.length === 0) { return null; @@ -133,35 +132,32 @@ export async function getWorkflowRun( per_page: PER_PAGE_MAX, }, ); - core.debug(`Workflow runs: ${JSON.stringify(workflowRuns)}`); if (workflowRuns.length === 0) { core.info(`No completed workflow runs`); return null; } - const matchingWorkflowRuns = workflowRuns.filter( + const matches = workflowRuns.filter( (run) => run.name === workflowName, ); - if (matchingWorkflowRuns.length === 0) { + if (matches.length === 0) { return null; } - if (matchingWorkflowRuns.length > 1) { + if (matches.length > 1) { core.warning( `Multiple matching workflow runs, selecting the most recent run`, ); - matchingWorkflowRuns.forEach((wf) => { - core.info(`- ${wf.name}: ${wf.conclusion || wf.status}`); - }); + matches.forEach((wf) => core.info(`- ${wf.name}: ${wf.conclusion}`)); // Sort by "updated_at" descending, so most recent run is at index 0 - matchingWorkflowRuns.sort( + matches.sort( (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), ); } - return matchingWorkflowRuns[0]; + return matches[0]; } diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index 4d25ab1752d9..6bfb16a81862 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -160,8 +160,10 @@ describe("getWorkflowRun", () => { expect(actual).toBeNull(); }); - it("throws when multiple workflows match", async () => { + it("returns latest when multiple workflows match", async () => { const githubMock = createMockGithub(); + const earlyDate = "2025-04-01T00:00:00Z"; + const laterDate = "2025-04-02T00:00:00Z" githubMock.rest.actions.listWorkflowRunsForRepo = vi .fn() .mockResolvedValue({ @@ -171,26 +173,34 @@ describe("getWorkflowRun", () => { name: "workflowName", status: "completed", conclusion: "success", + updated_at: earlyDate }, { name: "workflowName", status: "completed", conclusion: "success", + updated_at: laterDate }, ], }, }); - await expect( - async () => - await getWorkflowRun( - githubMock, - createMockContext(), - createMockCore(), - "workflowName", - "head_sha", - ), - ).rejects.toThrow(/Multiple completed workflow runs with name/); + const actual = await getWorkflowRun( + githubMock, + createMockContext(), + createMockCore(), + "workflowName", + "head_sha", + ) + + expect(actual).toEqual( + expect.objectContaining({ + name: "workflowName", + status: "completed", + conclusion: "success", + updated_at: laterDate + }), + ); }); }); @@ -324,6 +334,29 @@ describe("verifyRunStatus", () => { ); }); + it ("returns early if event is check_run but does not match input name", async () => { + const github = createMockGithub(); + const context = { + eventName: "check_run", + payload: { + check_run: { + name: "checkRunName", + conclusion: "success", + }, + }, + }; + const core = createMockCore(); + await verifyRunStatus( + { github, context, core }, + "otherCheckRunName", + "workflowName", + ); + expect(core.setFailed).not.toHaveBeenCalled(); + expect(core.info).toHaveBeenCalledWith( + "Check run name (checkRunName) does not match input: otherCheckRunName", + ); + }); + it("throws if check_run conclusion does not match workflow_run conclusion", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ From ff7cfc2d1913435f1895f305ef5f4b1f29f1bf51 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:09:08 +0000 Subject: [PATCH 45/71] Workflow run URL --- .github/workflows/src/verify-run-status.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 323f465c79a8..ea196d3eea8c 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -150,7 +150,7 @@ export async function getWorkflowRun( core.warning( `Multiple matching workflow runs, selecting the most recent run`, ); - matches.forEach((wf) => core.info(`- ${wf.name}: ${wf.conclusion}`)); + matches.forEach((wf) => core.info(`- ${wf.name}: ${wf.conclusion} ${wf.html_url}`)); // Sort by "updated_at" descending, so most recent run is at index 0 matches.sort( From e51599fa8234970a9de3565505cb12372ab1abfc Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:15:26 +0000 Subject: [PATCH 46/71] formatting --- .github/workflows/src/verify-run-status.js | 8 ++++---- .github/workflows/test/verify-run-status.test.js | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index ea196d3eea8c..16baedfda9f3 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -138,9 +138,7 @@ export async function getWorkflowRun( return null; } - const matches = workflowRuns.filter( - (run) => run.name === workflowName, - ); + const matches = workflowRuns.filter((run) => run.name === workflowName); if (matches.length === 0) { return null; @@ -150,7 +148,9 @@ export async function getWorkflowRun( core.warning( `Multiple matching workflow runs, selecting the most recent run`, ); - matches.forEach((wf) => core.info(`- ${wf.name}: ${wf.conclusion} ${wf.html_url}`)); + matches.forEach((wf) => + core.info(`- ${wf.name}: ${wf.conclusion} ${wf.html_url}`), + ); // Sort by "updated_at" descending, so most recent run is at index 0 matches.sort( diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index 6bfb16a81862..bb8263668540 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -163,7 +163,7 @@ describe("getWorkflowRun", () => { it("returns latest when multiple workflows match", async () => { const githubMock = createMockGithub(); const earlyDate = "2025-04-01T00:00:00Z"; - const laterDate = "2025-04-02T00:00:00Z" + const laterDate = "2025-04-02T00:00:00Z"; githubMock.rest.actions.listWorkflowRunsForRepo = vi .fn() .mockResolvedValue({ @@ -173,13 +173,13 @@ describe("getWorkflowRun", () => { name: "workflowName", status: "completed", conclusion: "success", - updated_at: earlyDate + updated_at: earlyDate, }, { name: "workflowName", status: "completed", conclusion: "success", - updated_at: laterDate + updated_at: laterDate, }, ], }, @@ -191,14 +191,14 @@ describe("getWorkflowRun", () => { createMockCore(), "workflowName", "head_sha", - ) + ); expect(actual).toEqual( expect.objectContaining({ name: "workflowName", status: "completed", conclusion: "success", - updated_at: laterDate + updated_at: laterDate, }), ); }); @@ -334,7 +334,7 @@ describe("verifyRunStatus", () => { ); }); - it ("returns early if event is check_run but does not match input name", async () => { + it("returns early if event is check_run but does not match input name", async () => { const github = createMockGithub(); const context = { eventName: "check_run", From 2724b31de1deed3c1281a7979eb0baaa3f51cea4 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:15:38 +0000 Subject: [PATCH 47/71] Refactor watch-lintdiff to use reusable workflow --- .../_reusable-verify-run-status.yaml | 52 +++++++++++++++++++ .github/workflows/watch-lintdiff.yaml | 42 +++------------ 2 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/_reusable-verify-run-status.yaml diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml new file mode 100644 index 000000000000..44dbdcd4696b --- /dev/null +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -0,0 +1,52 @@ +# Prefix with "~" to sort last in Actions list +name: ~Templates - Verify Run Status + +on: + workflow_call: + inputs: + check_run_name: + description: Name of the check run to verify + required: true + type: string + workflow_name: + description: Name of the workflow to verify + required: true + type: string + +permissions: + checks: read + +jobs: + check-run-status: + runs-on: ubuntu-24.04 + + if: >- + github.event_name == 'workflow_run' || + (github.event_name == 'check_run' && github.event.check_run.name == '${{ inputs.check_run_name }}') || + (github.event_name == 'check_suite') + + steps: + # *** IMPORTANT *** + # For workflows that are triggered by the pull_request_target event, the workflow runs in the + # context of the base of the pull request. You should make sure that you do not check out, + # build, or run untrusted code from the head of the pull request. + - uses: actions/checkout@v4 + with: + # Only needs .github folder for automation, not the files in the PR (analyzed in a + # separate workflow). + # + # Uses the .github folder from the PR base branch (pull_request_target trigger), + # or the repo default branch (other triggers). + sparse-checkout: | + .github + + - name: Verify matching status + uses: actions/github-script@v7 + with: + script: | + const { verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); + return await verifyRunStatus( + { github, context, core }, + "${{ inputs.check_run_name }}", + "${{ inputs.workflow_name }}" + ); diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index cb70144d52c3..5797a4e1d14a 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -2,6 +2,8 @@ name: "~Watch - LintDiff" on: + # check_suite is preferred over check_run to avoid triggering on all check + # runs. In some cases, check_run must be used in testing environments. check_run: types: completed @@ -14,37 +16,9 @@ permissions: checks: read jobs: - check-lintdiff: - runs-on: ubuntu-24.04 - - # TODO: Parameterize github.event.check_run.name - if: >- - github.event_name == 'workflow_run' || - (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') || - (github.event_name == 'check_suite') - - steps: - # *** IMPORTANT *** - # For workflows that are triggered by the pull_request_target event, the workflow runs in the - # context of the base of the pull request. You should make sure that you do not check out, - # build, or run untrusted code from the head of the pull request. - - uses: actions/checkout@v4 - with: - # Only needs .github folder for automation, not the files in the PR (analyzed in a - # separate workflow). - # - # Uses the .github folder from the PR base branch (pull_request_target trigger), - # or the repo default branch (other triggers). - sparse-checkout: | - .github - - - name: Verify matching status - uses: actions/github-script@v7 - with: - script: | - const { verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); - return await verifyRunStatus( - { github, context, core }, - "Swagger LintDiff", - "[TEST-IGNORE] Swagger LintDiff" - ); + LintDiff: + name: Lint Diff + uses: ./.github/workflows/_reusable-verify-run-status.yaml + with: + check_run_name: "Lint Diff" + workflow_name: "\\[TEST-IGNORE\\] Swagger LintDiff" From a11b5bf08422c04360cc5f42521563b726f10e17 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:38:49 +0000 Subject: [PATCH 48/71] Name, condition --- .github/workflows/_reusable-verify-run-status.yaml | 2 +- .github/workflows/watch-lintdiff.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index 44dbdcd4696b..b9ee6aa149b0 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -22,7 +22,7 @@ jobs: if: >- github.event_name == 'workflow_run' || - (github.event_name == 'check_run' && github.event.check_run.name == '${{ inputs.check_run_name }}') || + (github.event_name == 'check_run' && github.event.check_run.name == '${{ github.event.inputs.check_run_name }}') || (github.event_name == 'check_suite') steps: diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 5797a4e1d14a..7bc120a4259b 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -20,5 +20,5 @@ jobs: name: Lint Diff uses: ./.github/workflows/_reusable-verify-run-status.yaml with: - check_run_name: "Lint Diff" + check_run_name: "Swagger LintDiff" workflow_name: "\\[TEST-IGNORE\\] Swagger LintDiff" From fb03a8b63472e6dffee0631a34757bac7a4de5a8 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:46:40 +0000 Subject: [PATCH 49/71] Formatting inputs --- .github/workflows/_reusable-verify-run-status.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index b9ee6aa149b0..7b0d828d6f24 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-24.04 if: >- - github.event_name == 'workflow_run' || - (github.event_name == 'check_run' && github.event.check_run.name == '${{ github.event.inputs.check_run_name }}') || - (github.event_name == 'check_suite') + ${{ github.event_name == 'workflow_run' || + (github.event_name == 'check_run' && github.event.check_run.name == github.event.inputs.check_run_name) || + (github.event_name == 'check_suite') }} steps: # *** IMPORTANT *** From 5fd733d345324f11bb67d831bc1a9974f74b938c Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 05:56:09 +0000 Subject: [PATCH 50/71] formatting --- .github/workflows/_reusable-verify-run-status.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index 7b0d828d6f24..0179082a80a1 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -20,10 +20,7 @@ jobs: check-run-status: runs-on: ubuntu-24.04 - if: >- - ${{ github.event_name == 'workflow_run' || - (github.event_name == 'check_run' && github.event.check_run.name == github.event.inputs.check_run_name) || - (github.event_name == 'check_suite') }} + if: ${{ github.event_name == 'workflow_run' || (github.event_name == 'check_run' && github.event.check_run.name == github.event.inputs.check_run_name) || (github.event_name == 'check_suite') }} steps: # *** IMPORTANT *** From 0e37a9b589fbfdb02d64525919b4f507e1cd0d98 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 06:01:05 +0000 Subject: [PATCH 51/71] Remove if --- .github/workflows/_reusable-verify-run-status.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index 0179082a80a1..e02d84b42384 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -20,8 +20,6 @@ jobs: check-run-status: runs-on: ubuntu-24.04 - if: ${{ github.event_name == 'workflow_run' || (github.event_name == 'check_run' && github.event.check_run.name == github.event.inputs.check_run_name) || (github.event_name == 'check_suite') }} - steps: # *** IMPORTANT *** # For workflows that are triggered by the pull_request_target event, the workflow runs in the From e9fe46dab5d1f91d9564f1fe62fa8840c32211b4 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 15:48:52 +0000 Subject: [PATCH 52/71] set head_sha after checking the name --- .github/workflows/src/verify-run-status.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 16baedfda9f3..156ad38c9e66 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -6,14 +6,13 @@ import { extractInputs } from "./context.js"; * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments * @param {string} checkRunName * @param {string} workflowName + * @returns {Promise} */ export async function verifyRunStatus( { github, context, core }, checkRunName, workflowName, ) { - const { head_sha } = await extractInputs(github, context, core); - // Exit early when context is a check_run event and the check run does not // match the checkRunName. if (context.eventName == "check_run") { @@ -26,6 +25,8 @@ export async function verifyRunStatus( } } + const { head_sha } = await extractInputs(github, context, core); + const checkRun = context.eventName == "check_run" ? context.payload.check_run @@ -114,7 +115,7 @@ export async function getCheckRunStatus( * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} workflowName * @param {string} head_sha - * @returns + * @returns {Promise} */ export async function getWorkflowRun( github, From 2500bc21375c25251991a5bb955152dbc0e5bdfc Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 18:25:22 +0000 Subject: [PATCH 53/71] Test after review feedback --- .../_reusable-verify-run-status.yaml | 19 ++++----------- .github/workflows/src/verify-run-status.js | 23 +++++++++++-------- .github/workflows/watch-lintdiff.yaml | 4 +++- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index e02d84b42384..ee9390688cc6 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -15,23 +15,15 @@ on: permissions: checks: read + contents: read jobs: check-run-status: runs-on: ubuntu-24.04 steps: - # *** IMPORTANT *** - # For workflows that are triggered by the pull_request_target event, the workflow runs in the - # context of the base of the pull request. You should make sure that you do not check out, - # build, or run untrusted code from the head of the pull request. - uses: actions/checkout@v4 with: - # Only needs .github folder for automation, not the files in the PR (analyzed in a - # separate workflow). - # - # Uses the .github folder from the PR base branch (pull_request_target trigger), - # or the repo default branch (other triggers). sparse-checkout: | .github @@ -40,8 +32,7 @@ jobs: with: script: | const { verifyRunStatus } = await import('${{ github.workspace }}/.github/workflows/src/verify-run-status.js'); - return await verifyRunStatus( - { github, context, core }, - "${{ inputs.check_run_name }}", - "${{ inputs.workflow_name }}" - ); + return await verifyRunStatus({ github, context, core }); + env: + CHECK_RUN_NAME: ${{ inputs.check_run_name }} + WORKFLOW_NAME: ${{ inputs.workflow_name }} diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 156ad38c9e66..821f7d07743a 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -4,15 +4,20 @@ import { extractInputs } from "./context.js"; /** * * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments - * @param {string} checkRunName - * @param {string} workflowName - * @returns {Promise} */ export async function verifyRunStatus( - { github, context, core }, - checkRunName, - workflowName, + { github, context, core } ) { + const checkRunName = process.env.CHECK_RUN_NAME || ""; + if (!checkRunName) { + throw new Error("CHECK_RUN_NAME is not set"); + } + + const workflowName = process.env.WORKFLOW_NAME || ""; + if (!workflowName) { + throw new Error("WORKFLOW_NAME is not set"); + } + // Exit early when context is a check_run event and the check run does not // match the checkRunName. if (context.eventName == "check_run") { @@ -38,7 +43,7 @@ export async function verifyRunStatus( } core.info( - `Check run name: ${checkRun.name} conclusion: ${checkRun.conclusion}`, + `Check run name: ${checkRun.name}, conclusion: ${checkRun.conclusion}, URL: ${checkRun.html_url}`, ); core.debug(`Check run: ${JSON.stringify(checkRun)}`); @@ -53,7 +58,7 @@ export async function verifyRunStatus( } core.info( - `Workflow run name: ${workflowRun.name} conclusion: ${workflowRun.conclusion}`, + `Workflow run name: ${workflowRun.name}, conclusion: ${workflowRun.conclusion}, URL: ${workflowRun.html_url}`, ); core.debug(`Workflow run: ${JSON.stringify(workflowRun)}`); @@ -74,7 +79,6 @@ export async function verifyRunStatus( * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} checkRunName * @param {string} head_sha - * @returns */ export async function getCheckRunStatus( github, @@ -115,7 +119,6 @@ export async function getCheckRunStatus( * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} workflowName * @param {string} head_sha - * @returns {Promise} */ export async function getWorkflowRun( github, diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 7bc120a4259b..1d977aba669f 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -14,10 +14,12 @@ on: permissions: checks: read + contents: read jobs: LintDiff: - name: Lint Diff + name: Watch LintDiff + # if: ${{ github.event.check_suite.app.name == 'openapi-pipeline-app' }} uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" From 3a994cfec7860401356199f13863cb1a630101a5 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 18:33:19 +0000 Subject: [PATCH 54/71] Escape --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 1d977aba669f..28e434ee02d9 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -23,4 +23,4 @@ jobs: uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" - workflow_name: "\\[TEST-IGNORE\\] Swagger LintDiff" + workflow_name: "[TEST-IGNORE] Swagger LintDiff" From 35680724060f45cfb16b40649be33574357fb15c Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 18:36:50 +0000 Subject: [PATCH 55/71] Notices --- .github/workflows/src/verify-run-status.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 821f7d07743a..96222f75f15f 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -23,9 +23,9 @@ export async function verifyRunStatus( if (context.eventName == "check_run") { const contextRunName = context.payload.check_run.name; if (contextRunName !== checkRunName) { - core.info( - `Check run name (${contextRunName}) does not match input: ${checkRunName}`, - ); + const message = `Check run name (${contextRunName}) does not match input: ${checkRunName}`; + core.info(message); + core.notice(message); return; } } @@ -38,7 +38,9 @@ export async function verifyRunStatus( : await getCheckRunStatus(github, context, core, checkRunName, head_sha); if (!checkRun) { - core.info(`No completed check run with name: ${checkRunName}`); + const message = `No completed check run with name: ${checkRunName}`; + core.info(message); + core.notice(message); return; } @@ -53,7 +55,9 @@ export async function verifyRunStatus( : await getWorkflowRun(github, context, core, workflowName, head_sha); if (!workflowRun) { - core.info(`No completed workflow run with name: ${workflowName}`); + const message = `No completed workflow run with name: ${workflowName}`; + core.info(message); + core.notice(message); return; } From 0cd37fea90faa263a46b2cd6b4575beff77112c8 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 20:25:51 +0000 Subject: [PATCH 56/71] DRY up calls to checks.listForRef and actions.listWorkflowRunsForRepo --- .github/workflows/src/arm-auto-signoff.js | 23 +++--- .github/workflows/src/checks.js | 25 ++++++ .github/workflows/src/verify-run-status.js | 15 +--- .github/workflows/src/workflows.js | 23 ++++++ .github/workflows/test/mocks.js | 1 + .../workflows/test/verify-run-status.test.js | 78 +++++++++++-------- 6 files changed, 109 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/src/checks.js create mode 100644 .github/workflows/src/workflows.js diff --git a/.github/workflows/src/arm-auto-signoff.js b/.github/workflows/src/arm-auto-signoff.js index f32f41720ca0..cd82b2bfa7f3 100644 --- a/.github/workflows/src/arm-auto-signoff.js +++ b/.github/workflows/src/arm-auto-signoff.js @@ -4,6 +4,8 @@ import { setEquals } from "../../src/equality.js"; import { extractInputs } from "./context.js"; import { PER_PAGE_MAX } from "./github.js"; import { LabelAction } from "./label.js"; +import { listChecksForRef } from "./checks.js"; +import { listWorkflowRunsForRepo } from "./workflows.js"; // TODO: Add tests /* v8 ignore start */ @@ -86,16 +88,12 @@ export async function getLabelActionImpl({ core.info(`Labels: ${labelNames}`); - const workflowRuns = await github.paginate( - github.rest.actions.listWorkflowRunsForRepo, - { - owner, - repo, - event: "pull_request", - head_sha, - per_page: PER_PAGE_MAX, - }, - ); + const workflowRuns = await listWorkflowRunsForRepo(github, { + owner, + repo, + event: "pull_request", + head_sha, + }); core.info("Workflow Runs:"); workflowRuns.forEach((wf) => { @@ -172,17 +170,16 @@ export async function getLabelActionImpl({ return removeAction; } - const checkRuns = await github.paginate(github.rest.checks.listForRef, { + const checkRuns = await listChecksForRef(github, { owner: owner, repo: repo, ref: head_sha, - per_page: PER_PAGE_MAX, }); const requiredCheckNames = ["Swagger LintDiff", "Swagger Avocado"]; /** - * @type {typeof checkRuns.check_runs} + * @type {typeof checkRuns} */ let requiredCheckRuns = []; diff --git a/.github/workflows/src/checks.js b/.github/workflows/src/checks.js new file mode 100644 index 000000000000..89121020281d --- /dev/null +++ b/.github/workflows/src/checks.js @@ -0,0 +1,25 @@ +import { PER_PAGE_MAX } from "./github.js"; + +/** + * @param {import('github-script').AsyncFunctionArguments['github']} github + * @param {{ + * owner: string; + * repo: string; + * ref: string; + * name?: string; + * status?: "queued" | "in_progress" | "completed"; + * }} params + * @returns {Promise} + */ +export async function listChecksForRef(github, { owner, repo, ref, name, status}) { + const options = { + owner, + repo, + ref, + ...(name && { check_name: name }), + ...(status && { status }), + per_page: PER_PAGE_MAX, + }; + + return await github.paginate(github.rest.checks.listForRef, options); +} diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index 96222f75f15f..f7d298f5bac7 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -1,5 +1,6 @@ -import { PER_PAGE_MAX } from "./github.js"; import { extractInputs } from "./context.js"; +import { listChecksForRef } from "./checks.js"; +import { listWorkflowRunsForRepo } from "./workflows.js"; /** * @@ -91,13 +92,7 @@ export async function getCheckRunStatus( checkRunName, head_sha, ) { - const checkRuns = await github.paginate(github.rest.checks.listForRef, { - ...context.repo, - ref: head_sha, - check_name: checkRunName, - status: "completed", - per_page: PER_PAGE_MAX, - }); + const checkRuns = await listChecksForRef(github, { ...context.repo, ref: head_sha, name: checkRunName, status: "completed" }); if (checkRuns.length === 0) { return null; @@ -131,13 +126,11 @@ export async function getWorkflowRun( workflowName, head_sha, ) { - const workflowRuns = await github.paginate( - github.rest.actions.listWorkflowRunsForRepo, + const workflowRuns = await listWorkflowRunsForRepo(github, { ...context.repo, head_sha, status: "completed", - per_page: PER_PAGE_MAX, }, ); diff --git a/.github/workflows/src/workflows.js b/.github/workflows/src/workflows.js new file mode 100644 index 000000000000..139f388cf4e0 --- /dev/null +++ b/.github/workflows/src/workflows.js @@ -0,0 +1,23 @@ +import { PER_PAGE_MAX } from "./github.js"; + +/** + * @param {import('github-script').AsyncFunctionArguments['github']} github + * @param {{ + * owner: string; + * repo: string; + * head_sha?: string; + * status?: "completed" | "in_progress" | "queued"; + * event?: string; + * }} params + * @returns {Promise} + */ +export async function listWorkflowRunsForRepo(github, { owner, repo, head_sha, status, event}) { + return await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + ...(head_sha && { head_sha }), + ...(status && { status }), + ...(event && { event }), + per_page: PER_PAGE_MAX, + }); +} diff --git a/.github/workflows/test/mocks.js b/.github/workflows/test/mocks.js index e4f8ae24e7ee..e07a9001d568 100644 --- a/.github/workflows/test/mocks.js +++ b/.github/workflows/test/mocks.js @@ -50,6 +50,7 @@ export function createMockCore() { info: vi.fn(console.log), error: vi.fn(console.error), warning: vi.fn(console.warn), + notice: vi.fn(console.log), isDebug: vi.fn().mockReturnValue(true), setOutput: vi.fn((name, value) => console.log(`setOutput('${name}', '${value}')`), diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index bb8263668540..08f3966d44eb 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from "vitest"; +import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockGithub, createMockContext, @@ -205,6 +205,16 @@ describe("getWorkflowRun", () => { }); describe("verifyRunStatus", () => { + beforeEach(() => { + delete process.env.CHECK_RUN_NAME; + delete process.env.WORKFLOW_NAME; + }); + + afterAll(() => { + delete process.env.CHECK_RUN_NAME; + delete process.env.WORKFLOW_NAME; + }) + it("verifies status when check_run event fires", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ @@ -231,11 +241,9 @@ describe("verifyRunStatus", () => { const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "checkRunName", - "workflowName", - ); + process.env.CHECK_RUN_NAME = "checkRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.info).toHaveBeenCalledWith("Checks match"); @@ -267,15 +275,13 @@ describe("verifyRunStatus", () => { const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "checkRunName", - "workflowName", - ); + + process.env.CHECK_RUN_NAME = "checkRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); }); - it("returns early during workflow_run event when no matching check_run is found", async () => { const github = createMockGithub(); github.rest.checks.listForRef = vi.fn().mockResolvedValue({ @@ -294,11 +300,9 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "checkRunName", - "workflowName", - ); + process.env.CHECK_RUN_NAME = "checkRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.info).toHaveBeenCalledWith( "No completed check run with name: checkRunName", @@ -323,11 +327,9 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "checkRunName", - "workflowName", - ); + process.env.CHECK_RUN_NAME = "checkRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.info).toHaveBeenCalledWith( "No completed workflow run with name: workflowName", @@ -346,11 +348,9 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "otherCheckRunName", - "workflowName", - ); + process.env.CHECK_RUN_NAME = "otherCheckRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.info).toHaveBeenCalledWith( "Check run name (checkRunName) does not match input: otherCheckRunName", @@ -381,13 +381,27 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - await verifyRunStatus( - { github, context, core }, - "checkRunName", - "workflowName", - ); + process.env.CHECK_RUN_NAME = "checkRunName"; + process.env.WORKFLOW_NAME = "workflowName"; + await verifyRunStatus({ github, context, core }); expect(core.setFailed).toHaveBeenCalledWith( "Check run conclusion (success) does not match workflow run conclusion (failure)", ); }); + + it("throws if CHECK_RUN_NAME is not set", async () => { + const github = createMockGithub(); + const context = createMockContext(); + const core = createMockCore(); + process.env.WORKFLOW_NAME = "workflowName"; + await expect(async () => await verifyRunStatus({ github, context, core }) ).rejects.toThrow(); + }); + + it("throws if WORKFLOW_NAME is not set", async () => { + const github = createMockGithub(); + const context = createMockContext(); + const core = createMockCore(); + process.env.CHECK_RUN_NAME = "checkRunName"; + await expect(async () => await verifyRunStatus({ github, context, core }) ).rejects.toThrow(); + }); }); From 0a49c8310cf6373730403e88c07a6f2e66ad6a1e Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:08:02 +0000 Subject: [PATCH 57/71] Revert up to current state --- .github/package-lock.json | 28 ++++---- .github/workflows/src/arm-auto-signoff.js | 23 +++--- .github/workflows/src/verify-run-status.js | 70 +++++++++++-------- .github/workflows/test/mocks.js | 2 +- .../workflows/test/verify-run-status.test.js | 70 +++++++++---------- .github/workflows/watch-lintdiff.yaml | 4 +- 6 files changed, 103 insertions(+), 94 deletions(-) diff --git a/.github/package-lock.json b/.github/package-lock.json index 6b29afcbb949..14d3bdd70e5d 100644 --- a/.github/package-lock.json +++ b/.github/package-lock.json @@ -646,9 +646,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -721,9 +721,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", "dev": true, "license": "MIT", "engines": { @@ -1963,19 +1963,19 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", + "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", + "@eslint/js": "9.24.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3311,9 +3311,9 @@ } }, "node_modules/vite": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz", - "integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", + "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/.github/workflows/src/arm-auto-signoff.js b/.github/workflows/src/arm-auto-signoff.js index cd82b2bfa7f3..f32f41720ca0 100644 --- a/.github/workflows/src/arm-auto-signoff.js +++ b/.github/workflows/src/arm-auto-signoff.js @@ -4,8 +4,6 @@ import { setEquals } from "../../src/equality.js"; import { extractInputs } from "./context.js"; import { PER_PAGE_MAX } from "./github.js"; import { LabelAction } from "./label.js"; -import { listChecksForRef } from "./checks.js"; -import { listWorkflowRunsForRepo } from "./workflows.js"; // TODO: Add tests /* v8 ignore start */ @@ -88,12 +86,16 @@ export async function getLabelActionImpl({ core.info(`Labels: ${labelNames}`); - const workflowRuns = await listWorkflowRunsForRepo(github, { - owner, - repo, - event: "pull_request", - head_sha, - }); + const workflowRuns = await github.paginate( + github.rest.actions.listWorkflowRunsForRepo, + { + owner, + repo, + event: "pull_request", + head_sha, + per_page: PER_PAGE_MAX, + }, + ); core.info("Workflow Runs:"); workflowRuns.forEach((wf) => { @@ -170,16 +172,17 @@ export async function getLabelActionImpl({ return removeAction; } - const checkRuns = await listChecksForRef(github, { + const checkRuns = await github.paginate(github.rest.checks.listForRef, { owner: owner, repo: repo, ref: head_sha, + per_page: PER_PAGE_MAX, }); const requiredCheckNames = ["Swagger LintDiff", "Swagger Avocado"]; /** - * @type {typeof checkRuns} + * @type {typeof checkRuns.check_runs} */ let requiredCheckRuns = []; diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index f7d298f5bac7..d6cad75e421a 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -1,20 +1,25 @@ import { extractInputs } from "./context.js"; -import { listChecksForRef } from "./checks.js"; -import { listWorkflowRunsForRepo } from "./workflows.js"; +import { PER_PAGE_MAX } from "./github.js"; /** - * + * @typedef {import('@octokit/plugin-rest-endpoint-methods').RestEndpointMethodTypes} RestEndpointMethodTypes + * @typedef {RestEndpointMethodTypes["checks"]["listForRef"]["response"]["data"]["check_runs"][number]} CheckRun + * @typedef {RestEndpointMethodTypes["actions"]["listWorkflowRunsForRepo"]["response"]["data"]["workflow_runs"][number]} WorkflowRun + */ + +/** + * Given the name of a completed check run name and a completed workflow, verify + * that both have the same conclusion. If conclusions are different, fail the + * action. * @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments */ -export async function verifyRunStatus( - { github, context, core } -) { - const checkRunName = process.env.CHECK_RUN_NAME || ""; - if (!checkRunName) { +export async function verifyRunStatus({ github, context, core }) { + const checkRunName = process.env.CHECK_RUN_NAME; + if (!checkRunName) { throw new Error("CHECK_RUN_NAME is not set"); } - - const workflowName = process.env.WORKFLOW_NAME || ""; + + const workflowName = process.env.WORKFLOW_NAME; if (!workflowName) { throw new Error("WORKFLOW_NAME is not set"); } @@ -24,9 +29,7 @@ export async function verifyRunStatus( if (context.eventName == "check_run") { const contextRunName = context.payload.check_run.name; if (contextRunName !== checkRunName) { - const message = `Check run name (${contextRunName}) does not match input: ${checkRunName}`; - core.info(message); - core.notice(message); + core.notice(`Check run name (${contextRunName}) does not match input: ${checkRunName}`); return; } } @@ -36,12 +39,10 @@ export async function verifyRunStatus( const checkRun = context.eventName == "check_run" ? context.payload.check_run - : await getCheckRunStatus(github, context, core, checkRunName, head_sha); + : await getCheckRun(github, context, core, checkRunName, head_sha); if (!checkRun) { - const message = `No completed check run with name: ${checkRunName}`; - core.info(message); - core.notice(message); + core.notice(`No completed check run with name: ${checkRunName}`); return; } @@ -56,9 +57,7 @@ export async function verifyRunStatus( : await getWorkflowRun(github, context, core, workflowName, head_sha); if (!workflowRun) { - const message = `No completed workflow run with name: ${workflowName}`; - core.info(message); - core.notice(message); + core.notice(`No completed workflow run with name: ${workflowName}`); return; } @@ -74,25 +73,32 @@ export async function verifyRunStatus( return; } - core.info("Checks match"); + core.notice(`Conclusions match for check run ${checkRunName} and workflow run ${workflowName}`); } /** - * + * Returns the check with the given checkRunName for the given ref. * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} checkRunName - * @param {string} head_sha + * @param {string} ref + * @returns {Promise} */ -export async function getCheckRunStatus( +export async function getCheckRun( github, context, core, checkRunName, - head_sha, + ref, ) { - const checkRuns = await listChecksForRef(github, { ...context.repo, ref: head_sha, name: checkRunName, status: "completed" }); + const checkRuns = await github.paginate(github.rest.checks.listForRef, { + ...context.repo, + ref: ref, + check_name: checkRunName, + status: "completed", + per_page: PER_PAGE_MAX, + }); if (checkRuns.length === 0) { return null; @@ -113,24 +119,28 @@ export async function getCheckRunStatus( } /** + * Returns the workflow run with the given workflowName for the given ref. * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} workflowName - * @param {string} head_sha + * @param {string} ref + * @returns {Promise} */ export async function getWorkflowRun( github, context, core, workflowName, - head_sha, + ref, ) { - const workflowRuns = await listWorkflowRunsForRepo(github, + const workflowRuns = await github.paginate( + github.rest.actions.listWorkflowRunsForRepo, { ...context.repo, - head_sha, + head_sha: ref, status: "completed", + per_page: PER_PAGE_MAX, }, ); diff --git a/.github/workflows/test/mocks.js b/.github/workflows/test/mocks.js index e07a9001d568..797d21c7a541 100644 --- a/.github/workflows/test/mocks.js +++ b/.github/workflows/test/mocks.js @@ -48,9 +48,9 @@ export function createMockCore() { return { debug: vi.fn(console.debug), info: vi.fn(console.log), + notice: vi.fn(console.log), error: vi.fn(console.error), warning: vi.fn(console.warn), - notice: vi.fn(console.log), isDebug: vi.fn().mockReturnValue(true), setOutput: vi.fn((name, value) => console.log(`setOutput('${name}', '${value}')`), diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index 08f3966d44eb..dc43500155f1 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -5,7 +5,7 @@ import { createMockCore, } from "./mocks.js"; import { - getCheckRunStatus, + getCheckRun, getWorkflowRun, verifyRunStatus, } from "../src/verify-run-status.js"; @@ -18,7 +18,7 @@ vi.mock("../src/context.js", () => { }; }); -describe("getCheckRunStatus", () => { +describe("getCheckRun", () => { it("returns matching check_run", async () => { const githubMock = createMockGithub(); githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ @@ -33,7 +33,7 @@ describe("getCheckRunStatus", () => { }, }); - const actual = await getCheckRunStatus( + const actual = await getCheckRun( githubMock, createMockContext(), createMockCore(), @@ -58,7 +58,7 @@ describe("getCheckRunStatus", () => { }, }); - const actual = await getCheckRunStatus( + const actual = await getCheckRun( githubMock, createMockContext(), createMockCore(), @@ -90,7 +90,7 @@ describe("getCheckRunStatus", () => { await expect( async () => - await getCheckRunStatus( + await getCheckRun( githubMock, createMockContext(), createMockCore(), @@ -205,16 +205,6 @@ describe("getWorkflowRun", () => { }); describe("verifyRunStatus", () => { - beforeEach(() => { - delete process.env.CHECK_RUN_NAME; - delete process.env.WORKFLOW_NAME; - }); - - afterAll(() => { - delete process.env.CHECK_RUN_NAME; - delete process.env.WORKFLOW_NAME; - }) - it("verifies status when check_run event fires", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ @@ -241,12 +231,12 @@ describe("verifyRunStatus", () => { const core = createMockCore(); - process.env.CHECK_RUN_NAME = "checkRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith("Checks match"); + expect(core.notice).toHaveBeenCalledWith("Conclusions match for check run checkRunName and workflow run workflowName"); }); it("verifies status when workflow_run event fires", async () => { @@ -275,13 +265,13 @@ describe("verifyRunStatus", () => { const core = createMockCore(); - - process.env.CHECK_RUN_NAME = "checkRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); }); + it("returns early during workflow_run event when no matching check_run is found", async () => { const github = createMockGithub(); github.rest.checks.listForRef = vi.fn().mockResolvedValue({ @@ -300,11 +290,11 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - process.env.CHECK_RUN_NAME = "checkRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith( + expect(core.notice).toHaveBeenCalledWith( "No completed check run with name: checkRunName", ); }); @@ -327,11 +317,11 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - process.env.CHECK_RUN_NAME = "checkRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith( + expect(core.notice).toHaveBeenCalledWith( "No completed workflow run with name: workflowName", ); }); @@ -348,11 +338,11 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - process.env.CHECK_RUN_NAME = "otherCheckRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "otherCheckRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.info).toHaveBeenCalledWith( + expect(core.notice).toHaveBeenCalledWith( "Check run name (checkRunName) does not match input: otherCheckRunName", ); }); @@ -381,8 +371,8 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - process.env.CHECK_RUN_NAME = "checkRunName"; - process.env.WORKFLOW_NAME = "workflowName"; + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); await verifyRunStatus({ github, context, core }); expect(core.setFailed).toHaveBeenCalledWith( "Check run conclusion (success) does not match workflow run conclusion (failure)", @@ -393,15 +383,21 @@ describe("verifyRunStatus", () => { const github = createMockGithub(); const context = createMockContext(); const core = createMockCore(); - process.env.WORKFLOW_NAME = "workflowName"; - await expect(async () => await verifyRunStatus({ github, context, core }) ).rejects.toThrow(); + vi.stubEnv("CHECK_RUN_NAME", undefined); + vi.stubEnv("WORKFLOW_NAME", "workflowName"); + await expect( + async () => await verifyRunStatus({ github, context, core }), + ).rejects.toThrow(); }); it("throws if WORKFLOW_NAME is not set", async () => { const github = createMockGithub(); const context = createMockContext(); const core = createMockCore(); - process.env.CHECK_RUN_NAME = "checkRunName"; - await expect(async () => await verifyRunStatus({ github, context, core }) ).rejects.toThrow(); + vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); + vi.stubEnv("WORKFLOW_NAME", undefined); + await expect( + async () => await verifyRunStatus({ github, context, core }), + ).rejects.toThrow(); }); -}); +}); \ No newline at end of file diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 28e434ee02d9..d7f592553a08 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -4,7 +4,7 @@ name: "~Watch - LintDiff" on: # check_suite is preferred over check_run to avoid triggering on all check # runs. In some cases, check_run must be used in testing environments. - check_run: + check_suite: types: completed workflow_run: @@ -19,7 +19,7 @@ permissions: jobs: LintDiff: name: Watch LintDiff - # if: ${{ github.event.check_suite.app.name == 'openapi-pipeline-app' }} + if: ${{ github.event.check_suite.app.name == 'openapi-pipeline-app' }} uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" From 28074fd4f85f75aa4f49f82128b41325231ba2a6 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:08:31 +0000 Subject: [PATCH 58/71] Attempt condition --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index d7f592553a08..9f53e60707cb 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -19,7 +19,7 @@ permissions: jobs: LintDiff: name: Watch LintDiff - if: ${{ github.event.check_suite.app.name == 'openapi-pipeline-app' }} + if: ${{ (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" From 5a313bca14881d816bd42fea429f3a3201160e10 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:13:37 +0000 Subject: [PATCH 59/71] github.event_name == 'workflow_run' --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 9f53e60707cb..67925d0075b4 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -19,7 +19,7 @@ permissions: jobs: LintDiff: name: Watch LintDiff - if: ${{ (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} + if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" From 40eacb70f4d5e49253c2ab657db28b038d502554 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:20:13 +0000 Subject: [PATCH 60/71] check_run --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 67925d0075b4..a9b098c819ab 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -4,7 +4,7 @@ name: "~Watch - LintDiff" on: # check_suite is preferred over check_run to avoid triggering on all check # runs. In some cases, check_run must be used in testing environments. - check_suite: + check_run: types: completed workflow_run: From 4528e5be2e21b409b2efbf34b12ad775c298ee00 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:25:51 +0000 Subject: [PATCH 61/71] Into template --- .github/workflows/_reusable-verify-run-status.yaml | 1 + .github/workflows/watch-lintdiff.yaml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index ee9390688cc6..85b9823e176c 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -19,6 +19,7 @@ permissions: jobs: check-run-status: + if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} runs-on: ubuntu-24.04 steps: diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index a9b098c819ab..a4c9f115430a 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -19,7 +19,6 @@ permissions: jobs: LintDiff: name: Watch LintDiff - if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} uses: ./.github/workflows/_reusable-verify-run-status.yaml with: check_run_name: "Swagger LintDiff" From 37c57eea19633a2711baee79b11b1e38c962003b Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:30:29 +0000 Subject: [PATCH 62/71] inputs --- .github/workflows/_reusable-verify-run-status.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index 85b9823e176c..50ae90b13010 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -19,7 +19,7 @@ permissions: jobs: check-run-status: - if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == 'Swagger LintDiff') }} + if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == github.action.inputs.check_run_name) }} runs-on: ubuntu-24.04 steps: From 992bcc9f044c3cec6c0b4308c4216128b07910ad Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Mon, 7 Apr 2025 23:36:31 +0000 Subject: [PATCH 63/71] inputs --- .github/workflows/_reusable-verify-run-status.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index 50ae90b13010..e5bab6996bd6 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -19,7 +19,7 @@ permissions: jobs: check-run-status: - if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == github.action.inputs.check_run_name) }} + if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == inputs.check_run_name) }} runs-on: ubuntu-24.04 steps: From 99a939df10e1d72d9c3d71aafee4219b945c738a Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Tue, 8 Apr 2025 04:35:46 +0000 Subject: [PATCH 64/71] Update to state of PR --- .../_reusable-verify-run-status.yaml | 5 +- .github/workflows/src/verify-run-status.js | 150 ++++++++--------- .../workflows/test/verify-run-status.test.js | 151 +++++++++--------- .github/workflows/watch-lintdiff.yaml | 2 +- 4 files changed, 161 insertions(+), 147 deletions(-) diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index e5bab6996bd6..4609526e78ef 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -19,7 +19,10 @@ permissions: jobs: check-run-status: - if: ${{ (github.event_name == 'workflow_run') || (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || (github.event_name == 'check_run' && github.event.check_run.name == inputs.check_run_name) }} + if: | + (github.event_name == 'workflow_run') || + (github.event_name == 'check_suite' && github.event.check_suite.app.name == 'openapi-pipeline-app') || + (github.event_name == 'check_run' && github.event.check_run.name == inputs.check_run_name) runs-on: ubuntu-24.04 steps: diff --git a/.github/workflows/src/verify-run-status.js b/.github/workflows/src/verify-run-status.js index d6cad75e421a..2a0770c46dfb 100644 --- a/.github/workflows/src/verify-run-status.js +++ b/.github/workflows/src/verify-run-status.js @@ -1,12 +1,15 @@ import { extractInputs } from "./context.js"; import { PER_PAGE_MAX } from "./github.js"; +const SUPPORTED_EVENTS = ["workflow_run", "check_run", "check_suite"]; + /** * @typedef {import('@octokit/plugin-rest-endpoint-methods').RestEndpointMethodTypes} RestEndpointMethodTypes - * @typedef {RestEndpointMethodTypes["checks"]["listForRef"]["response"]["data"]["check_runs"][number]} CheckRun - * @typedef {RestEndpointMethodTypes["actions"]["listWorkflowRunsForRepo"]["response"]["data"]["workflow_runs"][number]} WorkflowRun + * @typedef {RestEndpointMethodTypes["checks"]["listForRef"]["response"]["data"]["check_runs"]} CheckRuns + * @typedef {RestEndpointMethodTypes["actions"]["listWorkflowRunsForRepo"]["response"]["data"]["workflow_runs"]} WorkflowRuns */ +/* v8 ignore start */ /** * Given the name of a completed check run name and a completed workflow, verify * that both have the same conclusion. If conclusions are different, fail the @@ -24,26 +27,58 @@ export async function verifyRunStatus({ github, context, core }) { throw new Error("WORKFLOW_NAME is not set"); } - // Exit early when context is a check_run event and the check run does not - // match the checkRunName. + if (!SUPPORTED_EVENTS.some((e) => e === context.eventName)) { + throw new Error( + `Unsupported event: ${context.eventName}. Supported events: ${SUPPORTED_EVENTS.join(", ")}`, + ); + } + + if (context.eventName === "check_suite" && context.payload.check_suite.status !== "completed") { + core.setFailed(`Check suite ${context.payload.check_suite.app.name} is not completed. Cannot evaluate incomplete check suite.`); + return; + } + + return await verifyRunStatusImpl({ github, context, core , checkRunName, workflowName}); +} +/* v8 ignore stop */ + +/** + * @param {Object} params + * @param {import('github-script').AsyncFunctionArguments["github"]} params.github + * @param {import('github-script').AsyncFunctionArguments["context"]} params.context + * @param {import('github-script').AsyncFunctionArguments["core"]} params.core + * @param {string} params.checkRunName + * @param {string} params.workflowName + */ +export async function verifyRunStatusImpl({github, context, core, checkRunName, workflowName}) { if (context.eventName == "check_run") { const contextRunName = context.payload.check_run.name; if (contextRunName !== checkRunName) { - core.notice(`Check run name (${contextRunName}) does not match input: ${checkRunName}`); + core.setFailed(`Check run name (${contextRunName}) does not match input: ${checkRunName}. Ensure job is filtering by github.event.check_run.name.`); return; } } const { head_sha } = await extractInputs(github, context, core); - const checkRun = - context.eventName == "check_run" - ? context.payload.check_run - : await getCheckRun(github, context, core, checkRunName, head_sha); + let checkRun; + if (context.eventName == "check_run") { + checkRun = context.payload.check_run; + } else { + const checkRuns = await getCheckRuns(github, context, checkRunName, head_sha); + if (checkRuns.length === 0) { + if (context.eventName === "check_suite") { + const message = `Could not locate check run ${checkRunName} in check suite ${context.payload.check_suite.app.name}. Ensure job is filtering by github.event.check_suite.app.name.`; + core.setFailed(message); + return; + } + + core.notice(`No completed check run with name: ${checkRunName}. Not enough information to judge success or failure. Ending with success status.`); + return; + } - if (!checkRun) { - core.notice(`No completed check run with name: ${checkRunName}`); - return; + // Use the most recent check run + checkRun = checkRuns[0]; } core.info( @@ -51,14 +86,18 @@ export async function verifyRunStatus({ github, context, core }) { ); core.debug(`Check run: ${JSON.stringify(checkRun)}`); - const workflowRun = - context.eventName == "workflow_run" - ? context.payload.workflow_run - : await getWorkflowRun(github, context, core, workflowName, head_sha); + let workflowRun; + if (context.eventName == "workflow_run") { + workflowRun = context.payload.workflow_run; + } else { + const workflowRuns = await getWorkflowRuns(github, context, workflowName, head_sha); + if (workflowRuns.length === 0) { + core.notice(`No completed workflow run with name: ${workflowName}. Not enough information to judge success or failure. Ending with success status.`); + return; + } - if (!workflowRun) { - core.notice(`No completed workflow run with name: ${workflowName}`); - return; + // Use the most recent workflow run + workflowRun = workflowRuns[0]; } core.info( @@ -80,19 +119,17 @@ export async function verifyRunStatus({ github, context, core }) { * Returns the check with the given checkRunName for the given ref. * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context - * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} checkRunName * @param {string} ref - * @returns {Promise} + * @returns {Promise} */ -export async function getCheckRun( +export async function getCheckRuns( github, context, - core, checkRunName, ref, ) { - const checkRuns = await github.paginate(github.rest.checks.listForRef, { + const result = await github.paginate(github.rest.checks.listForRef, { ...context.repo, ref: ref, check_name: checkRunName, @@ -100,41 +137,26 @@ export async function getCheckRun( per_page: PER_PAGE_MAX, }); - if (checkRuns.length === 0) { - return null; - } - - if (checkRuns.length > 1) { - core.info(`Multiple check runs:`); - checkRuns.forEach((cr) => { - core.info(`- ${cr.name}: ${cr.conclusion}`); - }); - - const message = `Multiple completed check runs with name: ${checkRunName}`; - core.setFailed(message); - throw new Error(message); - } - - return checkRuns[0]; + // a and b will never be null because status is "completed" + /* v8 ignore next */ + return result.sort((a, b) => compareDatesDescending(a.completed_at || '', b.completed_at || '')); } /** * Returns the workflow run with the given workflowName for the given ref. * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context - * @param {import('github-script').AsyncFunctionArguments['core']} core * @param {string} workflowName * @param {string} ref - * @returns {Promise} + * @returns {Promise} */ -export async function getWorkflowRun( +export async function getWorkflowRuns( github, context, - core, workflowName, ref, ) { - const workflowRuns = await github.paginate( + const result = await github.paginate( github.rest.actions.listWorkflowRunsForRepo, { ...context.repo, @@ -144,31 +166,15 @@ export async function getWorkflowRun( }, ); - if (workflowRuns.length === 0) { - core.info(`No completed workflow runs`); - return null; - } - - const matches = workflowRuns.filter((run) => run.name === workflowName); - - if (matches.length === 0) { - return null; - } - - if (matches.length > 1) { - core.warning( - `Multiple matching workflow runs, selecting the most recent run`, - ); - matches.forEach((wf) => - core.info(`- ${wf.name}: ${wf.conclusion} ${wf.html_url}`), - ); - - // Sort by "updated_at" descending, so most recent run is at index 0 - matches.sort( - (a, b) => - new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), - ); - } - - return matches[0]; + return result.filter((run) => run.name === workflowName).sort((a, b) => compareDatesDescending(a.updated_at, b.updated_at)); } + +/** + * Compares two date strings in descending order. + * @param {string} a date string of the form "YYYY-MM-DDTHH:mm:ssZ" + * @param {string} b date string of the form "YYYY-MM-DDTHH:mm:ssZ" + * @returns + */ +export function compareDatesDescending(a, b) { + return new Date(b).getTime() - new Date(a).getTime(); +} \ No newline at end of file diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index dc43500155f1..b08a6c31c4ac 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -1,13 +1,13 @@ -import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { createMockGithub, createMockContext, createMockCore, } from "./mocks.js"; import { - getCheckRun, - getWorkflowRun, - verifyRunStatus, + getCheckRuns, + getWorkflowRuns, + verifyRunStatusImpl, } from "../src/verify-run-status.js"; vi.mock("../src/context.js", () => { @@ -18,7 +18,7 @@ vi.mock("../src/context.js", () => { }; }); -describe("getCheckRun", () => { +describe("getCheckRuns", () => { it("returns matching check_run", async () => { const githubMock = createMockGithub(); githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ @@ -33,7 +33,7 @@ describe("getCheckRun", () => { }, }); - const actual = await getCheckRun( + const actual = await getCheckRuns( githubMock, createMockContext(), createMockCore(), @@ -41,13 +41,13 @@ describe("getCheckRun", () => { "head_sha", ); - expect(actual).toEqual( + expect(actual).toEqual([ expect.objectContaining({ name: "checkRunName", status: "completed", conclusion: "success", }), - ); + ]); }); it("returns null when no check matches", async () => { @@ -58,19 +58,20 @@ describe("getCheckRun", () => { }, }); - const actual = await getCheckRun( + const actual = await getCheckRuns( githubMock, createMockContext(), - createMockCore(), "checkRunName", "head_sha", ); - expect(actual).toBeNull(); + expect(actual).toEqual([]); }); it("throws when multiple checks match", async () => { const githubMock = createMockGithub(); + const earlierDate = "2025-04-01T00:00:00Z"; + const laterDate = "2025-04-02T00:00:00Z"; githubMock.rest.checks.listForRef = vi.fn().mockResolvedValue({ data: { check_runs: [ @@ -78,30 +79,43 @@ describe("getCheckRun", () => { name: "checkRunName", status: "completed", conclusion: "success", + completed_at: earlierDate, }, { name: "checkRunName", status: "completed", conclusion: "success", + completed_at: laterDate, }, ], }, }); - await expect( - async () => - await getCheckRun( - githubMock, - createMockContext(), - createMockCore(), - "checkRunName", - "head_sha", - ), - ).rejects.toThrow(/Multiple completed check runs with name/); + const actual = await await getCheckRuns( + githubMock, + createMockContext(), + "checkRunName", + "head_sha", + ); + + expect(actual).toEqual([ + expect.objectContaining({ + name: "checkRunName", + status: "completed", + conclusion: "success", + completed_at: laterDate, + }), + expect.objectContaining({ + name: "checkRunName", + status: "completed", + conclusion: "success", + completed_at: earlierDate, + }), + ]); }); }); -describe("getWorkflowRun", () => { +describe("getWorkflowRuns", () => { it("returns matching workflow_run", async () => { const githubMock = createMockGithub(); githubMock.rest.actions.listWorkflowRunsForRepo = vi @@ -118,21 +132,20 @@ describe("getWorkflowRun", () => { }, }); - const actual = await getWorkflowRun( + const actual = await getWorkflowRuns( githubMock, createMockContext(), - createMockCore(), "workflowName", "head_sha", ); - expect(actual).toEqual( + expect(actual).toEqual([ expect.objectContaining({ name: "workflowName", status: "completed", conclusion: "success", }), - ); + ]); }); it("returns null when no workflow matches", async () => { @@ -149,15 +162,14 @@ describe("getWorkflowRun", () => { }, }); - const actual = await getWorkflowRun( + const actual = await getWorkflowRuns( githubMock, createMockContext(), - createMockCore(), "workflowName", "head_sha", ); - expect(actual).toBeNull(); + expect(actual).toEqual([]); }); it("returns latest when multiple workflows match", async () => { @@ -185,26 +197,25 @@ describe("getWorkflowRun", () => { }, }); - const actual = await getWorkflowRun( + const actual = await getWorkflowRuns( githubMock, createMockContext(), - createMockCore(), "workflowName", "head_sha", ); - expect(actual).toEqual( + expect(actual).toEqual([ expect.objectContaining({ - name: "workflowName", - status: "completed", - conclusion: "success", updated_at: laterDate, }), - ); + expect.objectContaining({ + updated_at: earlyDate, + }), + ]); }); }); -describe("verifyRunStatus", () => { +describe("verifyRunStatusImpl", () => { it("verifies status when check_run event fires", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunsForRepo = vi.fn().mockResolvedValue({ @@ -233,7 +244,7 @@ describe("verifyRunStatus", () => { vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.notice).toHaveBeenCalledWith("Conclusions match for check run checkRunName and workflow run workflowName"); @@ -267,7 +278,7 @@ describe("verifyRunStatus", () => { vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); expect(core.setFailed).not.toHaveBeenCalled(); }); @@ -290,12 +301,10 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); - vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.notice).toHaveBeenCalledWith( - "No completed check run with name: checkRunName", + "No completed check run with name: checkRunName. Not enough information to judge success or failure. Ending with success status.", ); }); @@ -317,12 +326,10 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); - vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); expect(core.setFailed).not.toHaveBeenCalled(); expect(core.notice).toHaveBeenCalledWith( - "No completed workflow run with name: workflowName", + "No completed workflow run with name: workflowName. Not enough information to judge success or failure. Ending with success status.", ); }); @@ -338,12 +345,9 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", "otherCheckRunName"); - vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); - expect(core.setFailed).not.toHaveBeenCalled(); - expect(core.notice).toHaveBeenCalledWith( - "Check run name (checkRunName) does not match input: otherCheckRunName", + await verifyRunStatusImpl({ github, context, core, checkRunName: "otherCheckRunName", workflowName: "workflowName" }); + expect(core.setFailed).toHaveBeenCalledWith( + "Check run name (checkRunName) does not match input: otherCheckRunName. Ensure job is filtering by github.event.check_run.name.", ); }); @@ -371,33 +375,34 @@ describe("verifyRunStatus", () => { }, }; const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); - vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await verifyRunStatus({ github, context, core }); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); expect(core.setFailed).toHaveBeenCalledWith( "Check run conclusion (success) does not match workflow run conclusion (failure)", ); }); - it("throws if CHECK_RUN_NAME is not set", async () => { + it ("throws when in check_suite event but no check_run with name is found", async () => { const github = createMockGithub(); - const context = createMockContext(); - const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", undefined); - vi.stubEnv("WORKFLOW_NAME", "workflowName"); - await expect( - async () => await verifyRunStatus({ github, context, core }), - ).rejects.toThrow(); - }); + github.rest.checks.listForRef = vi.fn().mockResolvedValue({ + data: { + check_runs: [], + }, + }); - it("throws if WORKFLOW_NAME is not set", async () => { - const github = createMockGithub(); - const context = createMockContext(); + const context = { + eventName: "check_suite", + payload: { + check_suite: { + app: { + name: "checkRunName", + }, + }, + }, + }; const core = createMockCore(); - vi.stubEnv("CHECK_RUN_NAME", "checkRunName"); - vi.stubEnv("WORKFLOW_NAME", undefined); - await expect( - async () => await verifyRunStatus({ github, context, core }), - ).rejects.toThrow(); + await verifyRunStatusImpl({ github, context, core, checkRunName: "checkRunName", workflowName: "workflowName" }); + expect(core.setFailed).toHaveBeenCalledWith( + "Could not locate check run checkRunName in check suite checkRunName. Ensure job is filtering by github.event.check_suite.app.name.", + ); }); }); \ No newline at end of file diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index a4c9f115430a..47fa438bd3c9 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -4,7 +4,7 @@ name: "~Watch - LintDiff" on: # check_suite is preferred over check_run to avoid triggering on all check # runs. In some cases, check_run must be used in testing environments. - check_run: + check_suite: types: completed workflow_run: From 274e8e248981d3fac49a842f033d512f039497ae Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Tue, 8 Apr 2025 04:35:58 +0000 Subject: [PATCH 65/71] check_run --- .github/workflows/watch-lintdiff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-lintdiff.yaml b/.github/workflows/watch-lintdiff.yaml index 47fa438bd3c9..a4c9f115430a 100644 --- a/.github/workflows/watch-lintdiff.yaml +++ b/.github/workflows/watch-lintdiff.yaml @@ -4,7 +4,7 @@ name: "~Watch - LintDiff" on: # check_suite is preferred over check_run to avoid triggering on all check # runs. In some cases, check_run must be used in testing environments. - check_suite: + check_run: types: completed workflow_run: From 23055659b998c3b71d97ebad08834dd5325f215d Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Wed, 9 Apr 2025 23:06:30 +0800 Subject: [PATCH 66/71] Update specificationRepositoryConfiguration.json (only for test) (#3644) * Update specificationRepositoryConfiguration.json * Remove Java, TypeScript, and Go configurations * Add TypeSpec configurations for Java, TS, and Go --- specificationRepositoryConfiguration.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specificationRepositoryConfiguration.json b/specificationRepositoryConfiguration.json index 0b2c1930d549..fbb51a455ca3 100644 --- a/specificationRepositoryConfiguration.json +++ b/specificationRepositoryConfiguration.json @@ -23,7 +23,8 @@ }, "azure-sdk-for-python": { "integrationRepository": "azure-sdk/azure-sdk-for-python", - "mainRepository": "Azure/azure-sdk-for-python" + "mainRepository": "Azure/azure-sdk-for-python", + "mainBranch": "fix-pipeline-2025-04-09-2" }, "azure-powershell": { "integrationRepository": "azure-sdk/azure-powershell", From 16ceaff4a7479bbc86c05524721d16371e8f5864 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Sun, 20 Apr 2025 23:20:00 -0400 Subject: [PATCH 67/71] Update tspconfig.yaml and add copilot-instructions.md for SDK release process --- .github/copilot-instructions.md | 69 +++++++++++++++++++ .../Contoso.WidgetManager/tspconfig.yaml | 1 + 2 files changed, 70 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000000..e12099868111 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,69 @@ +# Rules for SDK release process and Spec readiness to generate and release SDK +- If a Spec project path is not available, prompt user to provide or select the Spec project root +- Verify whether spec project root is TypeSpec project. This guidelines to support spec to SDK release is currently + supported only for TypeSpec + - Follow the steps below for SDK release process from TypeSpec specification + +## SDK release process from TypeSpec + +### Pre-requisites +- User should have a GitHub account and should be logged in to GitHub account using GitHub CLI `gh auth login`. + +- Check the current branch name for the cloned GitHub repo. Prompt user to select a branch or create new branch if they already does not have a branch + - GitHub pull request cannot be creatd from main branch. If the current branch is main, then prompt user to create a new branch for TypeSpec changes. + - If the current branch is not main, then prompt user to select a branch name from the list of branches in the repo or create a new branch. + - If a branch needs to be created then prompt user to run command: `git checkout -b `. + - If a branch already exists and if it is not same as current branch then prompt user to run command `git checkout ` to switch to the branch. + +### Steps to generate and release SDK from TypeSpec API specification +- Step 1: Verify if the user has a TypeSpec project path for API specification. + - If not, then prompt user to create a TypeSpec for API specification. + - If user has a TypeSpec project path, then prompt user to provide TypeSpec project name and then verify if the path is a valid TypeSpec project. +- Step 2: Run TypeSpec validation and make sure there are no TypeSpec validation + - If there are any TypeSpec validation failures, then highlight the failures to fix them. +- Step 3: Show list of changed files in the repo and prompt user to confirm if the changes are correct. + - If the user confirms, then proceed with the next steps. + - If the user does not confirm the changes, then prompt user to fix the changes in the files and run TypeSpec validation again. +- Step 4: Verify user has a GitHub account and is logged in to GitHub account using GitHub CLI `gh auth login`. + - if GitHub login fails then prompt user to make sure to install GitHub CLI and login to GitHub account using `gh auth login` command. + - If user is logged in to GitHub account, then proceed with the next steps. +- Step 5: Create Pull request for changes. + - Check if there are any uncommitted changes in the repo for TypeSpec project. + - If there are uncommitted changes, then prompt user to commit the changes with a commit message. + - Prompt to run `git add ` command to add the changes to the staging area. + - Prompt to run `git commit -m ""` command to commit the changes. + - Push the changes to GitHup remote. Make sure that remote branch name is not "main" + - Prompt user to run `git push -u origin ` command to push the changes to GitHub remote. + - Next step is to create a pull request for the branch. This step will either creates a new pull request or find the existing pull request for the branch. + - Check the current branch name. Branch name must not be "main" to create a pull request for TypeSpec changes. + - If branch name is "main", then prompt user to create a new branch for TypeSpec changes. command: `git checkout -b `. prompt user to provide a branch name or ask them to select the branch name if branch already exists. + - Push the changes to the remote branch in GitHub repo. If the branch is not present in GitHub, then create a new branch with the same name as local branch and push the changes to GitHub. + - Generate a title and description based on the changes. Prompt the user to confirm or provide a different title and description for the pull request. + - Prompt user to select the the target branch for pull request. Default target branch is "main". + - Create pull requet for changes in TypeSpec project, target branch, title and descriptions +- Step 6: Verify target lifecycle of API specification. + - Target lifecycle is the lifecycle of the API specification. Show below list of options and prompt user to select one of the options. + - Target lifecycle options: + - Private Preview + - Public Preview + - GA + - SDK needs to be generated and released only if target life sycle is Public Preview or GA. + - If target lifecycle is Private Preview, then inform user that SDK generation and release is not required for Private Preview. +- Step 7: Check SDK release readiness for TypeSpec API specification. + - Prompt user with a yes or no question to confirm if their TypeSpec API specification is ready for SDK generation and release or they have more API specification changes to be done. + - If user confirms, then proceed with the next steps. + - If user does not confirm, then inform user that SDK release process can be rerun when API specification is ready for SDK generation. +- Step 8: Create release plan work item + - Prompt user to provide the following details for the release plan work item: + - Service Tree ID for the Service + - Service Name + - Product Service tree ID for the Product + - Product Name + - Expected release timeline in Month and Year (e.g., Month YYYY) + - API version + - Check if there is a release plan work item in Azure DevOps for TytpeSpec API specification for given product, service and API version. + - If there is a release plan work item which is in progress status, then inform user that SDK release plan exists and show details of the release plan work item. + - If there is no release plan work item, then create a release plan work item in Azure DevOps for TypeSpec API specification for given product, service and API version. + + + diff --git a/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml b/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml index 52a82b1e045a..ad992cd6097d 100644 --- a/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml +++ b/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml @@ -15,6 +15,7 @@ options: emitter-output-dir: "{project-root}/.." output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/widgets.json" "@azure-tools/typespec-python": + package-name: "azure-contoso-widgetmanager" package-dir: "azure-contoso-widgetmanager" namespace: "azure.contoso.widgetmanager" generate-test: true From acfc55d43209aecdaa3ef2216bb67fdd0372c833 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Sun, 20 Apr 2025 23:42:29 -0400 Subject: [PATCH 68/71] Update SDK release process instructions --- .github/copilot-instructions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e12099868111..ecf0aa0600c4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -41,6 +41,8 @@ - Generate a title and description based on the changes. Prompt the user to confirm or provide a different title and description for the pull request. - Prompt user to select the the target branch for pull request. Default target branch is "main". - Create pull requet for changes in TypeSpec project, target branch, title and descriptions + - Get pull request details and show check status if pull request number is nto 0 + - Get pull request comments and check if there is any action item for the user. - Step 6: Verify target lifecycle of API specification. - Target lifecycle is the lifecycle of the API specification. Show below list of options and prompt user to select one of the options. - Target lifecycle options: From 33666623340a4b72d39f8d0dfc65c3b77c0b9846 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Mon, 21 Apr 2025 10:46:43 -0400 Subject: [PATCH 69/71] Updated copilot instructions --- .github/copilot-instructions.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ecf0aa0600c4..78ff521e7778 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -41,9 +41,9 @@ - Generate a title and description based on the changes. Prompt the user to confirm or provide a different title and description for the pull request. - Prompt user to select the the target branch for pull request. Default target branch is "main". - Create pull requet for changes in TypeSpec project, target branch, title and descriptions - - Get pull request details and show check status if pull request number is nto 0 - - Get pull request comments and check if there is any action item for the user. -- Step 6: Verify target lifecycle of API specification. +- Step 6: Get TypeSpec pull request details for current branch and show the details of the pull request. +- Step 7: Get pull request comments and check if there is any action item for the user. +- Step 8: Verify target lifecycle of API specification. - Target lifecycle is the lifecycle of the API specification. Show below list of options and prompt user to select one of the options. - Target lifecycle options: - Private Preview @@ -51,11 +51,7 @@ - GA - SDK needs to be generated and released only if target life sycle is Public Preview or GA. - If target lifecycle is Private Preview, then inform user that SDK generation and release is not required for Private Preview. -- Step 7: Check SDK release readiness for TypeSpec API specification. - - Prompt user with a yes or no question to confirm if their TypeSpec API specification is ready for SDK generation and release or they have more API specification changes to be done. - - If user confirms, then proceed with the next steps. - - If user does not confirm, then inform user that SDK release process can be rerun when API specification is ready for SDK generation. -- Step 8: Create release plan work item +- Step 9: Create release plan work item before generating the SDK or get a URL for existing release plan work item. - Prompt user to provide the following details for the release plan work item: - Service Tree ID for the Service - Service Name @@ -66,6 +62,3 @@ - Check if there is a release plan work item in Azure DevOps for TytpeSpec API specification for given product, service and API version. - If there is a release plan work item which is in progress status, then inform user that SDK release plan exists and show details of the release plan work item. - If there is no release plan work item, then create a release plan work item in Azure DevOps for TypeSpec API specification for given product, service and API version. - - - From 46acb156d402699b8da162cf30171a0021b89482 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Mon, 21 Apr 2025 10:51:37 -0400 Subject: [PATCH 70/71] Committing TypeSpec changes for SDK generation --- .../examples/2022-12-01/Widgets_GetWidgetSample.json | 2 +- .../stable/2022-12-01/examples/Widgets_GetWidgetSample.json | 2 +- .../Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/contosowidgetmanager/Contoso.WidgetManager/examples/2022-12-01/Widgets_GetWidgetSample.json b/specification/contosowidgetmanager/Contoso.WidgetManager/examples/2022-12-01/Widgets_GetWidgetSample.json index ecab18c65303..ccf33c894707 100644 --- a/specification/contosowidgetmanager/Contoso.WidgetManager/examples/2022-12-01/Widgets_GetWidgetSample.json +++ b/specification/contosowidgetmanager/Contoso.WidgetManager/examples/2022-12-01/Widgets_GetWidgetSample.json @@ -1,6 +1,6 @@ { "operationId": "Widgets_GetWidget", - "title": "Get widget by widget name.", + "title": "Get widget by widget name. Test change", "parameters": { "api-version": "2022-12-01", "widgetName": "searchbox" diff --git a/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/examples/Widgets_GetWidgetSample.json b/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/examples/Widgets_GetWidgetSample.json index ecab18c65303..ccf33c894707 100644 --- a/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/examples/Widgets_GetWidgetSample.json +++ b/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/examples/Widgets_GetWidgetSample.json @@ -1,6 +1,6 @@ { "operationId": "Widgets_GetWidget", - "title": "Get widget by widget name.", + "title": "Get widget by widget name. Test change", "parameters": { "api-version": "2022-12-01", "widgetName": "searchbox" diff --git a/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json b/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json index 34f77daf7126..17949c8e5d28 100644 --- a/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json +++ b/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json @@ -115,7 +115,7 @@ } }, "x-ms-examples": { - "Get widget by widget name.": { + "Get widget by widget name. Test change": { "$ref": "./examples/Widgets_GetWidgetSample.json" } } From 78e7c9f66c039041f728fd157b3d7e887d9caf8b Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Mon, 21 Apr 2025 13:26:23 -0400 Subject: [PATCH 71/71] Describe your changes for AI.Face TypeSpec --- specification/ai/Face/main.tsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/ai/Face/main.tsp b/specification/ai/Face/main.tsp index 8db754e34662..b06b8f002191 100644 --- a/specification/ai/Face/main.tsp +++ b/specification/ai/Face/main.tsp @@ -14,7 +14,7 @@ using TypeSpec.Rest; using TypeSpec.Versioning; @useAuth(KeyAuth | AADToken) -@service(#{ title: "Azure AI Face API" }) +@service(#{ title: "Azure AI Face APIs" }) @versioned(Versions) @server( "{endpoint}/face/{apiVersion}",