├── composer.json ├── action.yml ├── CHANGELOG.md ├── SECURITY.md ├── run.sh ├── README.md ├── funcs.sh └── LICENSE.txt /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "automattic/action-pr-is-up-to-date", 3 | "description": "GitHub Action to check that Pull Requests are up to date with respect to a git tag.", 4 | "type": "library", 5 | "license": "GPL-2.0-or-later", 6 | "require": {}, 7 | "require-dev": { 8 | "automattic/jetpack-changelogger": "^3.2.2" 9 | }, 10 | "scripts": { 11 | "test-js": "tests/run-tests.sh" 12 | }, 13 | "extra": { 14 | "autotagger": { 15 | "major": true 16 | }, 17 | "autorelease": true, 18 | "mirror-repo": "Automattic/action-pr-is-up-to-date", 19 | "changelogger": { 20 | "link-template": "https://github.com/Automattic/action-pr-is-up-to-date/compare/v${old}...v${new}" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: PR is Up To Date 2 | description: Check that Pull Requests are up to date with respect to a git tag. 3 | branding: 4 | icon: 'check-square' 5 | color: 'green' 6 | inputs: 7 | branch: 8 | description: Only check PRs being merged into this branch. Defaults to the repo's default branch. 9 | required: false 10 | default: ${{ github.event.repository.default_branch }} 11 | status: 12 | description: Status context for the status check. Default is "PR is up to date". 13 | required: false 14 | description-fail: 15 | description: Description field for the status check when the PR is out of date. Default is "This PR needs a $BRANCH merge or rebase." 16 | required: false 17 | description-ok: 18 | description: Description field for the status check when the PR is up to date. Default is empty. 19 | required: false 20 | tags: 21 | description: Whitespace-separated list of tags that the PR must be up to date with. Required when called on pull_request, but currently defaults to `input.tag` if that is set for back compat. 22 | required: false 23 | tag: 24 | description: Tag that PRs must be up to date with. Required when called on push. 25 | required: false 26 | paths: 27 | description: When called on push, only process PRs that touch a file matching one of these paths (one path per line). Any path format accepted by `git diff` may be used. 28 | required: false 29 | token: 30 | description: GitHub Access Token. The user associated with this token will show up as the "creator" of the status check. 31 | required: false 32 | default: ${{ github.token }} 33 | runs: 34 | using: composite 35 | steps: 36 | - shell: bash 37 | env: 38 | API_TOKEN: ${{ inputs.token }} 39 | BRANCH: ${{ inputs.branch }} 40 | CONTEXT: ${{ inputs.status }} 41 | DESCRIPTION_FAIL: ${{ inputs.description-fail }} 42 | DESCRIPTION_OK: ${{ inputs.description-ok }} 43 | PATHS: ${{ inputs.paths }} 44 | TAG: ${{ inputs.tag }} 45 | TAGS: ${{ inputs.tags }} 46 | run: $GITHUB_ACTION_PATH/run.sh 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [2.0.2-alpha] - unreleased 9 | 10 | This is an alpha version! The changes listed here are not final. 11 | 12 | ## [2.0.1] - 2022-11-01 13 | ### Added 14 | - Tooling: enable automatic GitHub releases when a new version of the action is tagged, so the new version can be made available in the GitHub Actions marketplace. 15 | 16 | ### Changed 17 | - Updated package dependencies. 18 | 19 | ## [2.0.0] - 2022-07-06 20 | ### Added 21 | - Added the ability to check against multiple tags. [#22925] 22 | 23 | ### Changed 24 | - BREAKING: Changed the default value for the `status` input. [#22925] 25 | - Renaming `master` references to `main` where relevant. [#24712, #24661] 26 | - Updated package dependencies. [#24045] 27 | 28 | ### Fixed 29 | - Disable automatic garbage collection, like GitHub's checkout action does for its own checkouts. [#23047] 30 | - Fix documentation of the token parameter in README.md. [#22793] 31 | - Remove a stray comma. [#23046] 32 | - Speed up processing of tag push with paths. [#23123] 33 | - Try and fix source file not found error by specifying path. [#23022] 34 | 35 | ## [1.0.3] - 2022-02-09 36 | ### Changed 37 | - Core: update description and metadata before to publish to marketplace. 38 | 39 | ## [1.0.2] - 2021-12-07 40 | ### Changed 41 | - Updated package dependencies. 42 | 43 | ## [1.0.1] - 2021-08-26 44 | ### Changed 45 | - Avoid context expression substitution in GitHub Actions `run` steps. 46 | - Update package dependencies. 47 | 48 | ## 1.0.0 - 2021-04-05 49 | ### Added 50 | - Initial release. 51 | 52 | [2.0.2-alpha]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v2.0.1...v2.0.2-alpha 53 | [2.0.1]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v2.0.0...v2.0.1 54 | [2.0.0]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v1.0.3...v2.0.0 55 | [1.0.3]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v1.0.2...v1.0.3 56 | [1.0.2]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v1.0.1...v1.0.2 57 | [1.0.1]: https://github.com/Automattic/action-pr-is-up-to-date/compare/v1.0.0...v1.0.1 58 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/). 4 | 5 | ## Supported Versions 6 | 7 | Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions. 8 | 9 | ## Reporting a Vulnerability 10 | 11 | [Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure. 12 | 13 | **For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.** 14 | 15 | Our most critical targets are: 16 | 17 | * Jetpack and the Jetpack composer packages (all within this repo) 18 | * Jetpack.com -- the primary marketing site. 19 | * cloud.jetpack.com -- a management site. 20 | * wordpress.com -- the shared management site for both Jetpack and WordPress.com sites. 21 | 22 | For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic). 23 | 24 | _Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._ 25 | 26 | ## Guidelines 27 | 28 | We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines: 29 | 30 | * Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines). 31 | * Pen-testing Production: 32 | * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above). 33 | * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC. 34 | * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels. 35 | * To be eligible for a bounty, all of these guidelines must be followed. 36 | * Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability. 37 | 38 | We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties. 39 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 6 | source "$BASE/funcs.sh" 7 | 8 | GITHUB_API_URL="${GITHUB_API_URL:-https://api.github.com}" 9 | GITHUB_SERVER_URL="${GITHUB_SERVER_URL:-https://github.com}" 10 | [[ -n "$API_TOKEN" ]] || die "API_TOKEN must be set" 11 | [[ -n "$GITHUB_REPOSITORY" ]] || die "GITHUB_REPOSITORY must be set" 12 | [[ -n "$BRANCH" ]] || die "BRANCH must be set" 13 | CONTEXT="${CONTEXT:-PR is up to date}" 14 | DESCRIPTION_FAIL="${DESCRIPTION_FAIL:-This PR needs a $BRANCH merge or rebase.}" 15 | DESCRIPTION_OK="${DESCRIPTION_OK:-}" 16 | 17 | DATA_FAIL="$(jq --arg url "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" --arg context "$CONTEXT" --arg desc "$DESCRIPTION_FAIL" -n '{ "state": "failure", "target_url": $url, "context": $context, "description": $desc }')" 18 | DATA_OK="$(jq --arg url "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" --arg context "$CONTEXT" --arg desc "$DESCRIPTION_OK" -n '{ "state": "success", "target_url": $url, "context": $context, "description": $desc }')" 19 | 20 | case "$GITHUB_EVENT_NAME" in 21 | pull_request|pull_request_target) 22 | if [[ -n "$TAGS" ]]; then 23 | IFS=$' \t\r\n' read -d '' -ra TAGS <<<"$TAGS" || true 24 | elif [[ -n "$TAG" ]]; then 25 | # Back compat. 26 | echo "Deprecation: TAGS should be set instead of TAG on $GITHUB_EVENT_NAME" 27 | TAGS=( "$TAG" ) 28 | else 29 | die "TAGS (or TAG) must be set on $GITHUB_EVENT_NAME" 30 | fi 31 | 32 | PR="$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH")" 33 | if [[ -z "$PR" ]]; then 34 | echo "::error::Could not determine PR number from event data" 35 | cat "$GITHUB_EVENT_PATH" 36 | exit 1 37 | fi 38 | ;; 39 | push) 40 | [[ -n "$TAG" ]] || die "TAG must be set on $GITHUB_EVENT_NAME" 41 | if [[ "$GITHUB_REF" != "refs/tags/$TAG" ]]; then 42 | die "Push event for incorrect tag, $GITHUB_REF != refs/tags/$TAG" 43 | fi 44 | TAGS=( "$TAG" ) 45 | 46 | if [[ -n "$PATHS" ]]; then 47 | mapfile -t PATHS <<<"$PATHS" 48 | else 49 | PATHS=() 50 | fi 51 | ;; 52 | *) 53 | die "Unsupported event $GITHUB_EVENT_NAME" 54 | ;; 55 | esac 56 | 57 | TMPDIR="${TMPDIR:-/tmp}" 58 | DIR="$(mktemp -d "${TMPDIR%/}/pr-is-up-to-date.XXXXXXXX")" 59 | trap 'rm -rf $DIR' EXIT 60 | cd "$DIR" 61 | 62 | init_repo 63 | 64 | NOTIFY_SUCCESS=false 65 | case "$GITHUB_EVENT_NAME" in 66 | pull_request|pull_request_target) 67 | NOTIFY_SUCCESS=true 68 | echo "::group::Fetching PR $PR" 69 | fetch_prs "$PR" 70 | echo "::endgroup::" 71 | process_pr "$PR" 72 | ;; 73 | push) 74 | declare -i PAGE=1 75 | declare -i DEEPENBY=100 76 | while :; do 77 | echo "::group::Fetching PRs (page $PAGE)" 78 | JSON=$(curl --fail --get --header "authorization: Bearer $API_TOKEN" --data-urlencode "base=${BRANCH}" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls?state=open&per_page=100&page=${PAGE}") 79 | 80 | PRS=() 81 | mapfile -t PRS < <( jq -r '.[].number' <<<"$JSON" ) 82 | [[ ${#PRS[@]} != 0 ]] || break 83 | fetch_prs "${PRS[@]}" 84 | 85 | echo "::endgroup::" 86 | 87 | for PR in "${PRS[@]}"; do 88 | if should_process_pr "$PR"; then 89 | process_pr "$PR" 90 | else 91 | printf "\e[1;34mPR #%d does not touch the specified paths\e[0m\n" "$PR" 92 | fi 93 | done 94 | 95 | jq -e 'length == 100' <<<"$JSON" >/dev/null || break 96 | PAGE+=1 97 | done 98 | ;; 99 | *) 100 | die "Unsupported event $GITHUB_EVENT_NAME" 101 | ;; 102 | esac 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PR is Up-to-Date 2 | 3 | This [Github Action](https://github.com/features/actions) will check that PRs in the repo 4 | are up to date with respect to a tag. 5 | 6 | The idea is to work like GitHub's "Require branches to be up to date before merging" setting, 7 | but using one or more tags rather than the HEAD of the target branch. 8 | 9 | ## Example 10 | 11 | ```yaml 12 | name: PR is up-to-date 13 | on: 14 | pull_request_target: 15 | branches: [ main ] 16 | push: 17 | tags: [ latest ] 18 | 19 | jobs: 20 | check: 21 | name: Check 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Handle pull request 25 | if: github.event_name != 'push' 26 | uses: Automattic/action-pr-is-up-to-date@v2 27 | with: 28 | token: ${{ secrets.API_TOKEN_GITHUB }} 29 | tags: latest 30 | 31 | - name: Handle tag push 32 | if: github.event_name == 'push' 33 | uses: Automattic/action-pr-is-up-to-date@v2 34 | with: 35 | token: ${{ secrets.API_TOKEN_GITHUB }} 36 | tag: latest 37 | ``` 38 | 39 | ## Usage 40 | 41 | This action is intended to be triggered by `pull_request_target` or `pull_request` targeting the specified branch, and by a `push` to the specified tags. 42 | It will not work for pushes to anything else. 43 | 44 | ### On pull request 45 | 46 | ```yaml 47 | - uses: Automattic/action-pr-is-up-to-date@v2 48 | with: 49 | # Branch to use. Defaults to the repository's default branch. 50 | branch: 51 | 52 | # Specify the "context" for the status to set. This is what shows up in the 53 | # PR's checks list. The default is "PR is up to date". 54 | status: 55 | 56 | # Specify the "description" when the PR is out of date. 57 | # The default is "This PR needs a ${{ inputs.branch }} merge or rebase.". 58 | description-fail: 59 | 60 | # Specify the "description" when the PR is up to date. The default is empty. 61 | description-ok: 62 | 63 | # Specify the tags that are used for the check, separated by whitespace. 64 | # The tags must point to commits that are reachable from the input branch. 65 | # For backwards compatibility, if `tags` is not set but `tag` is, the single tag in `tag` will be used here. 66 | tags: 67 | 68 | # GitHub Access Token. The user associated with this token will show up 69 | # as the "creator" of the status check. 70 | token: 71 | ``` 72 | 73 | The current pull request will be checked for being up to date with the specified tags. The status check will be set accordingly. 74 | 75 | ### On tag push 76 | 77 | ```yaml 78 | - uses: Automattic/action-pr-is-up-to-date@v2 79 | with: 80 | # Branch to use. Defaults to the repository's default branch. 81 | branch: 82 | 83 | # Specify the "context" for the status to set. This is what shows up in the 84 | # PR's checks list. The default is "PR is up to date". 85 | status: 86 | 87 | # Specify the "description" when the PR is out of date. 88 | # The default is "This PR needs a ${{ inputs.branch }} merge or rebase.". 89 | description-fail: 90 | 91 | # Specify the tag that is used for the check. The tag must point to a commit that 92 | # is reachable from the input branch. 93 | tag: 94 | 95 | # Only process PRs that touch a file matching one of these paths (one path per line). 96 | # Any path format accepted by `git diff` may be used. 97 | paths: 98 | 99 | # GitHub Access Token. The user associated with this token will show up 100 | # as the "creator" of the status check. 101 | token: 102 | ``` 103 | 104 | All open pull requests targeting the specified branch will be checked for being up to date with the specified tag. 105 | Any that are not will have the status check set to a failing status. 106 | 107 | Pull requests to process may be filtered by setting `paths`. In that case, only PRs that affect the specified paths will be processed. 108 | 109 | ### Updating tags 110 | 111 | You may update tags manually, for example from the command line with 112 | ``` 113 | git tag --force $TAG $COMMIT 114 | git push --force origin $TAG 115 | ``` 116 | 117 | Or you might use another action to examine pushes to your branch and update the tags accordingly. 118 | In that case, you'd want to make sure to use a custom access token as [events triggered by the stock `GITHUB_TOKEN` will not trigger workflows](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) and you'll want this workflow to be triggered by the tag push. 119 | 120 | ### Multiple tags 121 | 122 | As of v2, this action supports use of multiple tags. The intended use is for a monorepo where you want separate tags per project. 123 | 124 | * On pull request, you'd analyze the paths affected by the PR to determine which tags apply and pass all relevant tags as `tags`. 125 | * On push to any of the tags, you'd pass that tag as `tag` and set `paths` to the paths that tag applies to. 126 | 127 | Perhaps something like this: 128 | 129 | ```yaml 130 | - name: Determine tags for PR or paths for pull request 131 | id: determine 132 | run: some-script.sh 133 | - name: Check PR 134 | if: github.event_name == 'pull_request' 135 | uses: Automattic/action-pr-is-up-to-date@v2 136 | with: 137 | tags: steps.determine.outputs.pr-tags 138 | - name: Check tag push 139 | if: github.event_name == 'push' 140 | uses: Automattic/action-pr-is-up-to-date@v2 141 | with: 142 | tag: steps.determine.outputs.push-tag 143 | paths: steps.determine.outputs.push-paths 144 | ``` 145 | -------------------------------------------------------------------------------- /funcs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | # Execute `git` with logging of commands. 6 | # 7 | # Inputs: 8 | # $*: Git command and options. 9 | function git { 10 | printf "\e[32m%s\e[0m\n" "/usr/bin/git $*" >&2 11 | /usr/bin/git "$@" 12 | } 13 | 14 | # Initialize the repo in the current directory. 15 | # 16 | # Inputs: 17 | # $BRANCH: Base branch, e.g. main. 18 | # $GITHUB_SERVER_URL: GitHub server URL. 19 | # $GITHUB_REPOSITORY: GitHub repository slug. 20 | # ${TAGS[@]}: Tags to check against. 21 | # Outputs: 22 | # ${TAGS[@]}: Sorted list of tags. 23 | function init_repo { 24 | echo "::group::Initializing repo" 25 | git init -q . 26 | git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" 27 | git config --local gc.auto 0 28 | echo "::endgroup::" 29 | 30 | echo "::group::Fetching tags" 31 | gitfetch --depth=1 origin tag "${TAGS[@]}" || die "Failed to fetch specified tags" 32 | mapfile -t TAGS < <(git tag --list --sort=committerdate "${TAGS[@]}") 33 | echo "Tags, in order:" 34 | local TAG 35 | for TAG in "${TAGS[@]}"; do 36 | printf " - %s: %s\n" "$TAG" "$(/usr/bin/git rev-parse --verify "$TAG")" 37 | done 38 | echo "::endgroup::" 39 | 40 | echo "::group::Fetching $BRANCH" 41 | echo "Fetching first commit" 42 | gitfetch --depth=1 origin "$BRANCH" 43 | local TAG 44 | for TAG in "${TAGS[@]}"; do 45 | if ! git merge-base --is-ancestor "$TAG" "origin/$BRANCH"; then 46 | echo "Fetching commits to $TAG" 47 | gitfetch --shallow-exclude="$TAG" origin "$BRANCH" 48 | if ! git merge-base --is-ancestor "$TAG" "origin/$BRANCH"; then 49 | die "Tag $TAG is not an ancestor of $BRANCH! Aborting." 50 | fi 51 | else 52 | echo "Already have commits to $TAG" 53 | fi 54 | done 55 | echo "::endgroup::" 56 | } 57 | 58 | # Execute `git fetch`, with logging and some cleanup. 59 | # 60 | # Note: I've tried including `--filter=tree:0` or `--filter=blob:none`, but git tends to try to 61 | # implicitly fetch later (including blobs and trees) when commits are missing which defeats the purpose. 62 | # 63 | # Inputs: 64 | # $*: Git fetch options. 65 | function gitfetch { 66 | git fetch "$@" && git_clean_shallow 67 | } 68 | 69 | # Clean up .git/shallow after a shallow fetch. 70 | # 71 | # A shallow fetch will add a graft point to .git/shallow, even if the parent of the graft point exists locally. 72 | # This makes sense for git's specific intention, but it's not what we want. 73 | # 74 | # This function updates .git/shallow to remove any revision where its parent revs exist, and fetches parent 75 | # revs for merge commits where some but not all parent revs exist. 76 | function git_clean_shallow { 77 | local REV REVS P PP PPM TOFETCH 78 | 79 | [[ -f .git/shallow ]] || return 0 80 | 81 | TOFETCH=() 82 | mapfile -t REVS < .git/shallow 83 | rm .git/shallow 84 | for REV in "${REVS[@]}"; do 85 | # Read parents. If any are missing, put the rev back in .git/shallow. If not all are missing, queue the missing ones to be fetched. 86 | mapfile -t PP < <(/usr/bin/git cat-file commit "$REV" 2>/dev/null | sed -n 's/^parent //p') 87 | PPM=() 88 | for P in "${PP[@]}"; do 89 | if ! /usr/bin/git cat-file -e "$P" &>/dev/null; then 90 | PPM+=( "$P" ) 91 | fi 92 | done 93 | if [[ ${#PPM[@]} -gt 0 ]]; then 94 | echo "$REV" >> .git/shallow 95 | if [[ ${#PP[@]} -ne ${#PPM[@]} ]]; then 96 | TOFETCH+=( "${PPM[@]}" ) 97 | fi 98 | fi 99 | done 100 | 101 | # If we queued any revs for fetching, fetch them now. Note that'll in turn re-run git_clean_shallow, which should now be able to remove the revs that had them as parents. 102 | if [[ ${#TOFETCH[@]} -gt 0 ]]; then 103 | gitfetch --depth=1 origin "${TOFETCH[@]}" 104 | fi 105 | } 106 | 107 | # Print a GitHub error message and exit. 108 | # 109 | # Inputs: 110 | # $*: Error message. 111 | function die { 112 | echo "::error::$*" 113 | exit 1 114 | } 115 | 116 | # Fetch one or more PRs. 117 | # 118 | # Inputs: 119 | # $*: PR numbers to fetch. 120 | # $BRANCH: Base branch, e.g. main. 121 | function fetch_prs { 122 | local PR REFS=() 123 | for PR in "$@"; do 124 | REFS+=( "+refs/pull/$PR/head:refs/remotes/pulls/$PR" ) 125 | done 126 | 127 | gitfetch --shallow-exclude="$BRANCH" origin "${REFS[@]}" 128 | } 129 | 130 | # Test if a PR should be processed. 131 | # 132 | # Inputs: 133 | # $1: PR number to test. 134 | # $BRANCH: Base branch, e.g. main. 135 | # $DEEPENBY: How much to deepen by if we're having to deepen $BRANCH. If empty, it'll deepen by just enough to find the merge base. 136 | # ${PATHS[@]}: Paths that must be touched. 137 | # Returns: 0 if the PR should be processed, non-zero otherwise. 138 | function should_process_pr { 139 | local MB PR=$1 140 | if [[ ${#PATHS[@]} -gt 0 ]]; then 141 | git rev-parse --verify "pulls/$PR" &>/dev/null || die "PR #$PR has not been fetched" 142 | MB="$(git merge-base "pulls/$PR" "origin/$BRANCH")" 143 | # We need to find the merge base in order to diff. If there wasn't one, fetch the needed revs and try again. 144 | if [[ -z "$MB" ]]; then 145 | if [[ -z "$DEEPENBY" || $DEEPENBY -lt 1 ]]; then 146 | echo "::group::Fetching $BRANCH to PR #$PR" 147 | # We need to fetch $BRANCH down to the PR, and then deepen the PR by 1 to get the actual merge base. 148 | gitfetch --shallow-exclude="refs/pull/$PR/head" origin "$BRANCH" || die "should_process_pr: Failed to fetch $BRANCH to PR #$PR" 149 | gitfetch --deepen=1 origin "+refs/pull/$PR/head:refs/remotes/pulls/$PR" || die "should_process_pr: Failed to fetch next revision for PR #$PR" 150 | echo "::endgroup::" 151 | MB="$(git merge-base "pulls/$PR" "origin/$BRANCH")" 152 | [[ -z "$MB" ]] && die "Failed to determine merge base for pulls/$PR and origin/$BRANCH" 153 | else 154 | # Keep deepening until we find the merge base. Double the DEEPENBY each time to try to adapt to busier repos. 155 | while [[ -z "$MB" ]]; do 156 | echo "::group::Fetching next $DEEPENBY revisions for $BRANCH" 157 | gitfetch --deepen="$DEEPENBY" origin "$BRANCH" || die "should_process_pr: Failed to fetch next $DEEPENBY revisions for $BRANCH" 158 | DEEPENBY=$(( DEEPENBY * 2 )) 159 | echo "::endgroup::" 160 | MB="$(git merge-base "pulls/$PR" "origin/$BRANCH")" 161 | done 162 | fi 163 | fi 164 | ! git diff --name-only --exit-code "$MB..pulls/$PR" -- "${PATHS[@]}" 165 | return 166 | fi 167 | 168 | return 0 169 | } 170 | 171 | # Update status on GitHub. 172 | # 173 | # Inputs: 174 | # $1: Commit hash being updated. 175 | # $2: JSON data for the status update. 176 | # $API_TOKEN: GitHub API token for updates. 177 | # $CI: Does nothing unless this is non-empty. 178 | # $GITHUB_API_URL: GitHub API URL for updates. 179 | # $GITHUB_REPOSITORY: GitHub repository for updates. 180 | function update_github_status { 181 | if [[ -n "$CI" ]]; then 182 | echo "::group::Setting GitHub status" 183 | curl -v \ 184 | --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${1}" \ 185 | --header "authorization: Bearer $API_TOKEN" \ 186 | --header 'content-type: application/json' \ 187 | --data "$2" 188 | local R=$? 189 | echo "::endgroup::" 190 | return $R 191 | fi 192 | } 193 | 194 | # Process a PR. 195 | # 196 | # Inputs: 197 | # $1: PR numbers to process. 198 | # $DATA_FAIL: JSON string to send to GitHub on failure. 199 | # $DATA_OK: JSON string to send to GitHub on success. 200 | # $NOTIFY_SUCCESS: `true` or `false`, whether to update the GitHub status on success. 201 | # ${TAGS[@]}: Tags to check against. 202 | # (and anything used by update_github_status) 203 | function process_pr { 204 | local TAG COMMIT PR=$1 205 | COMMIT="$(git rev-parse --verify "pulls/$PR")" 206 | [[ -z "$COMMIT" ]] && die "PR #$PR has not been fetched" 207 | for TAG in "${TAGS[@]}"; do 208 | if ! git merge-base --is-ancestor "$TAG" "$COMMIT"; then 209 | printf "\e[1;31mPR #%d is outdated\e[0m\n" "$PR" 210 | update_github_status "$COMMIT" "$DATA_FAIL" 211 | return 212 | fi 213 | done 214 | printf "\e[1;32mPR #%d is up to date\e[0m\n" "$PR" 215 | if $NOTIFY_SUCCESS; then 216 | update_github_status "$COMMIT" "$DATA_OK" 217 | fi 218 | } 219 | 220 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This program is free software; you can redistribute it and/or modify 2 | it under the terms of the GNU General Public License as published by 3 | the Free Software Foundation; either version 2 of the License, or 4 | (at your option) any later version. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program; if not, write to the Free Software 13 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 14 | 15 | 16 | =================================== 17 | 18 | 19 | GNU GENERAL PUBLIC LICENSE 20 | Version 2, June 1991 21 | 22 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 23 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | Everyone is permitted to copy and distribute verbatim copies 25 | of this license document, but changing it is not allowed. 26 | 27 | Preamble 28 | 29 | The licenses for most software are designed to take away your 30 | freedom to share and change it. By contrast, the GNU General Public 31 | License is intended to guarantee your freedom to share and change free 32 | software--to make sure the software is free for all its users. This 33 | General Public License applies to most of the Free Software 34 | Foundation's software and to any other program whose authors commit to 35 | using it. (Some other Free Software Foundation software is covered by 36 | the GNU Lesser General Public License instead.) You can apply it to 37 | your programs, too. 38 | 39 | When we speak of free software, we are referring to freedom, not 40 | price. Our General Public Licenses are designed to make sure that you 41 | have the freedom to distribute copies of free software (and charge for 42 | this service if you wish), that you receive source code or can get it 43 | if you want it, that you can change the software or use pieces of it 44 | in new free programs; and that you know you can do these things. 45 | 46 | To protect your rights, we need to make restrictions that forbid 47 | anyone to deny you these rights or to ask you to surrender the rights. 48 | These restrictions translate to certain responsibilities for you if you 49 | distribute copies of the software, or if you modify it. 50 | 51 | For example, if you distribute copies of such a program, whether 52 | gratis or for a fee, you must give the recipients all the rights that 53 | you have. You must make sure that they, too, receive or can get the 54 | source code. And you must show them these terms so they know their 55 | rights. 56 | 57 | We protect your rights with two steps: (1) copyright the software, and 58 | (2) offer you this license which gives you legal permission to copy, 59 | distribute and/or modify the software. 60 | 61 | Also, for each author's protection and ours, we want to make certain 62 | that everyone understands that there is no warranty for this free 63 | software. If the software is modified by someone else and passed on, we 64 | want its recipients to know that what they have is not the original, so 65 | that any problems introduced by others will not reflect on the original 66 | authors' reputations. 67 | 68 | Finally, any free program is threatened constantly by software 69 | patents. We wish to avoid the danger that redistributors of a free 70 | program will individually obtain patent licenses, in effect making the 71 | program proprietary. To prevent this, we have made it clear that any 72 | patent must be licensed for everyone's free use or not licensed at all. 73 | 74 | The precise terms and conditions for copying, distribution and 75 | modification follow. 76 | 77 | GNU GENERAL PUBLIC LICENSE 78 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 79 | 80 | 0. This License applies to any program or other work which contains 81 | a notice placed by the copyright holder saying it may be distributed 82 | under the terms of this General Public License. The "Program", below, 83 | refers to any such program or work, and a "work based on the Program" 84 | means either the Program or any derivative work under copyright law: 85 | that is to say, a work containing the Program or a portion of it, 86 | either verbatim or with modifications and/or translated into another 87 | language. (Hereinafter, translation is included without limitation in 88 | the term "modification".) Each licensee is addressed as "you". 89 | 90 | Activities other than copying, distribution and modification are not 91 | covered by this License; they are outside its scope. The act of 92 | running the Program is not restricted, and the output from the Program 93 | is covered only if its contents constitute a work based on the 94 | Program (independent of having been made by running the Program). 95 | Whether that is true depends on what the Program does. 96 | 97 | 1. You may copy and distribute verbatim copies of the Program's 98 | source code as you receive it, in any medium, provided that you 99 | conspicuously and appropriately publish on each copy an appropriate 100 | copyright notice and disclaimer of warranty; keep intact all the 101 | notices that refer to this License and to the absence of any warranty; 102 | and give any other recipients of the Program a copy of this License 103 | along with the Program. 104 | 105 | You may charge a fee for the physical act of transferring a copy, and 106 | you may at your option offer warranty protection in exchange for a fee. 107 | 108 | 2. You may modify your copy or copies of the Program or any portion 109 | of it, thus forming a work based on the Program, and copy and 110 | distribute such modifications or work under the terms of Section 1 111 | above, provided that you also meet all of these conditions: 112 | 113 | a) You must cause the modified files to carry prominent notices 114 | stating that you changed the files and the date of any change. 115 | 116 | b) You must cause any work that you distribute or publish, that in 117 | whole or in part contains or is derived from the Program or any 118 | part thereof, to be licensed as a whole at no charge to all third 119 | parties under the terms of this License. 120 | 121 | c) If the modified program normally reads commands interactively 122 | when run, you must cause it, when started running for such 123 | interactive use in the most ordinary way, to print or display an 124 | announcement including an appropriate copyright notice and a 125 | notice that there is no warranty (or else, saying that you provide 126 | a warranty) and that users may redistribute the program under 127 | these conditions, and telling the user how to view a copy of this 128 | License. (Exception: if the Program itself is interactive but 129 | does not normally print such an announcement, your work based on 130 | the Program is not required to print an announcement.) 131 | 132 | These requirements apply to the modified work as a whole. If 133 | identifiable sections of that work are not derived from the Program, 134 | and can be reasonably considered independent and separate works in 135 | themselves, then this License, and its terms, do not apply to those 136 | sections when you distribute them as separate works. But when you 137 | distribute the same sections as part of a whole which is a work based 138 | on the Program, the distribution of the whole must be on the terms of 139 | this License, whose permissions for other licensees extend to the 140 | entire whole, and thus to each and every part regardless of who wrote it. 141 | 142 | Thus, it is not the intent of this section to claim rights or contest 143 | your rights to work written entirely by you; rather, the intent is to 144 | exercise the right to control the distribution of derivative or 145 | collective works based on the Program. 146 | 147 | In addition, mere aggregation of another work not based on the Program 148 | with the Program (or with a work based on the Program) on a volume of 149 | a storage or distribution medium does not bring the other work under 150 | the scope of this License. 151 | 152 | 3. You may copy and distribute the Program (or a work based on it, 153 | under Section 2) in object code or executable form under the terms of 154 | Sections 1 and 2 above provided that you also do one of the following: 155 | 156 | a) Accompany it with the complete corresponding machine-readable 157 | source code, which must be distributed under the terms of Sections 158 | 1 and 2 above on a medium customarily used for software interchange; or, 159 | 160 | b) Accompany it with a written offer, valid for at least three 161 | years, to give any third party, for a charge no more than your 162 | cost of physically performing source distribution, a complete 163 | machine-readable copy of the corresponding source code, to be 164 | distributed under the terms of Sections 1 and 2 above on a medium 165 | customarily used for software interchange; or, 166 | 167 | c) Accompany it with the information you received as to the offer 168 | to distribute corresponding source code. (This alternative is 169 | allowed only for noncommercial distribution and only if you 170 | received the program in object code or executable form with such 171 | an offer, in accord with Subsection b above.) 172 | 173 | The source code for a work means the preferred form of the work for 174 | making modifications to it. For an executable work, complete source 175 | code means all the source code for all modules it contains, plus any 176 | associated interface definition files, plus the scripts used to 177 | control compilation and installation of the executable. However, as a 178 | special exception, the source code distributed need not include 179 | anything that is normally distributed (in either source or binary 180 | form) with the major components (compiler, kernel, and so on) of the 181 | operating system on which the executable runs, unless that component 182 | itself accompanies the executable. 183 | 184 | If distribution of executable or object code is made by offering 185 | access to copy from a designated place, then offering equivalent 186 | access to copy the source code from the same place counts as 187 | distribution of the source code, even though third parties are not 188 | compelled to copy the source along with the object code. 189 | 190 | 4. You may not copy, modify, sublicense, or distribute the Program 191 | except as expressly provided under this License. Any attempt 192 | otherwise to copy, modify, sublicense or distribute the Program is 193 | void, and will automatically terminate your rights under this License. 194 | However, parties who have received copies, or rights, from you under 195 | this License will not have their licenses terminated so long as such 196 | parties remain in full compliance. 197 | 198 | 5. You are not required to accept this License, since you have not 199 | signed it. However, nothing else grants you permission to modify or 200 | distribute the Program or its derivative works. These actions are 201 | prohibited by law if you do not accept this License. Therefore, by 202 | modifying or distributing the Program (or any work based on the 203 | Program), you indicate your acceptance of this License to do so, and 204 | all its terms and conditions for copying, distributing or modifying 205 | the Program or works based on it. 206 | 207 | 6. Each time you redistribute the Program (or any work based on the 208 | Program), the recipient automatically receives a license from the 209 | original licensor to copy, distribute or modify the Program subject to 210 | these terms and conditions. You may not impose any further 211 | restrictions on the recipients' exercise of the rights granted herein. 212 | You are not responsible for enforcing compliance by third parties to 213 | this License. 214 | 215 | 7. If, as a consequence of a court judgment or allegation of patent 216 | infringement or for any other reason (not limited to patent issues), 217 | conditions are imposed on you (whether by court order, agreement or 218 | otherwise) that contradict the conditions of this License, they do not 219 | excuse you from the conditions of this License. If you cannot 220 | distribute so as to satisfy simultaneously your obligations under this 221 | License and any other pertinent obligations, then as a consequence you 222 | may not distribute the Program at all. For example, if a patent 223 | license would not permit royalty-free redistribution of the Program by 224 | all those who receive copies directly or indirectly through you, then 225 | the only way you could satisfy both it and this License would be to 226 | refrain entirely from distribution of the Program. 227 | 228 | If any portion of this section is held invalid or unenforceable under 229 | any particular circumstance, the balance of the section is intended to 230 | apply and the section as a whole is intended to apply in other 231 | circumstances. 232 | 233 | It is not the purpose of this section to induce you to infringe any 234 | patents or other property right claims or to contest validity of any 235 | such claims; this section has the sole purpose of protecting the 236 | integrity of the free software distribution system, which is 237 | implemented by public license practices. Many people have made 238 | generous contributions to the wide range of software distributed 239 | through that system in reliance on consistent application of that 240 | system; it is up to the author/donor to decide if he or she is willing 241 | to distribute software through any other system and a licensee cannot 242 | impose that choice. 243 | 244 | This section is intended to make thoroughly clear what is believed to 245 | be a consequence of the rest of this License. 246 | 247 | 8. If the distribution and/or use of the Program is restricted in 248 | certain countries either by patents or by copyrighted interfaces, the 249 | original copyright holder who places the Program under this License 250 | may add an explicit geographical distribution limitation excluding 251 | those countries, so that distribution is permitted only in or among 252 | countries not thus excluded. In such case, this License incorporates 253 | the limitation as if written in the body of this License. 254 | 255 | 9. The Free Software Foundation may publish revised and/or new versions 256 | of the General Public License from time to time. Such new versions will 257 | be similar in spirit to the present version, but may differ in detail to 258 | address new problems or concerns. 259 | 260 | Each version is given a distinguishing version number. If the Program 261 | specifies a version number of this License which applies to it and "any 262 | later version", you have the option of following the terms and conditions 263 | either of that version or of any later version published by the Free 264 | Software Foundation. If the Program does not specify a version number of 265 | this License, you may choose any version ever published by the Free Software 266 | Foundation. 267 | 268 | 10. If you wish to incorporate parts of the Program into other free 269 | programs whose distribution conditions are different, write to the author 270 | to ask for permission. For software which is copyrighted by the Free 271 | Software Foundation, write to the Free Software Foundation; we sometimes 272 | make exceptions for this. Our decision will be guided by the two goals 273 | of preserving the free status of all derivatives of our free software and 274 | of promoting the sharing and reuse of software generally. 275 | 276 | NO WARRANTY 277 | 278 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 279 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 280 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 281 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 282 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 283 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 284 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 285 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 286 | REPAIR OR CORRECTION. 287 | 288 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 289 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 290 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 291 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 292 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 293 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 294 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 295 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 296 | POSSIBILITY OF SUCH DAMAGES. 297 | 298 | END OF TERMS AND CONDITIONS 299 | 300 | How to Apply These Terms to Your New Programs 301 | 302 | If you develop a new program, and you want it to be of the greatest 303 | possible use to the public, the best way to achieve this is to make it 304 | free software which everyone can redistribute and change under these terms. 305 | 306 | To do so, attach the following notices to the program. It is safest 307 | to attach them to the start of each source file to most effectively 308 | convey the exclusion of warranty; and each file should have at least 309 | the "copyright" line and a pointer to where the full notice is found. 310 | 311 | 312 | Copyright (C) 313 | 314 | This program is free software; you can redistribute it and/or modify 315 | it under the terms of the GNU General Public License as published by 316 | the Free Software Foundation; either version 2 of the License, or 317 | (at your option) any later version. 318 | 319 | This program is distributed in the hope that it will be useful, 320 | but WITHOUT ANY WARRANTY; without even the implied warranty of 321 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 322 | GNU General Public License for more details. 323 | 324 | You should have received a copy of the GNU General Public License along 325 | with this program; if not, write to the Free Software Foundation, Inc., 326 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 327 | 328 | Also add information on how to contact you by electronic and paper mail. 329 | 330 | If the program is interactive, make it output a short notice like this 331 | when it starts in an interactive mode: 332 | 333 | Gnomovision version 69, Copyright (C) year name of author 334 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 335 | This is free software, and you are welcome to redistribute it 336 | under certain conditions; type `show c' for details. 337 | 338 | The hypothetical commands `show w' and `show c' should show the appropriate 339 | parts of the General Public License. Of course, the commands you use may 340 | be called something other than `show w' and `show c'; they could even be 341 | mouse-clicks or menu items--whatever suits your program. 342 | 343 | You should also get your employer (if you work as a programmer) or your 344 | school, if any, to sign a "copyright disclaimer" for the program, if 345 | necessary. Here is a sample; alter the names: 346 | 347 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 348 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 349 | 350 | , 1 April 1989 351 | Ty Coon, President of Vice 352 | 353 | This General Public License does not permit incorporating your program into 354 | proprietary programs. If your program is a subroutine library, you may 355 | consider it more useful to permit linking proprietary applications with the 356 | library. If this is what you want to do, use the GNU Lesser General 357 | Public License instead of this License. 358 | --------------------------------------------------------------------------------