Skip to content

Commit 6caaff4

Browse files
committed
export timing data to circleci
1 parent c17f024 commit 6caaff4

File tree

4 files changed

+240
-3
lines changed

4 files changed

+240
-3
lines changed

.circleci/config.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,14 @@ jobs:
8080
name: Import Tests using BATS
8181
command: |
8282
export BATS_IMPORT_DEV_ORB="eddiewebb/<<pipeline.parameters.orbname>>@dev:${PR_NUMBER}"
83-
export BATS_TEST_LIST=$(bats -l test | circleci tests split | tr '\n' ' ')
83+
export BATS_TEST_LIST=$(bats -l test | circleci tests split --split-by=timings | tr '\n' ' ')
8484
echo "This node will run ${BATS_TEST_LIST}"
85-
bats test
85+
mkdir -p reports
86+
bats test | tee reports/test-report
87+
bash src/parse-taps.bash reports/test-report > reports/bats_junit_report.xml
8688
- pr-comment
89+
- store_test_results:
90+
path: reports
8791

8892
publish:
8993
docker:

src/parse-taps.bash

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
3+
head='<?xml version="1.0"?>\n<testsuite>'
4+
foot='</testsuite>'
5+
6+
test='<testcase name="%s" time="%d" />'
7+
8+
parse_it(){
9+
$line=$1
10+
NAME=$(expr "$line" : '.*ok [0-9]* \(.*\) #time.*')
11+
TIME=$(expr "$line" : '.*ok [0-9]*.*\#time=\(.*\)')
12+
printf "${test}" "${NAME}" ${TIME}
13+
}
14+
15+
16+
printf "$head"
17+
18+
while read -r line;do
19+
case $line in
20+
1..*) continue ;;
21+
ok*) parse_it $line;;
22+
\#*) continue;;
23+
*) echo "unkon line" ;;
24+
esac
25+
done < "$1"
26+
27+
printf "\n$foot"

