├── .gitignore ├── Dockerfile ├── LICENSE ├── NOTICE.md ├── README.md ├── assets ├── askpass.sh ├── check ├── common.sh └── in ├── ci └── tests.yml ├── scripts └── test └── test ├── all.sh ├── check.sh ├── get.sh ├── git-in-stub └── helpers.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM concourse/git-resource 2 | 3 | ENV LC_ALL C 4 | RUN apk add --update coreutils 5 | RUN mv /opt/resource /opt/git-resource 6 | 7 | ADD assets/ /opt/resource/ 8 | RUN chmod +x /opt/resource/* 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | Copyright 2016 Alex Suraci & Chris Brown 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Archiving; follow the [Concourse v10 roadmap](https://github.com/concourse/concourse/blob/d1dd60f7555f6e7f7d22b0e55289c24534c34b45/README.md#the-road-to-concourse-v10) which will eliminate the need for this pretty hacky resource type. 2 | 3 | # Git Branch `HEAD`s Resource 4 | 5 | Tracks changes made to all branches (or branches matching a filter). This will 6 | skip commits on individual branches, but ensure a version is emitted for each 7 | change to the branches. 8 | 9 | **This resource is meant to be used with [`version: 10 | every`](https://concourse.ci/get-step.html#get-version).** 11 | 12 | ## Installation 13 | 14 | Add the following `resource_types` entry to your pipeline: 15 | 16 | ```yaml 17 | --- 18 | resource_types: 19 | - name: git-branch-heads 20 | type: docker-image 21 | source: {repository: vito/git-branch-heads-resource} 22 | ``` 23 | 24 | ## Source Configuration 25 | 26 | All source configuration is based on the [Git 27 | resource](https://github.com/concourse/git-resource), with the addition of the 28 | following property: 29 | 30 | * `branches`: *Optional.* An array of branch name filters. If not specified, 31 | all branches are tracked. 32 | * `exclude`: *Optional* A Regex for branches to be excluded. If not specified, 33 | no branches are excluded. 34 | 35 | The `branch` configuration from the original resource is ignored for `check`. 36 | 37 | 38 | ### Example 39 | 40 | Resource configuration for a repo with a bunch of branches named `wip-*`: 41 | 42 | ``` yaml 43 | resources: 44 | - name: my-repo-with-feature-branches 45 | type: git-branch-heads 46 | source: 47 | uri: https://github.com/concourse/atc 48 | branches: [wip-*] 49 | ``` 50 | Resource configuration for a repo with `version` and branches beginning with `feature/` filtered out: 51 | 52 | ``` yaml 53 | resources: 54 | - name: my-repo-with-feature-branches 55 | type: git-branch-heads 56 | source: 57 | uri: https://github.com/concourse/atc 58 | exclude: version|feature/.* 59 | ``` 60 | 61 | ## Behavior 62 | 63 | 64 | ### `check`: Check for changes to all branches. 65 | 66 | The repository is cloned (or pulled if already present), all remote branches 67 | are enumerated, and compared to the existing set of branches. 68 | 69 | If any branches are new or removed, a new version is emitted including the 70 | delta. 71 | 72 | ### `in`: Fetch the commit that changed the branch. 73 | 74 | This resource delegates entirely to the `in` of the original Git resource, by 75 | specifying `source.branch` as the branch that changed, and `version.ref` as the 76 | commit on the branch. 77 | 78 | All `params` and `source` configuration of the original resource will be 79 | respected. 80 | 81 | 82 | ### `out`: No-op. 83 | 84 | *Not implemented.* 85 | -------------------------------------------------------------------------------- /assets/askpass.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Private keys with passphrases are not supported." >&2 3 | exit 1 4 | -------------------------------------------------------------------------------- /assets/check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: set ft=sh 3 | 4 | set -e 5 | 6 | exec 3>&1 # make stdout available as fd 3 for the result 7 | exec 1>&2 # redirect all output to stderr for logging 8 | 9 | source $(dirname $0)/common.sh 10 | 11 | payload=$TMPDIR/git-branch-heads-resource-request 12 | 13 | cat > $payload <&0 14 | 15 | load_pubkey $payload 16 | configure_git_ssl_verification $payload 17 | configure_credentials $payload 18 | 19 | uri=$(jq -r '.source.uri // ""' < $payload) 20 | exclude_branches=$(jq -r '.source.exclude // ""' < $payload) 21 | branch_filter=$(jq -r '.source.branches // [] | join(" ")' < $payload) 22 | git_config_payload=$(jq -r '.source.git_config // []' < $payload) 23 | 24 | previous_branches="$(jq -r '.version.branches // ""' < $payload)" 25 | 26 | configure_git_global "${git_config_payload}" 27 | 28 | e_point=! 29 | current_heads=$(git ls-remote -h "$uri" $branch_filter | sed 's/refs\/heads\///' | awk '{print $2, $1}' | awk "\$1 $e_point~ \"^($exclude_branches)$\"" | sort -V) 30 | 31 | current_heads_map=$( 32 | jq -n ' 33 | $heads | rtrimstr("\n") | split("\n") | 34 | map(split(" ") | {key: .[0], value: .[1]}) | 35 | from_entries 36 | ' --arg heads "$current_heads" 37 | ) 38 | 39 | echo "$current_heads" | 40 | while read branch ref; do 41 | if [ -z "$branch" ]; then 42 | continue 43 | fi 44 | 45 | prev_ref=$(jq -r '.version | .[$branch]' --arg branch "$branch" < $payload) 46 | if [ "$ref" != "$prev_ref" ]; then 47 | echo "$branch" 48 | fi 49 | done | 50 | jq -R . | 51 | jq -s 'map({changed: .} + $branches)' \ 52 | --argjson branches "$current_heads_map" >&3 53 | -------------------------------------------------------------------------------- /assets/common.sh: -------------------------------------------------------------------------------- 1 | export TMPDIR=${TMPDIR:-/tmp} 2 | 3 | load_pubkey() { 4 | local private_key_path=$TMPDIR/git-resource-private-key 5 | 6 | (jq -r '.source.private_key // empty' < $1) > $private_key_path 7 | 8 | if [ -s $private_key_path ]; then 9 | chmod 0600 $private_key_path 10 | 11 | eval $(ssh-agent) >/dev/null 2>&1 12 | trap "kill $SSH_AGENT_PID" 0 13 | 14 | SSH_ASKPASS=/opt/resource/askpass.sh DISPLAY= ssh-add $private_key_path >/dev/null 15 | 16 | mkdir -p ~/.ssh 17 | cat > ~/.ssh/config < $HOME/.netrc 45 | fi 46 | } 47 | -------------------------------------------------------------------------------- /assets/in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: set ft=sh 3 | 4 | set -e 5 | 6 | payload=$TMPDIR/git-branch-heads-resource-request 7 | 8 | cat > $payload <&0 9 | 10 | jq \ 11 | '. * { 12 | source: { 13 | branch: .version.changed 14 | } 15 | } + { 16 | version: { 17 | ref: .version | .[.changed] 18 | } 19 | }' < $payload | 20 | ${GIT_RESOURCE_IN:-/opt/git-resource/in} "$@" | 21 | jq --slurpfile payload $payload \ 22 | '. + {version: ($payload | .[0].version)}' 23 | 24 | cd "$@" 25 | mkdir .git/git-branch-heads-resource 26 | jq < $payload -r .version.changed > .git/git-branch-heads-resource/branch 27 | -------------------------------------------------------------------------------- /ci/tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | platform: linux 3 | 4 | image_resource: 5 | type: docker-image 6 | source: {repository: concourse/git-branches-resource} 7 | 8 | inputs: 9 | - name: git-branches-resource 10 | 11 | run: 12 | path: git-branches-resource/test/all.sh 13 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | not_installed() { 6 | ! command -v $1 > /dev/null 2>&1 7 | } 8 | 9 | git_resource_dir=$(cd $(dirname $0)/.. && pwd) 10 | 11 | if not_installed docker; then 12 | echo "# docker is not installed! run the following commands:" 13 | echo " brew install docker" 14 | echo " brew cask install docker-machine" 15 | echo " docker-machine create dev --driver virtualbox" 16 | echo ' eval $(docker-machine env dev)' 17 | echo " docker login" 18 | exit 1 19 | fi 20 | 21 | cd $git_resource_dir 22 | docker build --rm . 23 | -------------------------------------------------------------------------------- /test/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export TMPDIR_ROOT=$(mktemp -d /tmp/git-tests.XXXXXX) 6 | 7 | $(dirname $0)/check.sh 8 | $(dirname $0)/get.sh 9 | 10 | echo -e '\e[32mall tests passed!\e[0m' 11 | 12 | rm -rf $TMPDIR_ROOT 13 | -------------------------------------------------------------------------------- /test/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source $(dirname $0)/helpers.sh 6 | 7 | it_can_check_from_no_version() { 8 | local repo=$(init_repo) 9 | 10 | local refmaster1=$(git -C $repo rev-parse master) 11 | 12 | # NB: these actually end up being the same ref most of the time, since commit 13 | # shas are computed based on the patch message, and if it's the same code 14 | # change, message, parent sha, and timestamp, the sha will be the same 15 | local refa1=$(make_commit_to_branch $repo branch-a) 16 | local refb1=$(make_commit_to_branch $repo branch-b) 17 | 18 | check_uri $repo | jq -e ' 19 | . == [{ 20 | changed: "branch-a", 21 | "branch-a": $refa1, 22 | "branch-b": $refb1, 23 | "master": $refmaster1 24 | },{ 25 | changed: "branch-b", 26 | "branch-a": $refa1, 27 | "branch-b": $refb1, 28 | "master": $refmaster1 29 | },{ 30 | changed: "master", 31 | "branch-a": $refa1, 32 | "branch-b": $refb1, 33 | "master": $refmaster1 34 | }] 35 | ' --arg refa1 "$refa1" --arg refb1 "$refb1" --arg refmaster1 "$refmaster1" 36 | } 37 | 38 | it_can_check_from_no_version_with_filter() { 39 | set -e 40 | 41 | local repo=$(init_repo) 42 | 43 | local refmaster1=$(git -C $repo rev-parse master) 44 | 45 | # NB: these actually end up being the same ref most of the time, since commit 46 | # shas are computed based on the patch message, and if it's the same code 47 | # change, message, parent sha, and timestamp, the sha will be the same 48 | local refa1=$(make_commit_to_branch $repo matched-a) 49 | local refb1=$(make_commit_to_branch $repo matched-b) 50 | 51 | make_commit_to_branch $repo not-matched-a 52 | 53 | make_commit_to_branch $repo ignored-a 54 | make_commit_to_branch $repo ignored-b 55 | 56 | check_uri_branches $repo 'matched-*' | jq -e ' 57 | . == [{ 58 | changed: "matched-a", 59 | "matched-a": $refa1, 60 | "matched-b": $refb1 61 | },{ 62 | changed: "matched-b", 63 | "matched-a": $refa1, 64 | "matched-b": $refb1 65 | }] 66 | ' --arg refa1 "$refa1" --arg refb1 "$refb1" 67 | } 68 | 69 | it_can_check_from_no_version_with_filters() { 70 | set -e 71 | 72 | local repo=$(init_repo) 73 | 74 | local refmaster1=$(git -C $repo rev-parse master) 75 | 76 | # NB: these actually end up being the same ref most of the time, since commit 77 | # shas are computed based on the patch message, and if it's the same code 78 | # change, message, parent sha, and timestamp, the sha will be the same 79 | local refa1=$(make_commit_to_branch $repo matched-a) 80 | local refb1=$(make_commit_to_branch $repo matched-b) 81 | 82 | local refc1=$(make_commit_to_branch $repo also-matched-c) 83 | 84 | make_commit_to_branch $repo ignored-a 85 | make_commit_to_branch $repo ignored-b 86 | 87 | check_uri_branches $repo 'matched-*' 'also-*' | jq -e ' 88 | . == [{ 89 | changed: "also-matched-c", 90 | "matched-a": $refa1, 91 | "matched-b": $refb1, 92 | "also-matched-c": $refc1 93 | },{ 94 | changed: "matched-a", 95 | "matched-a": $refa1, 96 | "matched-b": $refb1, 97 | "also-matched-c": $refc1 98 | },{ 99 | changed: "matched-b", 100 | "matched-a": $refa1, 101 | "matched-b": $refb1, 102 | "also-matched-c": $refc1 103 | }] 104 | ' --arg refa1 "$refa1" --arg refb1 "$refb1" --arg refc1 "$refc1" 105 | } 106 | 107 | it_can_check_with_updated_branch() { 108 | local repo=$(init_repo) 109 | 110 | local refmaster1=$(git -C $repo rev-parse master) 111 | 112 | local refa1=$(make_commit_to_branch $repo branch-a) 113 | local refb1=$(make_commit_to_branch $repo branch-b) 114 | 115 | local refa2=$(make_commit_to_branch $repo branch-a) 116 | 117 | check_uri_from $repo "branch-a=$refa1" "branch-b=$refb1" "master=$refmaster1" | jq -e ' 118 | . == [{ 119 | changed: "branch-a", 120 | "branch-a": $refa2, 121 | "branch-b": $refb1, 122 | "master": $refmaster1 123 | }] 124 | ' --arg refa2 "$refa2" --arg refb1 "$refb1" --arg refmaster1 "$refmaster1" 125 | } 126 | 127 | it_can_check_with_updated_branches() { 128 | local repo=$(init_repo) 129 | 130 | local refmaster1=$(git -C $repo rev-parse master) 131 | 132 | local refa1=$(make_commit_to_branch $repo branch-a) 133 | local refb1=$(make_commit_to_branch $repo branch-b) 134 | 135 | local refa2=$(make_commit_to_branch $repo branch-a) 136 | local refb2=$(make_commit_to_branch $repo branch-b) 137 | 138 | check_uri_from $repo "branch-a=$refa1" "branch-b=$refb1" "master=$refmaster1" | jq -e ' 139 | . == [{ 140 | changed: "branch-a", 141 | "branch-a": $refa2, 142 | "branch-b": $refb2, 143 | "master": $refmaster1 144 | }, { 145 | changed: "branch-b", 146 | "branch-a": $refa2, 147 | "branch-b": $refb2, 148 | "master": $refmaster1 149 | }] 150 | ' --arg refa2 "$refa2" --arg refb2 "$refb2" --arg refmaster1 "$refmaster1" 151 | } 152 | 153 | it_can_check_with_added_branch() { 154 | local repo=$(init_repo) 155 | 156 | local refmaster1=$(git -C $repo rev-parse master) 157 | 158 | local refa1=$(make_commit_to_branch $repo branch-a) 159 | local refb1=$(make_commit_to_branch $repo branch-b) 160 | 161 | local refc1=$(make_commit_to_branch $repo branch-c) 162 | 163 | check_uri_from $repo "branch-a=$refa1" "branch-b=$refb1" "master=$refmaster1" | jq -e ' 164 | . == [{ 165 | changed: "branch-c", 166 | "branch-a": $refa1, 167 | "branch-b": $refb1, 168 | "branch-c": $refc1, 169 | "master": $refmaster1 170 | }] 171 | ' --arg refa1 "$refa1" --arg refb1 "$refb1" --arg refc1 "$refc1" --arg refmaster1 "$refmaster1" 172 | } 173 | 174 | it_can_check_with_removed_branch() { 175 | local repo=$(init_repo) 176 | 177 | local refmaster1=$(git -C $repo rev-parse master) 178 | 179 | local refa1=$(make_commit_to_branch $repo branch-a) 180 | 181 | check_uri_from $repo "branch-a=$refa1" "branch-b=bogus-ref" "master=$refmaster1" | jq -e ' 182 | . == [] 183 | ' --arg refa1 "$refa1" --arg refmaster1 "$refmaster1" 184 | } 185 | 186 | run it_can_check_from_no_version 187 | run it_can_check_from_no_version_with_filter 188 | run it_can_check_from_no_version_with_filters 189 | run it_can_check_with_updated_branch 190 | run it_can_check_with_updated_branches 191 | run it_can_check_with_added_branch 192 | run it_can_check_with_removed_branch 193 | -------------------------------------------------------------------------------- /test/get.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source $(dirname $0)/helpers.sh 6 | 7 | it_can_get_version() { 8 | local dest=$TMPDIR/destination 9 | 10 | mkdir $dest 11 | 12 | local repo=$(init_repo) 13 | 14 | local refmaster1=$(git -C $repo rev-parse master) 15 | 16 | # NB: these actually end up being the same ref most of the time, since commit 17 | # shas are computed based on the patch message, and if it's the same code 18 | # change, message, parent sha, and timestamp, the sha will be the same 19 | local refa1=$(make_commit_to_branch $repo branch-a) 20 | local refb1=$(make_commit_to_branch $repo branch-b) 21 | 22 | get_changed_branch $repo $dest "branch-a" "branch-a=$refa1" "branch-b=$refb1" "master=$refmaster1" | jq -e ' 23 | . == { 24 | destination: $dest, 25 | request: { 26 | source: { 27 | uri: $repo, 28 | branch: $branch, 29 | }, 30 | version: { 31 | ref: $ref, 32 | } 33 | }, 34 | version: { 35 | "changed": $branch, 36 | "branch-a": $refa1, 37 | "branch-b": $refb1, 38 | "master": $refmaster1 39 | } 40 | } 41 | ' --arg dest "$dest" \ 42 | --arg repo "$repo" \ 43 | --arg branch "branch-a" \ 44 | --arg ref "$refa1" \ 45 | --arg refa1 "$refa1" \ 46 | --arg refb1 "$refb1" \ 47 | --arg refmaster1 "$refmaster1" 48 | } 49 | 50 | run it_can_get_version 51 | -------------------------------------------------------------------------------- /test/git-in-stub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq '{ 4 | destination: $dest, 5 | request: ., 6 | version: {ref: "some-ref"} 7 | }' --arg dest "$1" 8 | -------------------------------------------------------------------------------- /test/helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u 4 | 5 | set -o pipefail 6 | 7 | export TMPDIR_ROOT=$(mktemp -d /tmp/git-tests.XXXXXX) 8 | trap "rm -rf $TMPDIR_ROOT" EXIT 9 | 10 | if [ -d /opt/resource ]; then 11 | resource_dir=/opt/resource 12 | else 13 | resource_dir=$(cd $(dirname $0)/../assets && pwd) 14 | fi 15 | 16 | run() { 17 | export TMPDIR=$(mktemp -d ${TMPDIR_ROOT}/git-tests.XXXXXX) 18 | 19 | echo -e 'running \e[33m'"$@"$'\e[0m...' 20 | eval "$@" 2>&1 | sed -e 's/^/ /g' 21 | echo "" 22 | } 23 | 24 | init_repo() { 25 | ( 26 | set -e 27 | 28 | cd $(mktemp -d $TMPDIR/repo.XXXXXX) 29 | 30 | git init -q 31 | 32 | # start with an initial commit 33 | git \ 34 | -c user.name='test' \ 35 | -c user.email='test@example.com' \ 36 | commit -q --allow-empty -m "init" 37 | 38 | # back to master 39 | git checkout master 40 | 41 | # print resulting repo 42 | pwd 43 | ) 44 | } 45 | 46 | make_commit_to_file_on_branch() { 47 | local repo=$1 48 | local file=$2 49 | local branch=$3 50 | local msg=${4-} 51 | 52 | # ensure branch exists 53 | if ! git -C $repo rev-parse --verify $branch >/dev/null 2>&1; then 54 | git -C $repo branch $branch master 55 | fi 56 | 57 | # switch to branch 58 | git -C $repo checkout -q $branch 59 | 60 | # modify file and commit 61 | echo x >> $repo/$file 62 | git -C $repo add $file 63 | git -C $repo \ 64 | -c user.name='test' \ 65 | -c user.email='test@example.com' \ 66 | commit -q -m "commit $(wc -l $repo/$file) $msg" 67 | 68 | # output resulting sha 69 | git -C $repo rev-parse HEAD 70 | } 71 | 72 | make_commit_to_branch() { 73 | make_commit_to_file_on_branch $1 some-file $2 74 | } 75 | 76 | check_uri() { 77 | jq -n '{ 78 | source: { 79 | uri: $uri 80 | } 81 | }' --arg uri "$1" | ${resource_dir}/check | tee /dev/stderr 82 | } 83 | 84 | check_uri_branches() { 85 | jq -n '{ 86 | source: { 87 | uri: $uri, 88 | branches: ($branches | split(" ")) 89 | } 90 | }' --arg uri "$1" --arg branches "$*" | ${resource_dir}/check | tee /dev/stderr 91 | } 92 | 93 | check_uri_from() { 94 | uri=$1 95 | 96 | shift 97 | 98 | branches=$(echo "$@" | jq -R 'split(" ") | map(split("=") | {key: .[0], value: .[1]}) | from_entries') 99 | 100 | jq -n '{ 101 | source: { 102 | uri: $uri 103 | }, 104 | version: ({changed: "doesnt-matter"} + $branches) 105 | }' --arg uri "$uri" --argjson branches "$branches" | ${resource_dir}/check | tee /dev/stderr 106 | } 107 | 108 | get_changed_branch() { 109 | uri=$1 110 | dest=$2 111 | changed=$3 112 | 113 | shift 3 114 | 115 | branches=$(echo "$@" | jq -R 'split(" ") | map(split("=") | {key: .[0], value: .[1]}) | from_entries') 116 | 117 | jq -n '{ 118 | source: { 119 | uri: $uri 120 | }, 121 | version: ({changed: $changed} + $branches) 122 | }' --arg uri "$uri" --arg changed "$changed" --argjson branches "$branches" | 123 | env GIT_RESOURCE_IN=$(dirname $0)/git-in-stub \ 124 | ${resource_dir}/in "$dest" | 125 | tee /dev/stderr 126 | } 127 | --------------------------------------------------------------------------------