diff --git a/.github/workflows/pr-template.yml b/.github/workflows/pr-template.yml new file mode 100644 index 0000000000..cf4f83152a --- /dev/null +++ b/.github/workflows/pr-template.yml @@ -0,0 +1,142 @@ +name: PR Template + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + validate: + name: Validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ github.event.repository.default_branch }} + + - name: Validate PR template + id: validate + continue-on-error: true + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: node scripts/validate-pr-template.js + + - name: Handle incomplete PR + if: steps.validate.outcome == 'failure' + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.BOT }} + script: | + const label = "reason: incomplete pr"; + + try { + await github.rest.issues.getLabel({ + owner: context.repo.owner, + repo: context.repo.name, + name: label + }); + } catch (error) { + if (error.status === 404) { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.name, + name: label, + color: "D93F0B", + description: "Your PR template is not complete. (e.g. missing the requirements form, missing website preview)" + }); + } else { + throw error; + } + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.name, + issue_number: context.issue.number, + labels: [label] + }); + + const body = [ + "## ❌ Incomplete PR template", + "", + "This pull request is missing required information from the PR template.", + "", + "Please make sure that:", + "", + "- Every required checkbox is checked.", + "- **Website Preview** is filled out.", + "- **Website Purpose** is filled out.", + "", + "Once the template has been completed, this check will run again automatically." + ].join("\n"); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.name, + issue_number: context.issue.number, + body + }); + + - name: Remove incomplete label + if: steps.validate.outcome == 'success' + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.BOT }} + script: | + const label = "reason: incomplete pr"; + + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.name, + issue_number: context.issue.number, + name: label + }); + } catch (error) { + if (error.status !== 404) { + throw error; + } + + tests: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - run: npm ci + + - name: Fetch PR information + if: github.event_name == 'pull_request' + run: | + echo "PR_AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV + echo "PR_AUTHOR_ID=${{ github.event.pull_request.user.id }}" >> $GITHUB_ENV + + LABELS=$(gh api --jq '[.labels[].name]' /repos/{owner}/{repo}/pulls/${{ github.event.number }}) + echo "PR_LABELS=$LABELS" >> $GITHUB_ENV + + FILES=$(gh api --jq '[.[] | select(.status != "removed") | .filename]' /repos/{owner}/{repo}/pulls/${{ github.event.number }}/files) + echo "CHANGED_FILES=$FILES" >> $GITHUB_ENV + + REMOVED_FILE_DATA=$(gh api --jq '[.[] | select(.status == "removed") | {name: .filename, data: .patch}]' /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files) + echo "DELETED_FILES=$REMOVED_FILE_DATA" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ github.token }} + + - uses: gacts/install-dnscontrol@890ba3249712c51203ccb7aba63307c0091e921b # v1.3.4 + if: github.event_name == 'pull_request' && contains(fromJson(env.CHANGED_FILES), 'dnsconfig.js') + with: + version: 4.14.0 + + - name: Run DNSControl checks + run: dnscontrol check + if: github.event_name == 'pull_request' && contains(fromJson(env.CHANGED_FILES), 'dnsconfig.js') + + - name: Run tests + run: npx ava tests/*.test.js --timeout=1m