Rehash github workflow PR management

import/copybara status checks cannot trigger workflows, so we need to poll for them instead.
Rehash management of imported PRs to support this. When the import is successful, github actions will comment on the
PR with a link to Gerrit. After the CL merges on Gerrit and is pushed back to Github, the PR will be automatically closed.

This is an imported pull request from
https://github.com/google/dawn/pull/9

GITHUB_PR_HEAD_SHA=9aabe4663503b6aeef832b2de2c5aafbcd73ce8c
ORIGINAL_AUTHOR=Austin Eng <2154796+austinEng@users.noreply.github.com>
GitOrigin-RevId: 927bdc938f74e23a92f4fdd6b3b3c11d5443c883
Change-Id: Iacc6a9ca3e768b1d936eae0fe7c259dfd1771cc1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159022
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/.github/workflows/on-copybara-import.yml b/.github/workflows/on-copybara-import.yml
deleted file mode 100644
index 3eb6516..0000000
--- a/.github/workflows/on-copybara-import.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: On copybara import
-
-on:
-  status
-
-jobs:
-  debug:
-    name: debug
-    runs-on: ubuntu-latest
-    steps:
-      name: Dump Github Context
-      run: echo '${{ toJSON(github) }}'
-
-  comment:
-    name: comment
-    if: github.event.state == 'success' && github.event.context == 'import/copybara'
-    runs-on: ubuntu-latest
-    steps:
-
-    - name: Find PR
-      uses: sharesight/find-github-pull-request@1.2.0
-      with:
-        commitSha: ${{ github.event.sha }}
-        allowClosed: false
-        failIfNotFound: false
-
-    - name: Comment on PR
-      if: steps.find-pr.outputs.number != ''
-      uses: marocchino/sticky-pull-request-comment@v2
-      with:
-        number: ${{ steps.find-pr.outputs.number }}
-        header: pr_was_imported # key to reuse the same comment
-        message: |
-          👋 Thanks for your contribution! Your PR has been imported to Gerrit.
-          Please visit ${{ github.event.target_url }} to see it and CC yourself on the change.
-          All comments are handled within Gerrit. Any comments on the GitHub PR may be ignored.
-          You can continue to upload commits to the PR in order to address feedback from Gerrit.
diff --git a/.github/workflows/pr-manager.yml b/.github/workflows/pr-manager.yml
new file mode 100644
index 0000000..eac4ada
--- /dev/null
+++ b/.github/workflows/pr-manager.yml
@@ -0,0 +1,60 @@
+# Sub-workflow to manage commenting on and closing PRs.
+name: PR Manager
+
+on:
+  workflow_dispatch:
+    inputs:
+      pullNumber:
+        type: number
+        required: true
+      state:
+        type: string
+        required: true
+      targetUrl:
+        type: string
+
+jobs:
+  imported:
+    name: PR Imported
+    if: inputs.state == 'success' || inputs.state == 'SUCCESS'
+    runs-on: ubuntu-latest
+    permissions:
+      pull-requests: write
+    steps:
+    - uses: marocchino/sticky-pull-request-comment@v2
+      with:
+        number: ${{ inputs.pullNumber }}
+        header: pr_was_imported # key to reuse the same comment
+        skip_unchanged: true
+        message: |
+          👋 Thanks for your contribution! Your PR has been imported to Gerrit.
+          Please visit ${{ inputs.targetUrl }} to see it and CC yourself on the change.
+          After iterating on feedback, please comment on the Gerrit review to notify reviewers.
+          All reviews are handled within Gerrit, any comments on the GitHub PR may be missed.
+          You can continue to upload commits to this PR, and they will be automatically imported
+          into Gerrit.
+
+  merged:
+    name: PR Merged
+    if: inputs.state == 'merged' || inputs.state == 'MERGED'
+    runs-on: ubuntu-latest
+    permissions:
+      pull-requests: write
+    steps:
+    - uses: marocchino/sticky-pull-request-comment@v2
+      with:
+        number: ${{ inputs.pullNumber }}
+        header: pr_was_merged # key to reuse the same comment
+        skip_unchanged: true
+        message: 🚀 PR was merged in ${{ inputs.targetUrl }}!
+
+    - name: Close PR
+      uses: actions/github-script@v6
+      with:
+        script: |
+          await github.rest.pulls.update({
+            owner: context.repo.owner,
+            repo: context.repo.repo,
+            pull_number: ${{ inputs.pullNumber }},
+            state: 'closed',
+          });
diff --git a/.github/workflows/pr-watcher.yml b/.github/workflows/pr-watcher.yml
new file mode 100644
index 0000000..ef365b3
--- /dev/null
+++ b/.github/workflows/pr-watcher.yml
@@ -0,0 +1,131 @@
+# Workflow that runs periodically to check whether PRs have been imported into
+# Gerrit and if they have been merged. The workflow adds a PR comment after
+# import and after merge. PRs that are merged upstream are then closed.
+name: PR Watcher
+on:
+  workflow_dispatch:
+  schedule:
+    - cron: "*/10 * * * *" # Every 10 minutes
+
+jobs:
+  check:
+    name: Check Copybara Import Status
+    runs-on: ubuntu-latest
+    permissions:
+      actions: write
+    steps:
+      - name: Check Pull Requests
+        uses: actions/github-script@v6
+        with:
+          script: |
+            // Get all open PRs.
+            const pulls = await github.rest.pulls.list({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              state: 'open',
+              base: 'main',
+            });
+
+            for (const pull of pulls.data) {
+              const query = `query($owner:String!, $name:String!, $pullNumber:Int!, $statusContext:String!) {
+                repository(owner:$owner, name:$name) {
+                  pullRequest(number:$pullNumber) {
+                    commits(last:1) {
+                      nodes {
+                        commit {
+                          status {
+                            context(name: $statusContext) {
+                              state
+                              targetUrl
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }`;
+
+              // Query the import/copybara status check.
+              const result = await github.graphql(query, {
+                owner: context.repo.owner,
+                name: context.repo.repo,
+                pullNumber: pull.number,
+                statusContext: 'import/copybara',
+              });
+              const nodes = result.repository.pullRequest.commits.nodes;
+              const commit = nodes[0].commit;
+              if (commit && commit.status && commit.status.context) {
+                // Forward the status check information to the pr-manager workflow.
+                await github.rest.actions.createWorkflowDispatch({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  ref: 'main',
+                  workflow_id: 'pr-manager.yml',
+                  inputs: {
+                    pullNumber: pull.number.toString(),
+                    state: commit.status.context.state,
+                    targetUrl: commit.status.context.targetUrl,
+                  },
+                });
+              }
+
+              // Look through all timeline events on the PR.
+              // When the PR is merged upstream, it will include a backreference back to
+              // the PR in the commit message.
+              const timeline = github.paginate.iterator(github.rest.issues.listEventsForTimeline, {
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                issue_number: pull.number,
+              });
+              for await (const { data: timelineData } of timeline) {
+                for (const timelineItem of timelineData) {
+                  if (timelineItem.event === 'referenced' && timelineItem.commit_id) {
+                    try {
+                      // Compare commit ids against refs/heads/main to see if they are
+                      // included in the main branch.
+                      const compare = await github.rest.repos.compareCommitsWithBasehead({
+                        basehead: `${timelineItem.commit_id}...refs/heads/main`,
+                        owner: context.repo.owner,
+                        repo: context.repo.repo,
+                      });
+                      if (compare.data.status == "ahead" || compare.data.status == "identical") {
+                        // This is a commit that has already been merged into main.
+                        const commit = await github.rest.repos.getCommit({
+                          owner: context.repo.owner,
+                          repo: context.repo.repo,
+                          ref: timelineItem.commit_id,
+                        });
+                        const message = commit.data.commit.message;
+                        const lines = message.split('\n');
+                        for (const line of lines) {
+                          // Check if the commit has a footer referencing a PR.
+                          const tag = 'GITHUB_PR_HEAD_SHA=';
+                          if (line.startsWith(tag)) {
+                            const sha = line.slice(tag.length);
+                            // The merged commit footer matches the PR sha.
+                            if (pull.head.sha === sha) {
+                              // Close the PR.
+                              await github.rest.actions.createWorkflowDispatch({
+                                owner: context.repo.owner,
+                                repo: context.repo.repo,
+                                ref: 'main',
+                                workflow_id: 'pr-manager.yml',
+                                inputs: {
+                                  pullNumber: pull.number.toString(),
+                                  state: 'merged',
+                                  targetUrl: commit.data.html_url,
+                                },
+                              });
+                            }
+                          }
+                        }
+                      }
+                    } catch (err) {
+                      // This is OK because not all commits will be comparable.
+                      console.warn(err);
+                    }
+                  }
+                }
+              }
+            }