diff --git a/.circleci/test-deploy.yml b/.circleci/test-deploy.yml index f4c88e9..e30fc12 100644 --- a/.circleci/test-deploy.yml +++ b/.circleci/test-deploy.yml @@ -7,25 +7,51 @@ filters: &filters tags: only: /.*/ +jobs: + sleep: + docker: + - image: cimg/base:edge + parameters: + time: + type: integer + steps: + - run: echo "sleeping << parameters.time >>s" && sleep << parameters.time >> + workflows: - test-deploy: + test-pipeline-a: jobs: - - orb-tools/pack: - filters: *filters - - workflow-queue/queue: + - workflow-queue/pipeline-queue: + context: orb-publishing + debug: true + test-pipeline-b: + jobs: + - sleep: + time: 5 + test-pipeline-c: + jobs: + - sleep: + time: 20 + + test-global-queue: + jobs: + - workflow-queue/global-queue: + context: orb-publishing + debug: true + ignored-workflows: "test-pipeline-a" # given we are in one pipeline, don't block on ourselves filters: tags: ignore: /.*/ - context: orb-publishing - requires: [orb-tools/pack] + test-deploy: + jobs: + - orb-tools/pack: + filters: *filters - orb-tools/publish: + context: orb-publishing orb-name: promiseofcake/workflow-queue vcs-type: << pipeline.project.type >> pub-type: production requires: - orb-tools/pack - - workflow-queue/queue - context: orb-publishing filters: branches: ignore: /.*/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e2f59c --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test-global-queue: + ./src/scripts/test.sh global-queue.sh + +test-pipeline-queue: + ./src/scripts/test.sh pipeline-queue.sh diff --git a/README.md b/README.md index f10888f..8533da0 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ ## Introduction -Forked from and updated to reduce the use-cases, and migrate to the CircleCI V2 API +Originally forked from and updated to reduce the use-cases, and migrate to the CircleCI V2 API -The purpose of this Orb is to add a concept of a queue to specific branch workflow tasks in CircleCi. The main use-case is to isolate a set of changes to ensure that one set of a thing is running at one time. Think of smoke-tests against a nonproduction environment as a promotion gate. +The purpose of this Orb is to add a concept of a queue to specific branch's workflow tasks in CircleCi. The main use-case is to isolate a set of changes to ensure that one set of a thing is running at one time. Think of smoke-tests against a nonproduction environment as a promotion gate. + +Additional use-cases are for queueing workflows within a given pipeline (a feature missing today from CircleCi). ## Configuration Requirements diff --git a/src/README.md b/src/README.md deleted file mode 100644 index b8e1015..0000000 --- a/src/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Orb Source - -Orbs are shipped as individual `orb.yml` files, however, to make development easier, it is possible to author an orb in _unpacked_ form, which can be _packed_ with the CircleCI CLI and published. - -The default `.circleci/config.yml` file contains the configuration code needed to automatically pack, test, and deploy any changes made to the contents of the orb source in this directory. - -## @orb.yml - -This is the entry point for our orb "tree", which becomes our `orb.yml` file later. - -Within the `@orb.yml` we generally specify 4 configuration keys - -**Keys** - -1. **version** - Specify version 2.1 for orb-compatible configuration `version: 2.1` -2. **description** - Give your orb a description. Shown within the CLI and orb registry -3. **display** - Specify the `home_url` referencing documentation or product URL, and `source_url` linking to the orb's source repository. -4. **orbs** - (optional) Some orbs may depend on other orbs. Import them here. - -## See: - - [Orb Author Intro](https://circleci.com/docs/2.0/orb-author-intro/#section=configuration) - - [Reusable Configuration](https://circleci.com/docs/2.0/reusing-config) diff --git a/src/commands/block_execution.yml b/src/commands/block_execution.yml deleted file mode 100755 index a7b55a7..0000000 --- a/src/commands/block_execution.yml +++ /dev/null @@ -1,43 +0,0 @@ -description: > - This command is executed and blocks further execution until the necessary criteria is met. - -parameters: - debug: - type: boolean - default: false - description: "If enabled, DEBUG messages will be logged." - time: - type: string - default: "10" - description: "Minutes to wait before giving up." - dont-quit: - type: boolean - default: false - description: "Force job through once time expires instead of failing." - only-on-branch: - type: string - default: "*" - description: "Only queue on specified branch" - confidence: - type: string - default: "1" - description: > - Due to concurrency issues, how many times should we requery the pipeline list to ensure previous jobs are "pending", - but not yet active. This number indicates the threshold for API returning no previous pending pipelines. - Default is one confirmation, increase if you see issues. - include-on-hold: - type: boolean - default: false - description: Consider on-hold workflows waiting for approval as running and include them in the queue. - -steps: - - run: - name: Block execution until workflow is at the front of the line - environment: - CONFIG_DEBUG_ENABLED: "<< parameters.debug >>" - CONFIG_TIME: "<< parameters.time >>" - CONFIG_DONT_QUIT: "<< parameters.dont-quit >>" - CONFIG_ONLY_ON_BRANCH: "<< parameters.only-on-branch >>" - CONFIG_CONFIDENCE: "<< parameters.confidence >>" - CONFIG_INCLUDE_ON_HOLD: " << parameters.include-on-hold >>" - command: <> diff --git a/src/commands/global_block.yml b/src/commands/global_block.yml new file mode 100755 index 0000000..291a008 --- /dev/null +++ b/src/commands/global_block.yml @@ -0,0 +1,48 @@ +description: > + When used, this command blocks the assigned workflow from running until all previous (global) workflows have completed. + This ensures only one instance of a given workflow is running at a time across all defined branches. +parameters: + debug: + type: boolean + default: false + description: "When enabled, additional debug logging with be output." + time: + type: string + default: "10" + description: "Number of minutes to wait for a lock before giving up." + dont-quit: + type: boolean + default: false + description: "If true, forces the job through once time expires instead of failing." + only-on-branch: + type: string + default: "*" + description: "Only queue on specified branch" + confidence: + type: string + default: "1" + description: > + Due to concurrency issues, the number of times should we requery the pipeline list to ensure previous jobs are "pending", + but not yet active. This number indicates the threshold for API returning no previous pending pipelines. + Default is `1` confirmation, increase if you see issues. + ignored-workflows: + type: string + default: "" + description: Comma separated list of workflow names to ignore as blocking workflows for the global queue. + include-on-hold: + type: boolean + default: false + description: Consider `on-hold` workflows waiting for approval as running and include them in the queue. + +steps: + - run: + name: Block execution until the current workflow is at the front of the line + environment: + CONFIG_DEBUG_ENABLED: "<< parameters.debug >>" + CONFIG_TIME: "<< parameters.time >>" + CONFIG_DONT_QUIT: "<< parameters.dont-quit >>" + CONFIG_ONLY_ON_BRANCH: "<< parameters.only-on-branch >>" + CONFIG_CONFIDENCE: "<< parameters.confidence >>" + CONFIG_IGNORED_WORKFLOWS: "<< parameters.ignored-workflows >>" + CONFIG_INCLUDE_ON_HOLD: " << parameters.include-on-hold >>" + command: <> diff --git a/src/commands/pipeline_block.yml b/src/commands/pipeline_block.yml new file mode 100644 index 0000000..517f56f --- /dev/null +++ b/src/commands/pipeline_block.yml @@ -0,0 +1,23 @@ +description: > + This command blocks execution of a workflow within the context of a given pipeline. + +parameters: + debug: + type: boolean + default: false + description: "When enabled, additional debug logging with be output." + confidence: + type: string + default: "1" + description: > + Due to concurrency issues, the number of times should we requery the pipeline list to ensure previous jobs are "pending", + but not yet active. This number indicates the threshold for API returning no previous pending pipelines. + Default is `1` confirmation, increase if you see issues. + +steps: + - run: + name: Blocking execution until the current workflow is the last to run in the given pipeline. + environment: + CONFIG_DEBUG_ENABLED: "<< parameters.debug >>" + CONFIG_CONFIDENCE: "<< parameters.confidence >>" + command: <> diff --git a/src/examples/example.yml b/src/examples/example.yml index 3f7a2c3..d911b38 100755 --- a/src/examples/example.yml +++ b/src/examples/example.yml @@ -3,7 +3,7 @@ description: > usage: version: 2.1 orbs: - workflow-queue: promiseofcake/workflow-queue@1 + workflow-queue: promiseofcake/workflow-queue@2 workflows: example: jobs: diff --git a/src/jobs/global-queue.yml b/src/jobs/global-queue.yml new file mode 100755 index 0000000..3040dd1 --- /dev/null +++ b/src/jobs/global-queue.yml @@ -0,0 +1,49 @@ +description: > + This job ensures only a single defined global workflow is running at a given point in time. + +docker: + - image: cimg/base:stable +resource_class: small + +parameters: + debug: + type: boolean + default: false + description: "When enabled, additional debug logging with be output." + time: + type: string + default: "10" + description: "Number of minutes to wait for a lock before giving up." + dont-quit: + type: boolean + default: false + description: "If true, forces the job through once time expires instead of failing." + only-on-branch: + type: string + default: "*" + description: "Only queue on specified branch" + confidence: + type: string + default: "1" + description: > + Due to concurrency issues, the number of times should we requery the pipeline list to ensure previous jobs are "pending", + but not yet active. This number indicates the threshold for API returning no previous pending pipelines. + Default is `1` confirmation, increase if you see issues. + ignored-workflows: + type: string + default: "" + description: Comma separated list of workflow names to ignore as blocking workflows for the global queue. + include-on-hold: + type: boolean + default: false + description: Consider `on-hold` workflows waiting for approval as running and include them in the queue. + +steps: + - global_block: + debug: << parameters.debug >> + time: << parameters.time >> + dont-quit: << parameters.dont-quit >> + only-on-branch: << parameters.only-on-branch >> + confidence: << parameters.confidence >> + ignored-workflows: << parameters.ignored-workflows >> + include-on-hold: << parameters.include-on-hold >> diff --git a/src/jobs/pipeline-queue.yml b/src/jobs/pipeline-queue.yml new file mode 100644 index 0000000..b2b64b5 --- /dev/null +++ b/src/jobs/pipeline-queue.yml @@ -0,0 +1,24 @@ +description: > + This job prevents a workflow within a given pipeline from running until all previous workflows have completed. + +docker: + - image: cimg/base:stable +resource_class: small + +parameters: + debug: + type: boolean + default: false + description: "If enabled, DEBUG messages will be logged." + confidence: + type: string + default: "1" + description: > + Due to concurrency issues, how many times should we requery the pipeline list to ensure previous jobs are "pending", + but not yet active. This number indicates the threshold for API returning no previous pending pipelines. + Default is one confirmation, increase if you see issues. + +steps: + - pipeline_block: + debug: <> + confidence: <> diff --git a/src/jobs/queue.yml b/src/jobs/queue.yml deleted file mode 100755 index cc346f7..0000000 --- a/src/jobs/queue.yml +++ /dev/null @@ -1,44 +0,0 @@ -description: > - This job is executed and blocks further execution until the necessary criteria is met. - -docker: - - image: cimg/base:stable -resource_class: small - -parameters: - debug: - type: boolean - default: false - description: "If enabled, DEBUG messages will be logged." - time: - type: string - default: "10" - description: "Minutes to wait before giving up." - dont-quit: - type: boolean - default: false - description: "Force job through once time expires instead of failing." - only-on-branch: - type: string - default: "*" - description: "Only queue on specified branch" - confidence: - type: string - default: "1" - description: > - Due to concurrency issues, how many times should we requery the pipeline list to ensure previous jobs are "pending", - but not yet active. This number indicates the threshold for API returning no previous pending pipelines. - Default is one confirmation, increase if you see issues. - include-on-hold: - type: boolean - default: false - description: Consider on-hold workflows waiting for approval as running and include them in the queue. - -steps: - - block_execution: - debug: << parameters.debug >> - time: <> - dont-quit: <> - only-on-branch: <> - confidence: <> - include-on-hold: <> diff --git a/src/scripts/queue.sh b/src/scripts/global-queue.sh similarity index 78% rename from src/scripts/queue.sh rename to src/scripts/global-queue.sh index d1bb409..769f9c1 100644 --- a/src/scripts/queue.sh +++ b/src/scripts/global-queue.sh @@ -1,7 +1,6 @@ #!/bin/bash - -tmp="/tmp" -pipeline_file="${tmp}/pipeline_status.json" +tmp=${TMP_DIR:-/tmp} +pipelines_file="${tmp}/pipeline_status.json" workflows_file="${tmp}/workflow_status.json" # logger command for debugging @@ -13,18 +12,18 @@ debug() { # ensure we have the required variables present to execute load_variables(){ - # just confirm our required variables are present : "${CIRCLE_WORKFLOW_ID:?"Required Env Variable not found!"}" : "${CIRCLE_PROJECT_USERNAME:?"Required Env Variable not found!"}" : "${CIRCLE_PROJECT_REPONAME:?"Required Env Variable not found!"}" : "${CIRCLE_REPOSITORY_URL:?"Required Env Variable not found!"}" : "${CIRCLE_JOB:?"Required Env Variable not found!"}" - # Only needed for private projects + + # required for private projects if [ -z "${CIRCLECI_API_TOKEN}" ]; then echo "CIRCLECI_API_TOKEN not set. Private projects will be inaccessible." else - fetch "https://circleci.com/api/v2/me" "/tmp/me.cci" - me=$(jq -e '.id' /tmp/me.cci) + fetch "https://circleci.com/api/v2/me" "${tmp}/me.cci" + me=$(jq -e '.id' "${tmp}/me.cci") echo "Using API key for user: ${me}" fi } @@ -34,15 +33,15 @@ fetch(){ url=$1 target=$2 method=${3:-GET} - debug "Performing API ${method} Call to ${url} to ${target}" + debug "api call: ${method} ${url} > ${target}" http_response=$(curl -s -X "${method}" -H "Circle-Token: ${CIRCLECI_API_TOKEN}" -H "Content-Type: application/json" -o "${target}" -w "%{http_code}" "${url}") if [ "${http_response}" != "200" ]; then - echo "ERROR: Server returned error code: ${http_response}" + echo "ERROR: api-call: server returned error code: ${http_response}" debug "${target}" exit 1 else - debug "API Success" + debug "api call: success" fi } @@ -52,32 +51,45 @@ fetch_pipelines(){ echo "Only blocking execution if running previous workflows on branch: ${CIRCLE_BRANCH}" pipelines_api_url_template="https://circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pipeline?branch=${CIRCLE_BRANCH}" - debug "Fetching piplines: ${pipelines_api_url_template} > ${pipeline_file}" - fetch "${pipelines_api_url_template}" "${pipeline_file}" + debug "Fetching piplines for: ${CIRCLE_BRANCH}" + fetch "${pipelines_api_url_template}" "${pipelines_file}" } -# fetch all running or created workflows for a given pipeline +# iterate over all pipelines, and fetch workflow information fetch_pipeline_workflows(){ - for pipeline in $(jq -r ".items[] | .id //empty" ${pipeline_file} | uniq) + for pipeline in $(jq -r ".items[] | .id //empty" "${pipelines_file}" | uniq) do - debug "Fetching workflow information for pipeline: ${pipeline}" + debug "Fetching workflow metadata for pipeline: ${pipeline}" pipeline_detail=${tmp}/pipeline-${pipeline}.json fetch "https://circleci.com/api/v2/pipeline/${pipeline}/workflow" "${pipeline_detail}" created_at=$(jq -r '.items[] | .created_at' "${pipeline_detail}") - debug "Pipeline's workflow was created at: ${created_at}" + debug "Pipeline:'s workflow was created at: ${created_at}" done + + # filter out any workflows that are not active if [ "${CONFIG_INCLUDE_ON_HOLD}" = "1" ]; then active_statuses="$(printf '%s' '["running","created","on_hold"]')" else active_statuses="$(printf '%s' '["running","created"]')" fi - jq -s "[.[].items[] | select([.status] | inside(${active_statuses}))]" ${tmp}/pipeline-*.json > ${workflows_file} + + debug "filtering on statuses: ${active_statuses}" + + # filter out any workflows that match the ignored list + ignored_workflows="[]" + if [ -n "${CONFIG_IGNORED_WORKFLOWS}" ]; then + ignored_workflows=$(printf '"%s"' "${CONFIG_IGNORED_WORKFLOWS}" | jq 'split(",")') + fi + + debug "ignoring workflows: ${ignored_workflows}" + + jq -s "[.[].items[] | select(([.name] | inside(${ignored_workflows}) | not) and ([.status] | inside(${active_statuses})))]" "${tmp}"/pipeline-*.json > "${workflows_file}" } # parse workflows to fetch parmeters about this current running workflow load_current_workflow_values(){ - my_commit_time=$(jq ".[] | select (.id == \"${CIRCLE_WORKFLOW_ID}\").created_at" ${workflows_file}) - my_workflow_id=$(jq ".[] | select (.id == \"${CIRCLE_WORKFLOW_ID}\").id" ${workflows_file}) + my_commit_time=$(jq ".[] | select (.id == \"${CIRCLE_WORKFLOW_ID}\").created_at" "${workflows_file}") + my_workflow_id=$(jq ".[] | select (.id == \"${CIRCLE_WORKFLOW_ID}\").id" "${workflows_file}") } # load all the data necessary to compare build executions @@ -89,8 +101,8 @@ update_comparables(){ load_current_workflow_values echo "This job will block until no previous workflows have *any* workflows running." - oldest_running_workflow_id=$(jq '. | sort_by(.created_at) | .[0].id' ${workflows_file}) - oldest_commit_time=$(jq '. | sort_by(.created_at) | .[0].created_at' ${workflows_file}) + oldest_running_workflow_id=$(jq '. | sort_by(.created_at) | .[0].id' "${workflows_file}") + oldest_commit_time=$(jq '. | sort_by(.created_at) | .[0].created_at' "${workflows_file}") if [ -z "${oldest_commit_time}" ] || [ -z "${oldest_running_workflow_id}" ]; then echo "ERROR: API Error - unable to load previous workflow timings. File a bug" exit 1 diff --git a/src/scripts/pipeline-queue.sh b/src/scripts/pipeline-queue.sh new file mode 100644 index 0000000..6c3834e --- /dev/null +++ b/src/scripts/pipeline-queue.sh @@ -0,0 +1,94 @@ +#!/bin/bash +tmp=${TMP_DIR:-/tmp} +workflows_file="${tmp}/workflow_status.json" + +# logger command for debugging +debug() { + if [ "${CONFIG_DEBUG_ENABLED}" == "1" ]; then + echo "DEBUG: ${*}" + fi +} + +# ensure we have the required variables present to execute +load_variables(){ + # just confirm our required variables are present + : "${CIRCLE_WORKFLOW_ID:?"Required Env Variable not found!"}" + : "${CIRCLE_PIPELINE_ID:?"Required Env Variable not found!"}" + : "${CIRCLE_PROJECT_USERNAME:?"Required Env Variable not found!"}" + : "${CIRCLE_PROJECT_REPONAME:?"Required Env Variable not found!"}" + # Only needed for private projects + if [ -z "${CIRCLECI_API_TOKEN}" ]; then + echo "CIRCLECI_API_TOKEN not set. Private projects will be inaccessible." + else + fetch "https://circleci.com/api/v2/me" "${tmp}/me.cci" + me=$(jq -e '.id' "${tmp}/me.cci") + echo "Using API key for user: ${me}" + fi +} + +# helper function to perform HTTP requests via curl +fetch(){ + url=$1 + target=$2 + method=${3:-GET} + debug "Performing API ${method} Call to ${url} to ${target}" + + http_response=$(curl -s -X "${method}" -H "Circle-Token: ${CIRCLECI_API_TOKEN}" -H "Content-Type: application/json" -o "${target}" -w "%{http_code}" "${url}") + if [ "${http_response}" != "200" ]; then + echo "ERROR: Server returned error code: ${http_response}" + debug "${target}" + exit 1 + else + debug "API Success" + fi +} + +# fetch all workflows within the current pipeline +fetch_pipeline_workflows(){ + debug "Fetching workflow information for pipeline: ${CIRCLE_PIPELINE_ID}" + pipeline_detail=${tmp}/pipeline-${CIRCLE_PIPELINE_ID}.json + fetch "https://circleci.com/api/v2/pipeline/${CIRCLE_PIPELINE_ID}/workflow" "${pipeline_detail}" + debug "Pipeline's details: $(jq -r '.' "${pipeline_detail}")" + # fetch all workflows that are not this workflow + jq -s "[.[].items[] | select((.id != \"${CIRCLE_WORKFLOW_ID}\") and ((.status == \"running\") or (.status == \"created\")))]" "${pipeline_detail}" > "${workflows_file}" +} + +# load all the data necessary to compare build executions +update_comparables(){ + fetch_pipeline_workflows + + running_workflows=$(jq length "${workflows_file}") + debug "Running workflows: ${running_workflows}" +} + +load_variables +echo "This build will block until all previous builds complete." +wait_time=0 +loop_time=11 + +# queue loop +confidence=0 +while true; do + update_comparables + + # if we have no running workflows, check confidence, and move to front of line. + if [[ "${running_workflows}" -eq 0 ]] ; then + if [ $confidence -lt "${CONFIG_CONFIDENCE}" ]; then + # To grow confidence, we check again with a delay. + confidence=$((confidence+1)) + echo "API shows no running pipeline workflows, but it is possible a previous workflow has pending jobs not yet visible in API." + echo "Rerunning check ${confidence}/${CONFIG_CONFIDENCE}" + else + echo "Front of the line, WooHoo!, Build continuing" + break + fi + else + # If we fail, reset confidence + confidence=0 + echo "This workflow (${CIRCLE_WORKFLOW_ID}) is queued, waiting for ${running_workflows} pipeline workflows to complete." + echo "Total Queue time: ${wait_time} seconds." + fi + + sleep $loop_time + wait_time=$(( loop_time + wait_time )) +done diff --git a/src/scripts/test.sh b/src/scripts/test.sh new file mode 100755 index 0000000..e910af4 --- /dev/null +++ b/src/scripts/test.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# shellcheck disable=all +# This script is used to test the queuing scripts in the src/scripts directory +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +TMP_DIR=`mktemp -d` + +# default cci job config +CONFIG_DEBUG_ENABLED=1 +CONFIG_TIME=10 +CONFIG_DONT_QUIT=1 +CONFIG_ONLY_ON_BRANCH=* +CONFIG_CONFIDENCE=1 + +# local test values +CIRCLE_PIPELINE_ID=4e1ffd3b-c260-43db-a66c-8766eaf7fc88 +CIRCLE_WORKFLOW_ID=476fa7ff-7534-440f-9a65-a2779170d344 +CIRCLE_PROJECT_USERNAME=promiseofcake +CIRCLE_PROJECT_REPONAME=circleci-workflow-queue +CIRCLECI_API_TOKEN=${CIRCLECI_USER_TOKEN} + +source ${SCRIPT_DIR}/$@ diff --git a/test/workflow-fixture.json b/test/workflow-fixture.json new file mode 100644 index 0000000..b588320 --- /dev/null +++ b/test/workflow-fixture.json @@ -0,0 +1,72 @@ +{ + "next_page_token": null, + "items": [ + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "5d707409-4828-47bd-9a59-0e349f164d02", + "name": "test-deploy", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "status": "success", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:11:30Z", + "stopped_at": "2024-04-16T00:11:40Z" + }, + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "4967e8f6-a4ad-4b32-87e4-445f2d6d912f", + "name": "test-global-queue", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "status": "success", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:11:30Z", + "stopped_at": "2024-04-16T00:12:11Z" + }, + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "9258267c-bfaa-4e6a-bbb2-31ac5bce5bd4", + "name": "test-pipeline-c", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "status": "success", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:11:30Z", + "stopped_at": "2024-04-16T00:12:05Z" + }, + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "633d6ddf-8ed3-45c5-a376-eba934bf8533", + "name": "test-pipeline-b", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "status": "success", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:11:29Z", + "stopped_at": "2024-04-16T00:11:49Z" + }, + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "b332b406-2860-4354-be89-2b363301fdd4", + "name": "test-pipeline-a", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "status": "failed", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:11:29Z", + "stopped_at": "2024-04-16T00:12:39Z" + }, + { + "pipeline_id": "c77bf329-c78f-4591-a71f-8541759fb55e", + "id": "502d6e3e-8ede-4d8f-b553-87005bfc5271", + "name": "lint-pack", + "project_slug": "gh/promiseofcake/circleci-workflow-queue", + "tag": "setup", + "status": "success", + "started_by": "df121a07-9607-43e1-8d85-b100e1a6c0a2", + "pipeline_number": 169, + "created_at": "2024-04-16T00:10:55Z", + "stopped_at": "2024-04-16T00:11:30Z" + } + ] +} diff --git a/test_workflow.json b/test_workflow.json deleted file mode 100644 index ddfd282..0000000 --- a/test_workflow.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "next_page_token": null, - "items": [ - { - "pipeline_id": "49287a8d-969e-4bba-850a-051f18078b2c", - "id": "574f4ff2-eadb-48ca-a082-8241c000d5bc", - "name": "workflow", - "project_slug": "gh/boxfortinc/level99-nimbus", - "status": "on_hold", - "started_by": "f471266e-c8b8-4237-8564-36e5537ad72d", - "pipeline_number": 1719, - "created_at": "2023-10-17T17:38:27Z", - "stopped_at": null - } - ] -}