Skip to content

Instantly share code, notes, and snippets.

@ndbroadbent
Created April 4, 2022 02:55
Show Gist options
  • Save ndbroadbent/3b2c909432913d567680f2e683fbaaff to your computer and use it in GitHub Desktop.
Save ndbroadbent/3b2c909432913d567680f2e683fbaaff to your computer and use it in GitHub Desktop.
run_failed_gitlab_pipeline_specs
#!/bin/bash
set -eo pipefail
CURRENT_DIR="$(realpath $(dirname "$0"))"
ROOT_DIR="$(realpath $CURRENT_DIR/..)"
# https://gitlab.com/docspring/docspring
PROJECT_ID="1908805"
GITLAB_TOKEN=$(cat $ROOT_DIR/.gitlab-api-token)
PIPELINE_ID="$1"
if [ -z "$PIPELINE_ID" ]; then
CURRENT_GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
echo "Fetching latest failed pipeline ID for '$CURRENT_GIT_BRANCH'..."
ESCAPED_BRANCH=$(ruby -r cgi -e "puts CGI.escape('$CURRENT_GIT_BRANCH')")
LATEST_PIPELINE_RESPONSE=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?ref=$ESCAPED_BRANCH&status=failed&per_page=1&page=1")
PIPELINE_ID="$(echo $LATEST_PIPELINE_RESPONSE | jq -r '.[0].id')"
echo "Found latest failed pipeline ID: $PIPELINE_ID"
echo
echo $LATEST_PIPELINE_RESPONSE | jq -r '" * " + .[0].web_url'
echo
fi
PIPELINE_CACHE_DIR="$ROOT_DIR/tmp/gitlab_pipeline_results/$PIPELINE_ID"
mkdir -p "$PIPELINE_CACHE_DIR"
JOB_IDS_FILE="$PIPELINE_CACHE_DIR/job_ids.json"
if [ -f "$JOB_IDS_FILE" ]; then
echo "Found jobs for GitLab CI Pipeline $PIPELINE_ID in $JOB_IDS_FILE"
FAILED_PIPELINE_JOBS_RESPONSE="$(cat "$JOB_IDS_FILE")"
else
echo "Listing jobs for GitLab CI Pipeline $PIPELINE_ID..."
FAILED_PIPELINE_JOBS_RESPONSE="$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs?scope[]=failed")"
echo "$FAILED_PIPELINE_JOBS_RESPONSE" > "$JOB_IDS_FILE"
fi
FAILED_RSPEC_JOB_COUNT=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("rspec"))) | map(.id) | length')
FAILED_RSPEC_JOB_IDS=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("rspec"))) | map(.id) | .[]')
FAILED_RUBYLINT_JOB_COUNT=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("ruby_lint"))) | map(.id) | length')
FAILED_RUBYLINT_JOB_IDS=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("ruby_lint"))) | map(.id) | .[]')
FAILED_SECURITY_JOB_COUNT=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("security"))) | map(.id) | length')
FAILED_SECURITY_JOB_IDS=$(echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("security"))) | map(.id) | .[]')
echo "Found $FAILED_RSPEC_JOB_COUNT failed RSpec job(s)"
echo
if [ "$FAILED_RSPEC_JOB_COUNT" != "0" ]; then
echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("rspec"))) | map(" * " + .web_url) | .[]'
echo
fi
echo "Found $FAILED_RUBYLINT_JOB_COUNT failed Ruby Lint job(s)"
echo
if [ "$FAILED_RUBYLINT_JOB_COUNT" != "0" ]; then
echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("ruby_lint"))) | map(" * " + .web_url) | .[]'
echo
fi
echo "Found $FAILED_SECURITY_JOB_COUNT failed security job(s)"
echo
if [ "$FAILED_SECURITY_JOB_COUNT" != "0" ]; then
echo "$FAILED_PIPELINE_JOBS_RESPONSE" | \
jq -r '. | map(select(.name | contains ("security"))) | map(" * " + .web_url) | .[]'
echo
fi
if [ "$FAILED_RSPEC_JOB_COUNT" == "0" ] &&
[ "$FAILED_RUBYLINT_JOB_COUNT" == "0" ] &&
[ "$FAILED_SECURITY_JOB_COUNT" == "0" ]; then
exit
fi
for JOB_ID in $FAILED_RSPEC_JOB_IDS; do
JOB_LOGS_FILE="$PIPELINE_CACHE_DIR/rspec_job_$JOB_ID.txt"
if [ -f "$JOB_LOGS_FILE" ]; then
echo "Found logs for failed RSpec job $JOB_ID in $JOB_LOGS_FILE"
else
echo "Fetching logs for failed RSpec job $JOB_ID"
curl -s --location --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace" > "$JOB_LOGS_FILE"
fi
done
for JOB_ID in $FAILED_RUBYLINT_JOB_IDS; do
HAS_FAILED_RUBOCOP_JOB=true
JOB_LOGS_FILE="$PIPELINE_CACHE_DIR/ruby_lint_job_$JOB_ID.txt"
if [ -f "$JOB_LOGS_FILE" ]; then
echo "Found logs for failed Ruby Lint job $JOB_ID in $JOB_LOGS_FILE"
else
echo "Fetching logs for failed Ruby Lint job $JOB_ID"
curl -s --location --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace" > "$JOB_LOGS_FILE"
fi
done
echo
if [ "$HAS_FAILED_RUBOCOP_JOB" == true ]; then
FAILING_RUBOCOP_FILES="$(ruby -e "puts Dir.glob(\"$PIPELINE_CACHE_DIR/ruby_lint_job_*.txt\").map { |f|
logs = File.read(f);
start_regex = /Offenses:/;
end_regex = /\d+ files? inspected, \d+ offenses? detected/;
match_regex = /^(.+):\d+:\d+:/;
next nil if !logs.match?(start_regex) || !logs.match?(end_regex);
logs.
split(start_regex)[1].
split(end_regex)[0].
scan(match_regex)
}.flatten.reject(&:nil?).map(&:strip).
select { |f| File.exist?(f) }.join(' ')")"
if [ -z "$FAILING_RUBOCOP_FILES" ]; then
echo "Could not find any Ruby files in the RuboCop output! Please check the logs manually." >&2
else
echo "Running 'rubocop -a' to fix Rubocop errors..."
echo "=> bundle exec rubocop -a $FAILING_RUBOCOP_FILES"
bundle exec rubocop -a $FAILING_RUBOCOP_FILES
fi
fi
FAILING_SPECS=$(ruby -e "puts Dir.glob(\"$PIPELINE_CACHE_DIR/rspec_job_*.txt\").map { |f|
logs = File.read(f);
start_regex = /Failed examples?:/;
end_regex = /Randomized with seed/;
match_regex = /rspec '?([^'#]+)/;
next nil if !logs.match?(start_regex) || !logs.match?(end_regex);
logs.
split(start_regex)[1].
split(end_regex)[0].
scan(match_regex)
}.flatten.reject(&:nil?).map(&:strip).join(' ')")
# echo "FAILING_SPECS: '$FAILING_SPECS'"
if [ -z "$FAILING_SPECS" ]; then
echo "Could not find any failing specs in the RSpec output! Please check the logs manually." >&2
else
echo "Running failed specs locally..."
echo "=> bundle exec ./bin/rspec $FAILING_SPECS"
bundle exec ./bin/rspec $FAILING_SPECS
fi
if [ "$FAILED_SECURITY_JOB_COUNT" != "0" ]; then
./scripts/security
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment