diff --git a/.circleci/config.yml b/.circleci/config.yml index 8c9e922..849010f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,16 +16,17 @@ workflows: - orb-tools/pack: filters: *filters - orb-tools/review: + exclude: RC005,RC006,RC007 filters: *filters - shellcheck/check: filters: *filters - orb-tools/publish: - orb-name: / + orb-name: moneyforward/circleci-cancel-redundant vcs-type: << pipeline.project.type >> requires: [orb-tools/lint, orb-tools/review, orb-tools/pack, shellcheck/check] # Use a context to hold your publishing token. - context: + context: orb-publishing filters: *filters # Triggers the next workflow in the Orb Development Kit. - orb-tools/continue: diff --git a/.circleci/test-deploy.yml b/.circleci/test-deploy.yml index 13ff4be..68372a9 100644 --- a/.circleci/test-deploy.yml +++ b/.circleci/test-deploy.yml @@ -1,6 +1,6 @@ version: 2.1 orbs: - : /@dev:<> + circleci-cancel-redundant: moneyforward/circleci-cancel-redundant@dev:<> orb-tools: circleci/orb-tools@11.1 filters: &filters @@ -16,7 +16,17 @@ jobs: steps: - checkout # Run your orb's commands to validate them. - - /greet + - circleci-cancel-redundant/cancel: + circle_api_key: $CIRCLE_TOKEN + default_branch: main + pipeline_id: << pipeline.id >> + - run: + name: Sleep and echo + command: | + echo "Waking up..." + sleep 5 + echo "Hello world!" + workflows: test-deploy: jobs: @@ -26,13 +36,13 @@ workflows: - orb-tools/pack: filters: *filters - orb-tools/publish: - orb-name: / + orb-name: moneyforward/circleci-cancel-redundant vcs-type: << pipeline.project.type >> pub-type: production requires: - orb-tools/pack - command-tests - context: + context: orb-publishing filters: branches: ignore: /.*/ diff --git a/src/@orb.yml b/src/@orb.yml index f4d802c..f3a0af7 100755 --- a/src/@orb.yml +++ b/src/@orb.yml @@ -1,15 +1,7 @@ version: 2.1 -description: > - Sample orb description -# What will your orb allow users to accomplish? -# Descriptions should be short, simple, and informative. +description: Cancel redundant build for every workflow on default branch for CircleCI. -# This information will be displayed in the orb registry and is not mandatory. display: - home_url: "https://www.example.com/docs" - source_url: "https://github.com//" - -# If your orb requires other orbs, you can import them like this. Otherwise remove the "orbs" stanza. -# orbs: -# hello: circleci/hello-build@0.0.5 + home_url: https://corp.moneyforward.com/ + source_url: https://github.com/moneyforward/circleci-cancel-redundant diff --git a/src/commands/cancel.yml b/src/commands/cancel.yml new file mode 100755 index 0000000..e61732c --- /dev/null +++ b/src/commands/cancel.yml @@ -0,0 +1,19 @@ +description: > + This command cancels duplicate workflow on the default branch using the CircleCI API. + +parameters: + circle_api_key: + type: string + default_branch: + default: main + type: string + pipeline_id: + type: string +steps: + - run: + name: "Cancel duplicate workflow on default branch" + environment: + - CIRCLE_API_KEY: << parameters.circle_api_key >> + - DEFAULT_BRANCH: << parameters.default_branch >> + - PIPELINE_ID: << parameters.pipeline_id >> + command: <> diff --git a/src/commands/greet.yml b/src/commands/greet.yml deleted file mode 100755 index f4e2205..0000000 --- a/src/commands/greet.yml +++ /dev/null @@ -1,15 +0,0 @@ -description: > - This command echos "Hello World" using file inclusion. -# What will this command do? -# Descriptions should be short, simple, and clear. -parameters: - to: - type: string - default: "World" - description: "Hello to whom?" -steps: - - run: - environment: - PARAM_TO: <> - name: Hello Greeting - command: <> diff --git a/src/examples/example.yml b/src/examples/example.yml index a2cdaab..5fa026e 100755 --- a/src/examples/example.yml +++ b/src/examples/example.yml @@ -6,8 +6,13 @@ description: > usage: version: 2.1 orbs: - : /@1.2.3 + circleci-cancel-redundant: moneyforward/circleci-cancel-redundant@1.2.3 workflows: use-my-orb: jobs: - - / + - some-job: + pre-steps: + - circleci-cancel-redundant/cancel: + circle_api_key: "your-api-key" + default_branch: main + pipeline_id: << pipeline.id >> diff --git a/src/executors/default.yml b/src/executors/default.yml index ca3f5dc..b6f1507 100755 --- a/src/executors/default.yml +++ b/src/executors/default.yml @@ -1,12 +1,11 @@ description: > - This is a sample executor using Docker and Node. If you want to provide a custom environment in your orb, insert your image here. - If you do not require an executor, you can simply delete this directory. + CircleCI base executor. docker: - - image: 'cimg/node:<>' + - image: "cimg/base:<>" parameters: tag: - default: lts + default: stable description: > - Pick a specific cimg/node image variant: - https://hub.docker.com/r/cimg/node/tags + Pick a specific cimg/base image variant: + https://hub.docker.com/r/cimg/base/tags type: string diff --git a/src/jobs/hello.yml b/src/jobs/hello.yml deleted file mode 100755 index 8f1df04..0000000 --- a/src/jobs/hello.yml +++ /dev/null @@ -1,14 +0,0 @@ -description: > - Sample description -# What will this job do? - -executor: default - -parameters: - to: - type: string - default: "World" - description: "Hello to whom?" -steps: - - greet: - to: << parameters.to >> diff --git a/src/scripts/cancel.sh b/src/scripts/cancel.sh new file mode 100644 index 0000000..58ea3a1 --- /dev/null +++ b/src/scripts/cancel.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +if [ "$CIRCLE_BRANCH" != "$DEFAULT_BRANCH" ]; then + echo 'Not on default branch, no need to manually cancel duplicate workflow' + exit +fi + +CIRCLE_API_KEY=$(eval echo "$CIRCLE_API_KEY") + +# Get the name of the workflows in the pipeline +WF_NAMES=$(curl --header "Circle-Token: $CIRCLE_API_KEY" --request GET \ + "https://circleci.com/api/v2/pipeline/${PIPELINE_ID}/workflow" | jq -r '.items[].name') +echo "Workflow(s) in pipeline:" +echo "$WF_NAMES" + +## Get the IDs of pipelines created with the the current CIRCLE_SHA1 +PIPE_IDS=$(curl --header "Circle-Token: $CIRCLE_API_KEY" --request GET \ + "https://circleci.com/api/v2/project/gh/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pipeline?branch=$CIRCLE_BRANCH" \ + | jq -r --arg CIRCLE_SHA1 "${CIRCLE_SHA1}" \ + ' .items[]|select(.state == "created" and .vcs.revision == $CIRCLE_SHA1).id') + +## Get the IDs of currently running/on_hold workflows with the same name except the current workflow ID +if [ -n "$PIPE_IDS" ]; then + mapfile -t PIPE_LIST <<< "$PIPE_IDS" # Convert space separated values into array + for PIPE_ID in "${PIPE_LIST[@]:1}" # Skip the first PIPE_ID, since it's the latest one + do + result=$(curl --header "Circle-Token: $CIRCLE_API_KEY" --request GET "https://circleci.com/api/v2/pipeline/${PIPE_ID}/workflow") + # Cancel every workflow + for WF_NAME in $WF_NAMES + do + echo "$result" \ + | jq -r \ + --arg PIPELINE_ID "${PIPELINE_ID}" \ + --arg WF_NAME "${WF_NAME}" \ + '.items[]|select(.status == "on_hold" or .status == "running")|select(.name == $WF_NAME)|select(.pipeline_id != $PIPELINE_ID)|.id' \ + >> WF_to_cancel.txt + done + done +fi + +## Cancel any currently running/on_hold workflow with the same name +if [ -s WF_to_cancel.txt ]; then + echo "Canceling the following workflow(s):" + cat WF_to_cancel.txt + while read -r WF_ID; + do + curl --header "Circle-Token: $CIRCLE_API_KEY" --request POST https://circleci.com/api/v2/workflow/"$WF_ID"/cancel + done < WF_to_cancel.txt + ## Allowing some time to complete the cancellation + sleep 2 + else + echo "Nothing to cancel" +fi + +rm -f WF_to_cancel.txt \ No newline at end of file diff --git a/src/scripts/greet.sh b/src/scripts/greet.sh deleted file mode 100644 index f01be79..0000000 --- a/src/scripts/greet.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -echo Hello "${PARAM_TO}" \ No newline at end of file