-
Notifications
You must be signed in to change notification settings - Fork 2
/
command
134 lines (114 loc) · 5.39 KB
/
command
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
128
129
130
131
132
133
134
#!/bin/bash
# For debugging
#set | grep BUILDKITE_PLUGIN_FORERUNNER
# Initialize the features we need
set -eou pipefail
shopt -s extglob
shopt -s globstar
# Do a little bit of parameter checking
TARGET="${BUILDKITE_PLUGIN_FORERUNNER_TARGET}"
TARGET_TYPE="${BUILDKITE_PLUGIN_FORERUNNER_TARGET_TYPE:-simple}"
PATH_PROCESSOR=""
# Check for valid `target`
if [[ ! -f ${TARGET} ]]; then
echo "ERROR: Invalid target value '${TARGET}'" >&2
buildkite-agent annotate --style "error" "Invalid target value '${TARGET}'"
exit 1
fi
# Check for valid `target_type`
case "${TARGET_TYPE}" in
simple | template | command)
;;
*)
echo "ERROR: Invalid target_type value '${TARGET_TYPE}'" >&2
buildkite-agent annotate --style "error" "Invalid target_type value '${TARGET_TYPE}'"
exit 1
;;
esac
# A simple target type doesn't deal with paths, so it's illegal to provide a `path_processor` with it
if [[ "${TARGET_TYPE}" == "simple" ]]; then
if [[ -v "BUILDKITE_PLUGIN_FORERUNNER_PATH_PROCESSOR" ]]; then
echo "ERROR: Cannot combine path_processor with a '${TARGET_TYPE}' target_type" >&2
buildkite-agent annotate --style "error" "Cannot combine path_processor with a '${TARGET_TYPE}' target_type"
exit 1
fi
else
# Otherwise, default to a `per-file` processor.
PATH_PROCESSOR="${BUILDKITE_PLUGIN_FORERUNNER_PATH_PROCESSOR:-per-file}"
# If the given path processor is a name like `per-file`, look it up in our default library
if [[ ! -f ${PATH_PROCESSOR} ]]; then
DEFAULT_PP_PATH="$(dirname $(dirname "${BASH_SOURCE[0]}"))/lib/path_processors"
if [[ -f "${DEFAULT_PP_PATH}/${PATH_PROCESSOR}" ]]; then
PATH_PROCESSOR="${DEFAULT_PP_PATH}/${PATH_PROCESSOR}"
else
echo "ERROR: Invalid path_processor value '${PATH_PROCESSOR}'" >&2
buildkite-agent annotate --style "error" "Invalid path_processor value '${PATH_PROCESSOR}'"
exit 1
fi
fi
fi
# If we're a pull request, we look at the diff between the current branch tip and the base branch
if [[ "${BUILDKITE_PULL_REQUEST}" != "false" ]]; then
if [[ ! -v "BUILDKITE_PLUGIN_FORERUNNER_GIT_FETCHED" ]]; then
# The `--force` flags are necessary to be able to deal with rewritten
# history on the remotes' end when reusing a repository
git fetch --force origin "${BUILDKITE_PULL_REQUEST_BASE_BRANCH}"
git fetch --force origin "refs/pull/${BUILDKITE_PULL_REQUEST}/head:refs/remotes/origin/pr/${BUILDKITE_PULL_REQUEST}"
# Ensure that if this plugin is run multiple times, we don't try to fetch every time
export BUILDKITE_PLUGIN_FORERUNNER_GIT_FETCHED=1
fi
TARGET_BRANCH="remotes/origin/${BUILDKITE_PULL_REQUEST_BASE_BRANCH}"
COMPARE_AGAINST="$(git merge-base ${TARGET_BRANCH} HEAD)"
else
# If we're not a pull request, just compare with `HEAD~1`.
COMPARE_AGAINST="$(git rev-parse HEAD~1)"
fi
# Get list of modified files (excluding deletes), store in `MODIFIED_FILES`
CURR_HEAD="$(git rev-parse HEAD)"
MODIFIED_FILES=( $(git diff-tree --diff-filter=d --name-only -r "${COMPARE_AGAINST}" HEAD) )
echo "Found ${#MODIFIED_FILES[@]} modified files between ${CURR_HEAD} and ${COMPARE_AGAINST}"
# For each pattern we've been given, check if any of the modified files matches
PATTERN_IDX=0
MATCHED_FILES=()
while [[ -v "BUILDKITE_PLUGIN_FORERUNNER_WATCH_${PATTERN_IDX}" ]]; do
# Fetch the pattern
PATTERN_VARNAME="BUILDKITE_PLUGIN_FORERUNNER_WATCH_${PATTERN_IDX}"
PATTERN="${!PATTERN_VARNAME}"
# Iterate over our changed files, checking to see if any match the pattern we're interested in
for FILE in "${MODIFIED_FILES[@]}"; do
if [[ "${FILE}" == @(${PATTERN}) ]]; then
echo "File '${FILE}' matched pattern '${PATTERN}'"
MATCHED_FILES+=( ${FILE} )
fi
done
PATTERN_IDX=$((${PATTERN_IDX} + 1))
done
# If a path processor has been given, pass the results to it to filter down some more
if [[ -n "${PATH_PROCESSOR}" ]]; then
# Overwrite `MATCHED_FILES` with the de-duplicated processed paths
echo "Invoking ${PATH_PROCESSOR} on ${#MATCHED_FILES[@]} files..."
MATCHED_FILES=( $(${PATH_PROCESSOR} "${MATCHED_FILES[@]}") )
fi
echo "+++ pipeline invocations"
# If we are a "simple" target, the only thing we are about is that `MATCHED_FILES` is non-empty
if [[ "${TARGET_TYPE}" == "simple" ]] && [[ ${#MATCHED_FILES[@]} -ne 0 ]]; then
echo "Triggering simple pipeline ${TARGET}"
buildkite-agent pipeline upload "${TARGET}"
exit 0
fi
# If we are a "template" target, we iterate over the matched files, using `sed` to template a .yml
if [[ "${TARGET_TYPE}" == "template" ]]; then
for PP_PATH in "${MATCHED_FILES[@]}"; do
# Provide the user a santized form of the path, for use in keys and whatnot
SANITIZED_PP_PATH=$(tr '/' '-' <<< "${PP_PATH}" | tr '.' '-' | tr ' ' '_')
echo "Triggering template pipeline ${TARGET} with {PATH}='${PP_PATH}'"
sed -e "s&{PATH}&${PP_PATH}&g" -e "s&{SANITIZED_PATH}&${SANITIZED_PP_PATH}&g" < "${TARGET}" | buildkite-agent pipeline upload
done
fi
# And the same for a "command" target, only we don't pipe to `buildkite-agent pipeline upload`, we'll let the command do that.
if [[ "${TARGET_TYPE}" == "command" ]]; then
for PP_PATH in "${MATCHED_FILES[@]}"; do
echo "Triggering command pipeline ${TARGET} '${PP_PATH}'"
${TARGET} "${PP_PATH}"
done
fi