diff --git a/src/commands/approve_job.yml b/src/commands/approve_job.yml new file mode 100644 index 0000000..0acc607 --- /dev/null +++ b/src/commands/approve_job.yml @@ -0,0 +1,107 @@ +description: | + This command approves a hold job(s) by name. + This requires a Circle CI token be set as $t + +parameters: + job-name: + description: A list of comma separated hold jobs to approve + type: string + run-on-branch: + description: | + The branches to actually wait on. By default this waits on all branches. If set to anything but + '*' the wait will run only on the specified branch + type: string + default: "*" + kill-gracefully: + description: If true it completes and lets future steps continue + type: string + default: "true" + when: + description: Allows the user to set when conditions + type: enum + enum: [ + "always", + "on_success", + "on_fail" + ] + default: "on_success" + circleci-api-key: + default: CIRCLE_TOKEN + description: >- + In case you use a different Environment Variable Name than + CIRCLE_TOKEN, supply it here. + type: env_var_name + +steps: + - run: + name: Swissknife - Approve On Hold Job(s) by Name + when: << parameters.when >> + command: | + if [ -z "$BASH" ]; then + echo Bash not installed. + exit 1 + fi + hash jq 2>/dev/null || { echo >&2 "jq is not installed. Aborting."; exit 1; } + if [[ "$<< parameters.circleci-api-key >>" == "" ]]; then + echo "<< parameters.circleci-api-key >> not set. Set a token to access the circle API in the env var << parameters.circleci-api-key >>"; + exit 1; + fi + + if [[ "<< parameters.run-on-branch >>" != "*" && "<< parameters.run-on-branch >>" != "$CIRCLE_BRANCH" ]]; then + echo "Chosen to run only on << parameters.run-on-branch >> and currently we are on $CIRCLE_BRANCH, exiting"; + exit 0; + fi + + mkdir -p /tmp/swissknife + + + get_job_id_and_type() { + local NAME_OF_JOB="$1" + local ID="" + + curl --header "Circle-Token: $<< parameters.circleci-api-key >>" -f -s https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}/job > /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json + ID=$(jq -r ".items[] | select(.name==\"${NAME_OF_JOB}\") | .id" /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) + TYPE=$(jq -r ".items[] | select(.name==\"${NAME_OF_JOB}\") | .type" /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) + echo "${ID} ${TYPE}" + } + + + job_list="<< parameters.job-name >>" + + for job_name in ${job_list//,/ } + do + # Reset job id and job type + job_id="" + job_type="" + + echo "Checking type of ${job_name}...." + + read job_id job_type < <(get_job_id_and_type ${job_name}) + if [[ "$job_type" == "approval" ]]; then + echo "${job_name} is an approval job!" + echo "Moving onto approval...." + status_code=$(curl --write-out %{http_code} --silent --output /dev/null --request POST "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}/approve/${job_id}" --header "Circle-Token: ${<< parameters.circleci-api-key >>}") + + if [[ "$status_code" -eq 202 ]] ; then + echo "${job_name} has been approved!" + else + echo "Something went wrong! Status Code: ${status_code}" + if [[ "<< parameters.kill-gracefully >>" == "true" ]]; then + echo "Proceeding with future steps"; + break + else + echo "Failing job by exiting forcefully"; + exit 1; + fi + fi + else + echo "${job_name} is NOT approval job!" + if [[ "<< parameters.kill-gracefully >>" == "true" ]]; then + echo "Proceeding with future steps"; + break + else + echo "Failing job by exiting forcefully"; + exit 1; + fi + fi + done diff --git a/src/commands/wait_for_job.yml b/src/commands/wait_for_job.yml index 3612488..437448a 100644 --- a/src/commands/wait_for_job.yml +++ b/src/commands/wait_for_job.yml @@ -1,12 +1,12 @@ description: | - This command waits for another job in this workflow. Since right now Circle CI doesnt let + This command waits for a job(s) in this workflow. Since right now Circle CI doesn't let you queue up jobs irrespective of whether they fail or not. This is a faux queue where the command stalls till the other job succeeds. This requires a Circle CI token be set as $CIRCLE_TOKEN parameters: job-name: - description: The job on which to wait. If job not found continue immediately + description: The job or jobs on which to wait. If job not found continue immediately. For multiple jobs the list must be comma separated. type: string max-wait-time: description: | @@ -27,6 +27,12 @@ parameters: '*' the wait will run only on the specified branch type: string default: "*" + circleci-api-key: + default: CIRCLE_TOKEN + description: >- + In case you use a different Environment Variable Name than + CIRCLE_TOKEN, supply it here. + type: env_var_name steps: - run: @@ -37,8 +43,8 @@ steps: exit 1 fi hash jq 2>/dev/null || { echo >&2 "jq is not installed. Aborting."; exit 1; } - if [[ "$CIRCLE_TOKEN" == "" ]]; then - echo "CIRCLE_TOKEN not set. Set a token to access the circle API in the env var CIRCLE_TOKEN"; + if [[ "$<< parameters.circleci-api-key >>" == "" ]]; then + echo "<< parameters.circleci-api-key >> not set. Set a token to access the circle API in the env var << parameters.circleci-api-key >>"; exit 1; fi @@ -47,42 +53,51 @@ steps: exit 0; fi - api_endpoint="api/v2/workflow/${CIRCLE_WORKFLOW_ID}/job" - mkdir -p /tmp/swissknife - # This is a global variable used to get return value for get_job_status - job_status="" - job_number="" get_job_status() { - wf_url="https://circleci.com/$api_endpoint?circle-token=${CIRCLE_TOKEN}" - curl -f -s $wf_url > /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json - job_status=$(jq -r '.items[] | select(.name=="<< parameters.job-name >>") | .status' /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) - job_number=$(jq -r '.items[] | select(.name=="<< parameters.job-name >>") | .job_number' /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) + local NAME_OF_JOB="$1" + local STATUS="" + local NUMBER="" + + curl --header "Circle-Token: $<< parameters.circleci-api-key >>" -f -s https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}/job > /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json + STATUS=$(jq -r ".items[] | select(.name==\"${NAME_OF_JOB}\") | .status" /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) + NUMBER=$(jq -r ".items[] | select(.name==\"${NAME_OF_JOB}\") | .job_number" /tmp/swissknife/wf_$CIRCLE_WORKFLOW_ID.json) + echo "${STATUS} ${NUMBER}" } - current_wait_time=0 - while true; do - get_job_status - if [[ "$job_status" == "success" || "$job_status" == "failed" || "$job_status" == "canceled" || "$job_status" == "" ]]; then - echo "Its finally my turn. exiting" - exit 0 - else - echo "Looks like the other guy ($job_number) is still not done ($job_status)." - echo "Going to sleep for << parameters.sleep-time-between-checks >>" - sleep << parameters.sleep-time-between-checks >> - current_wait_time=$(( current_wait_time + << parameters.sleep-time-between-checks >> )) - fi + job_list="<< parameters.job-name >>" + + for job_name in ${job_list//,/ } + do + # Reset job status, job number and wait time + job_status="" + job_number="" + current_wait_time=0 - if (( $current_wait_time > << parameters.max-wait-time >> )); then - if [[ "<< parameters.kill-gracefully >>" == "true" ]]; then - echo "Proceeding with future steps"; - exit 0; + echo "Starting to check status of ${job_name}" + while true; do + read job_status job_number < <(get_job_status ${job_name}) + if [[ "$job_status" == "success" || "$job_status" == "failed" || "$job_status" == "canceled" || "$job_status" == "" ]]; then + echo "Job, ${job_name}, has completed!" + break else - echo "Failing job by exiting forcefully"; - exit 1; + echo "Looks like the other guy ($job_number) is still not done ($job_status)." + echo "Going to sleep for << parameters.sleep-time-between-checks >>" + sleep << parameters.sleep-time-between-checks >> + current_wait_time=$(( current_wait_time + << parameters.sleep-time-between-checks >> )) + fi + + if (( $current_wait_time > << parameters.max-wait-time >> )); then + if [[ "<< parameters.kill-gracefully >>" == "true" ]]; then + echo "Proceeding with future steps"; + break + else + echo "Failing job by exiting forcefully"; + exit 1; + fi fi - fi + done done