summaryrefslogtreecommitdiff
path: root/.github/workflows/pr-playground.yml
blob: cc060061421acde4dcade4bc52ff472eefea946d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
name: Post Playground link to PR
on:
  pull_request_target:
    types: [labeled]
  workflow_run:
    workflows: ["WebAssembly"]
    types: [completed]

jobs:
  post-summary:
    name: Post Playground link
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    # Post a comment only if the PR status check is passed and the PR is labeled with `Playground`.
    # Triggered twice: when the PR is labeled and when PR build is passed.
    if: >-
      ${{ false
      || (true
        && github.event_name == 'pull_request_target'
        && contains(github.event.pull_request.labels.*.name, 'Playground'))
      || (true
        && github.event_name == 'workflow_run'
        && github.event.workflow_run.conclusion == 'success'
        && github.event.workflow_run.event == 'pull_request')
      }}
    steps:
      - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const fs = require('fs/promises');

            const buildWorkflowPath = '.github/workflows/wasm.yml';
            const findSuccessfuBuildRun = async (pr) => {
              const opts = github.rest.actions.listWorkflowRunsForRepo.endpoint.merge({
                owner: context.repo.owner,
                repo: context.repo.repo,
                status: 'success',
                branch: pr.head.ref,
              });
              const runs = await github.paginate(opts);
              const buildRun = runs.find(run => run.path == buildWorkflowPath);
              return buildRun;
            }

            const postComment = async (body, pr) => {
              const { data: comments } = await github.rest.issues.listComments({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: pr.number,
              });

              const commentOpts = { owner: context.repo.owner, repo: context.repo.repo, body: comment };

              const existingComment = comments.find(comment => comment.body.startsWith(magicComment));
              if (existingComment) {
                core.info(`Updating existing comment: ${existingComment.html_url}`);
                await github.rest.issues.updateComment({
                  ...commentOpts, comment_id: existingComment.id
                });
              } else {
                await github.rest.issues.createComment({
                  ...commentOpts, issue_number: pr.number
                });
              }
            }

            const derivePRNumber = async () => {
              if (context.payload.pull_request) {
                return context.payload.pull_request.number;
              }
              // Workaround for https://github.com/orgs/community/discussions/25220

              const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts({
                owner: context.repo.owner,
                repo: context.repo.repo,
                run_id: context.payload.workflow_run.id,
              });
              const artifact = artifacts.find(artifact => artifact.name == 'github-pr-info');
              if (!artifact) {
                throw new Error('Cannot find github-pr-info.txt artifact');
              }

              const { data } = await github.rest.actions.downloadArtifact({
                owner: context.repo.owner,
                repo: context.repo.repo,
                artifact_id: artifact.id,
                archive_format: 'zip',
              });

              await fs.writeFile('pr-info.zip', Buffer.from(data));
              await exec.exec('unzip', ['pr-info.zip']);
              return await fs.readFile('github-pr-info.txt', 'utf8');
            }

            const prNumber = await derivePRNumber();

            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prNumber,
            });

            core.info(`Checking if the PR ${prNumber} is labeled with Playground...`);
            if (!pr.labels.some(label => label.name == 'Playground')) {
              core.info(`The PR is not labeled with Playground.`);
              return;
            }

            core.info(`Checking if the build is successful for ${pr.head.ref} in ${pr.head.repo.owner.login}/${pr.head.repo.name}...`);
            const buildRun = await findSuccessfuBuildRun(pr);
            if (!buildRun) {
              core.info(`No successful build run found for ${buildWorkflowPath} on ${pr.head.ref} yet.`);
              return;
            }
            core.info(`Found a successful build run: ${buildRun.html_url}`);

            const runLink = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
            const magicComment = `<!-- AUTO-GENERATED-COMMENT-PR-PLAYGROUND -->`;
            const comment = `${magicComment}
            **Try on Playground**: https://ruby.github.io/play-ruby?run=${buildRun.id}
            This is an automated comment by [\`pr-playground.yml\`](${runLink}) workflow.
            `;
            core.info(`Comment: ${comment}`);
            await postComment(comment, pr);