src/queue.bash

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
2+
#
3+
#.This query builds and determine our place in queue
4+
#
5+
main_loop(){
6+
load_variables
7+
max_time=<< parameters.time >>
8+
echo "This build will block until all previous builds complete."
9+
echo "Max Queue Time: ${max_time} minutes."
10+
wait_time=0
11+
loop_time=10
12+
max_time_seconds=$((max_time * 60))
13+
14+
15+
load_current_job_details #gives us our pipeline ID & Order
16+
17+
18+
19+
#get recent pipeline for this project (optionally filtering on branch)
20+
# for each pipeline, is ID lower than ours?
21+
# if lower, get all workflows
22+
# for each workflow, are they running?
23+
# if job-specific running, do they contain my job?
24+
# v2 api sucks.
25+
26+
27+
28+
29+
#
30+
# Queue Loop
31+
#
32+
confidence=0
33+
while true; do
34+
update_comparables
35+
echo "This Workflow Timestamp: $my_commit_time"
36+
echo "Oldest Workflow Timestamp: $oldest_commit_time"
37+
if [[ "$oldest_commit_time" > "$my_commit_time" ]] || [[ "$oldest_commit_time" = "$my_commit_time" ]] ; then
38+
# API returns Y-M-D HH:MM (with 24 hour clock) so alphabetical string compare is accurate to timestamp compare as well
39+
# recent-jobs API does not include pending, so it is posisble we queried in between a workfow transition, and we;re NOT really front of line.
40+
if [ $confidence -lt <<parameters.confidence>> ];then
41+
# To grow confidence, we check again with a delay.
42+
confidence=$((confidence+1))
43+
else
44+
echo "Front of the line, WooHoo!, Build continuing"
45+
break
46+
fi
47+
else
48+
echo "This build (${CIRCLE_BUILD_NUM}) is queued, waiting for build number (${oldest_running_build_num}) to complete."
49+
echo "Total Queue time: ${wait_time} seconds."
50+
fi
51+
52+
if [ $wait_time -ge $max_time_seconds ]; then
53+
echo "Max wait time exceeded, considering response."
54+
if [ "<<parameters.dont-quit>>" == "true" ];then
55+
echo "Orb parameter dont-quit is set to true, letting this job proceed!"
56+
exit 0
57+
else
58+
cancel_current_build
59+
sleep 10 # wait for API to cancel this job, rather than showing as failure
60+
exit 1 # but just in case, fail job
61+
fi
62+
fi
63+
64+
sleep $loop_time
65+
wait_time=$(( loop_time + wait_time ))
66+
done
67+
68+
}
69+
70+
71+
72+
load_variables(){
73+
# just confirm our required variables are present
74+
: ${CIRCLE_BUILD_NUM:?"Required Env Variable not found!"}
75+
: ${CIRCLE_PROJECT_USERNAME:?"Required Env Variable not found!"}
76+
: ${CIRCLE_PROJECT_REPONAME:?"Required Env Variable not found!"}
77+
: ${CIRCLE_REPOSITORY_URL:?"Required Env Variable not found!"}
78+
: ${CIRCLE_JOB:?"Required Env Variable not found!"}
79+
: ${CIRCLE_WORKFLOW_ID:?"Required Env Variable not found!"}
80+
# Only needed for private projects
81+
if [ -z "$CIRCLECI_API_KEY" ]; then
82+
echo "ERROR: CIRCLECI_API_KEY not set. API will be inaccessible." >&2
83+
exit 1
84+
fi
85+
VCS_TYPE="<<parameters.vcs-type>>"
86+
}
87+
88+
load_current_pipeline_info(){
89+
#get_api_payload "https://circleci.com/api/v2/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/job/${CIRCLE_BUILD_NUM}" /tmp/current_workflow.json
90+
get_api_payload "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}" /tmp/current_workflow.json
91+
CIRCLE_PIPELINE_ID=$(jq '.pipeline_id' /tmp/current_workflow.json) #UUID
92+
CIRCLE_PIPELINE_NUMBER=$(jq '.pipeline_number' /tmp/current_workflow.json) #RelativeOrder
93+
}
94+
95+
96+
get_api_payload(){
97+
url=$1
98+
target=$2
99+
curl -X GET "${url}" -H "Accept: application/json" -H "Circle-Token: ${CIRCLECI_API_KEY}" > ${target}
100+
if [ $? -ne 0 ];then
101+
echo "ERROR: Curl command to ${url} failed. Response below."
102+
cat $target
103+
exit 1
104+
fi
105+
}
106+
107+
108+
fetch_filtered_active_builds(){
109+
if [ "<<parameters.consider-branch>>" != "true" ];then
110+
echo "Orb parameter 'consider-branch' is false, will block previous builds on any branch."
111+
jobs_api_url_template="https://circleci.com/api/v1.1/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}?circle-token=${CIRCLECI_API_KEY}&filter=running"
112+
else
113+
echo "Only blocking execution if running previous jobs on branch: ${CIRCLE_BRANCH}"
114+
: ${CIRCLE_BRANCH:?"Required Env Variable not found!"}
115+
jobs_api_url_template="https://circleci.com/api/v1.1/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/tree/${CIRCLE_BRANCH}?circle-token=${CIRCLECI_API_KEY}&filter=running"
116+
fi
117+
118+
if [ ! -z $TESTING_MOCK_RESPONSE ] && [ -f $TESTING_MOCK_RESPONSE ];then
119+
echo "Using test mock response"
120+
cat $TESTING_MOCK_RESPONSE > /tmp/jobstatus.json
121+
else
122+
echo "Attempting to access CircleCI api. If the build process fails after this step, ensure your CIRCLECI_API_KEY is set."
123+
curl -f -s $jobs_api_url_template > /tmp/jobstatus.json
124+
echo "API access successful"
125+
fi
126+
}
127+
128+
fetch_active_workflows(){
129+
cp /tmp/jobstatus.json /tmp/augmented_jobstatus.json
130+
for workflow in `jq -r ".[] | .workflows.workflow_id" /tmp/augmented_jobstatus.json | uniq`
131+
do
132+
echo "Checking time of workflow: ${workflow}"
133+
workflow_file=/tmp/workflow-${workflow}.json
134+
if [ ! -z $TESTING_MOCK_WORKFLOW_RESPONSES ] && [ -f $TESTING_MOCK_WORKFLOW_RESPONSES/${workflow}.json ]; then
135+
echo "Using test mock workflow response"
136+
cat $TESTING_MOCK_WORKFLOW_RESPONSES/${workflow}.json > ${workflow_file}
137+
else
138+
curl -f -s "https://circleci.com/api/v2/workflow/${workflow}?circle-token=${CIRCLECI_API_KEY}" > ${workflow_file}
139+
fi
140+
created_at=`jq -r '.created_at' ${workflow_file}`
141+
echo "Workflow was created at: ${created_at}"
142+
cat /tmp/augmented_jobstatus.json | jq --arg created_at "${created_at}" --arg workflow "${workflow}" '(.[] | select(.workflows.workflow_id == $workflow) | .workflows) |= . + {created_at:$created_at}' > /tmp/augmented_jobstatus-${workflow}.json
143+
#DEBUG echo "new augmented_jobstatus:"
144+
#DEBUG cat /tmp/augmented_jobstatus-${workflow}.json
145+
mv /tmp/augmented_jobstatus-${workflow}.json /tmp/augmented_jobstatus.json
146+
done
147+
}
148+
149+
update_comparables(){
150+
fetch_filtered_active_builds
151+
152+
fetch_active_workflows
153+
154+
load_current_workflow_values
155+
156+
# falsey parameters are empty strings, so always compare against 'true'
157+
if [ "<<parameters.consider-job>>" != "true" ] || [ "<<parameters.block-workflow>>" = "true" ] ;then
158+
echo "Orb parameter block-workflow is true."
159+
echo "This job will block until no previous workflows have *any* jobs running."
160+
oldest_running_build_num=`jq 'sort_by(.workflows.created_at)| .[0].build_num' /tmp/augmented_jobstatus.json`
161+
oldest_commit_time=`jq 'sort_by(.workflows.created_at)| .[0].workflows.created_at' /tmp/augmented_jobstatus.json`
162+
else
163+
echo "Orb parameter block-workflow is false."
164+
echo "Only blocking execution if running previous jobs matching this job: ${CIRCLE_JOB}"
165+
oldest_running_build_num=`jq ". | map(select(.build_parameters.CIRCLE_JOB==\"${CIRCLE_JOB}\")) | sort_by(.workflows.created_at)| .[0].build_num" /tmp/augmented_jobstatus.json`
166+
oldest_commit_time=`jq ". | map(select(.build_parameters.CIRCLE_JOB==\"${CIRCLE_JOB}\")) | sort_by(.workflows.created_at)| .[0].workflows.created_at" /tmp/augmented_jobstatus.json`
167+
fi
168+
echo "Oldest job: $oldest_running_build_num"
169+
if [ -z $oldest_commit_time ];then
170+
echo "API Call for existing jobs failed, failing this build. Please check API token"
171+
echo "All running jobs:"
172+
cat /tmp/jobstatus.json || exit 0
173+
echo "All running jobs with created_at:"
174+
cat /tmp/augmented_jobstatus.json || exit 0
175+
echo "All worfklow details."
176+
cat /tmp/workflow-*.json
177+
exit 1
178+
fi
179+
}
180+
181+
load_current_workflow_values(){
182+
my_commit_time=`jq '.[] | select( .build_num == '"${CIRCLE_BUILD_NUM}"').workflows.created_at' /tmp/augmented_jobstatus.json`
183+
}
184+
185+
cancel_current_build(){
186+
echo "Cancelleing build ${CIRCLE_BUILD_NUM}"
187+
cancel_api_url_template="https://circleci.com/api/v1.1/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/cancel?circle-token=${CIRCLECI_API_KEY}"
188+
curl -s -X POST $cancel_api_url_template > /dev/null
189+
}
190+
191+
192+
193+
#
194+
# We can skip a few use cases without calling API
195+
#
196+
if [ ! -z "$CIRCLE_PR_REPONAME" ]; then
197+
echo "Queueing on forks is not supported. Skipping queue..."
198+
# It's important that we not fail here because it could cause issues on the main repo's branch
199+
exit 0
200+
fi
201+
if [ "<<parameters.only-on-branch>>" = "*" ] || [ "<<parameters.only-on-branch>>" = "${CIRCLE_BRANCH}" ]; then
202+
echo "${CIRCLE_BRANCH} queueable"
203+
else
204+
echo "Queueing only happens on <<parameters.only-on-branch>> branch, skipping queue"
205+
exit 0
206+
fi
207+

test/test_expansion.bats

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ function setup {
3232
# when
3333
assert_jq_match '.jobs | length' 1 #only 1 job
3434
assert_jq_match '.jobs["Single File"].steps | length' 1 #only 1 steps
35-
3635
}
3736

3837

0 commit comments

Comments
 (0)