Skip to content

Instantly share code, notes, and snippets.

@donaldguy
Last active December 12, 2020 22:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donaldguy/b713fa44ecca1f912faf1414c7a028b9 to your computer and use it in GitHub Desktop.
Save donaldguy/b713fa44ecca1f912faf1414c7a028b9 to your computer and use it in GitHub Desktop.
CircleCI failures fetch/local rerun scripts: rspec + cucumber, but not much would be needed to adapt for other langs/libs
#!/bin/bash
#: features_failures_on_circle.sh [job number] [project name]
#:
#: Summarizes recorded CircleCI results for a (parallelized) cucumber suite, with
#: a view towards debugging failures locally on macOS.
#:
#: BE ADVISED: running will overwrite your clipboard. You should consider a
#: clipboard history. I like the one provided by Alfred PowerPack
#:
#: Attempts to intuit urls from git remote.
#:
#: Sample output:
#: Checking jq installed: ✔
#: Querying CircleCI for failures in donaldguy/circlec-test Job #33 (features):
#: {
#: "counts": {
#: "failure": 1,
#: "skipped": 6,
#: "success": 1775
#: },
#: "failed_files": [
#: "features/example.feature"
#: ],
#: "failing_lines": [
#: "features/example.feature:9",
#: "features/example.feature:31"
#: ]
#: }
#:
#: Rerun files as:
#: bundle exec rspec features/example.feature
#:
#: Paste and push enter to rerun failing cases locally
#:
#: https://gist.github.com/donaldguy/33d0ed6ec7125d9d39181c2e9feb9b49
set -euo pipefail
JOB_NAME="features"
JOB_NUMBER=${1:-}
PROJECT_NAME=${2:-$(git remote get-url origin | perl -ne 'm/(?<=github.com[:\/])[\w\/]+/; print $&')}
# green HEAVY CHECK MARK (U+2714)
CHECKMARK="$(tput setaf 2)"$'\xe2\x9c\x94'"$(tput sgr0)"
# red HEAVY BALLOT X (U+2718)
EXMARK="$(tput setaf 1)"$'\xe2\x9c\x98'"$(tput sgr0)"
declare -r CHECKMARK EXMARK
function bolded_text() {
printf '%s' "$(tput bold)$1$(tput sgr0)"
}
function ensure_brew_installed() {
cmd=$1
printf '%s' "Checking $(bolded_text "$cmd") installed: "
if ! which "$cmd" &> /dev/null; then
echo "$EXMARK Installing..."
brew install "$cmd"
else
echo "$CHECKMARK"
fi
}
ensure_brew_installed jq
if [[ -z "$JOB_NUMBER" ]]; then
if [[ ! -f ~/.config/gh/hosts.yml ]]; then
ensure_brew_installed gh
echo "Running $(bolded_text "gh auth login --web"):"
gh auth login --web
fi
JOB_NUMBER=$((gh pr checks || :) | grep ": $JOB_NAME" | perl -ne 'm{(?<='"$PROJECT_NAME"'/)\d+(?=\?)}; print $&')
fi
if [[ ! -f ~/.circleci/cli.yml ]]; then
ensure_brew_installed circleci
echo "Press enter to run $(bolded_text "circleci setup") and open $(bolded_text "https://app.circleci.com/settings/user/tokens") in default browser"
read -r
open "https://app.circleci.com/settings/user/tokens"
circleci setup
fi
echo "Querying CircleCI for failures in $PROJECT_NAME Job #$JOB_NUMBER ($JOB_NAME): "
RESULTS=$(curl \
-sH "Circle-Token: $(perl -ne 'if (m/(?<=token: ).*/) { print $&; exit }' ~/.circleci/cli.yml)" \
"https://circleci.com/api/v2/project/github/$PROJECT_NAME/$JOB_NUMBER/tests" | \
jq -cS '.items | group_by(.result) | map({(.[0].result): .}) | add |
{
counts: (to_entries | map({(.key): (.value | length)}) | add),
failing_lines: (.failure // [] |
map( ".+\\.feature:\\d+" as $pattern |
.message | [scan($pattern)] | last
)
)
} | [., {failed_files: (.failing_lines | map(split(":") | first) | unique)}] | add ')
jq '.' <<<"$RESULTS"
echo
echo "Rerun files as: "
tput bold
tput setaf 3
jq -r '" bundle exec cucumber \(.failed_files | join(" "))"' <<<"$RESULTS"
tput sgr0
tput bold
tput setaf 6
echo
echo "Paste and push enter to rerun failing cases locally"
jq -r '"bundle exec cucumber \(.failing_lines | join(" "))"' <<<"$RESULTS" | pbcopy
tput sgr0
#: (C) 2020 Donald Guy
#:
#: Permission is hereby granted, free of charge, to any person obtaining a copy
#: of this software and associated documentation files (the "Software"), to deal
#: in the Software without restriction, including without limitation the rights
#: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#: copies of the Software, and to permit persons to whom the Software is
#: furnished to do so, subject to the following conditions:
#
#: The above copyright notice and this permission notice shall be included in all
#: copies or substantial portions of the Software.
#
#: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#: SOFTWARE.
#!/bin/bash
#: rspec_failures_on_circle.sh [job number] [project name]
#:
#: Summarizes recorded CircleCI results for a (parallelized) rspec suite, with
#: a view towards debugging failures locally on macOS.
#:
#: BE ADVISED: running will overwrite your clipboard. You should consider a
#: clipboard history. I like the one provided by Alfred PowerPack
#:
#: Attempts to intuit urls from git remote.
#:
#: Sample output:
#: Checking jq installed: ✔
#: Querying CircleCI for failures in donaldguy/circlec-test Job #17 (specs):
#: {
#: "counts": {
#: "failure": 1,
#: "skipped": 6,
#: "success": 1775
#: },
#: "failed_files": [
#: "spec/models/example_spec.rb"
#: ],
#: "failing_lines": [
#: "spec/models/example_spec.rb:9"
#: ]
#: }
#:
#: Rerun files as:
#: bundle exec rspec spec/models/example_spec.rb
#:
#: Paste and push enter to rerun failing cases locally
#:
#: https://gist.github.com/donaldguy/b713fa44ecca1f912faf1414c7a028b9
set -euo pipefail
JOB_NAME="specs"
JOB_NUMBER=${1:-}
PROJECT_NAME=${2:-$(git remote get-url origin | perl -ne 'm/(?<=github.com[:\/])[\w\/]+/; print $&')}
# green HEAVY CHECK MARK (U+2714)
CHECKMARK="$(tput setaf 2)"$'\xe2\x9c\x94'"$(tput sgr0)"
# red HEAVY BALLOT X (U+2718)
EXMARK="$(tput setaf 1)"$'\xe2\x9c\x98'"$(tput sgr0)"
declare -r CHECKMARK EXMARK
function bolded_text() {
printf '%s' "$(tput bold)$1$(tput sgr0)"
}
function ensure_brew_installed() {
cmd=$1
printf '%s' "Checking $(bolded_text "$cmd") installed: "
if ! which "$cmd" &> /dev/null; then
echo "$EXMARK Installing..."
brew install "$cmd"
else
echo "$CHECKMARK"
fi
}
ensure_brew_installed jq
if [[ -z "$JOB_NUMBER" ]]; then
if [[ ! -f ~/.config/gh/hosts.yml ]]; then
ensure_brew_installed gh
echo "Running $(bolded_text "gh auth login --web"):"
gh auth login --web
fi
JOB_NUMBER=$((gh pr checks || :) | grep ": $JOB_NAME" | perl -ne 'm{(?<='"$PROJECT_NAME"'/)\d+(?=\?)}; print $&')
fi
if [[ ! -f ~/.circleci/cli.yml ]]; then
ensure_brew_installed circleci
echo "Press enter to run $(bolded_text "circleci setup") and open $(bolded_text "https://app.circleci.com/settings/user/tokens") in default browser"
read -r
open "https://app.circleci.com/settings/user/tokens"
circleci setup
fi
echo "Querying CircleCI for failures in $PROJECT_NAME Job #$JOB_NUMBER ($JOB_NAME): "
RESULTS=$(curl \
-sH "Circle-Token: $(perl -ne 'if (m/(?<=token: ).*/) { print $&; exit }' ~/.circleci/cli.yml)" \
"https://circleci.com/api/v2/project/github/$PROJECT_NAME/$JOB_NUMBER/tests" | \
jq -r '.items | group_by(.result) | map({(.[0].result): .}) | add |
{
counts: (to_entries | map({(.key): (.value | length)}) | add),
failed_files: (.failure // [] | map(.file) | unique),
failing_lines: (.failure // [] |
map( "\(.file):\\d+" as $pattern |
.message | [scan($pattern)] | last
)
)
}')
jq '.' <<<"$RESULTS"
echo
echo "Rerun files as: "
tput bold
tput setaf 3
jq -r '" bundle exec rspec \(.failed_files | join(" "))"' <<<"$RESULTS"
tput sgr0
tput bold
tput setaf 6
echo
echo "Paste and push enter to rerun failing cases locally"
jq -r '"bundle exec rspec \(.failing_lines | join(" "))"' <<<"$RESULTS" | pbcopy
tput sgr0
#: (C) 2020 Donald Guy
#:
#: Permission is hereby granted, free of charge, to any person obtaining a copy
#: of this software and associated documentation files (the "Software"), to deal
#: in the Software without restriction, including without limitation the rights
#: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#: copies of the Software, and to permit persons to whom the Software is
#: furnished to do so, subject to the following conditions:
#
#: The above copyright notice and this permission notice shall be included in all
#: copies or substantial portions of the Software.
#
#: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#: SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment