CircleCIで特定の拡張子のファイル(今回は .go
)のみ変更されていた場合にのみjobのstepsを継続させたい
ケースに対応させるためのcommand
動かすのに時間溶かしたのでメモとして残しておきます
Dynamic Configurationを利用した例は見かけましたが
https://qiita.com/hatai/items/76ade5b81a0b2076263c
以下の都合でjob自体は動かしたかったのと既存のCIにサクッと組み込みたかったのでcommandsで実装しました
- プロダクションコード変更されていないのにLintやtestを動かしたくない
- GitHubのbranch protectionの都合job自体はskip動作で動かしたい
commands部分の実装
check_go_changes_in_pr_and_halt:
description: "Uses GitHub REST API (curl+jq) to find PR target branch, fetches history, checks for *.go changes against base branch, and halts if none found. Requires checkout and GITHUB_TOKEN."
parameters:
github-token:
type: env_var_name
default: GITHUB_TOKEN
steps:
- run:
name: Determine Target Branch via REST API, Fetch, Check Go Changes
command: |
# --- 1. Check Context and Required Env Vars ---
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "Not a Pull Request context. Proceeding."
exit 0
fi
if [ -z "${<< parameters.github-token >>}" ]; then
echo "Error: github-token parameters is not set. Required for GitHub REST API call."
exit 1 # Configuration error, fail fast
fi
# --- 2. Parse PR Info ---
PR_NUMBER=$(basename "$CIRCLE_PULL_REQUEST")
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "Error: Could not parse PR number from CIRCLE_PULL_REQUEST: $CIRCLE_PULL_REQUEST"
exit 0 # Proceed safely
fi
# Use CircleCI env vars for owner/repo
OWNER="$CIRCLE_PROJECT_USERNAME"
REPO="$CIRCLE_PROJECT_REPONAME"
if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
echo "Error: Could not determine repository owner/name from CircleCI env vars (CIRCLE_PROJECT_USERNAME, CIRCLE_PROJECT_REPONAME)."
exit 0 # Proceed safely
fi
echo "Detected PR: $OWNER/$REPO #$PR_NUMBER"
# --- 3. Call GitHub REST API determined Target Branch ---
API_URL="https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER"
echo "Querying GitHub REST API: $API_URL"
# Use curl to get PR details, jq to extract base.ref
# -sS: Silent but show errors. -f: Fail fast on HTTP errors (>=400). -L: Follow redirects.
if ! HTTP_RESPONSE=$(curl -sSLf \
-H "Authorization: Bearer ${<< parameters.github-token >>}" \
-H "Accept: application/vnd.github.v3+json" \
"$API_URL"); then
echo "Error: Failed to fetch PR details from GitHub API."
echo "Check API URL, token permissions (needs 'repo' scope for private repos), and network."
# Don't echo potentially sensitive response here if curl failed entirely
echo "Proceeding with job steps as a safety measure."
exit 0 # Exit script successfully to proceed safely
fi
# Parse the JSON response to get the target branch name
TARGET_BRANCH=$(echo "$HTTP_RESPONSE" | jq -r '.base.ref')
# Check if jq successfully extracted a non-null, non-empty value
if [ -z "$TARGET_BRANCH" ] || [ "$TARGET_BRANCH" == "null" ]; then
echo "Error: Could not parse target branch (.base.ref) from API response."
# Consider logging $HTTP_RESPONSE only if necessary for debugging, might contain sensitive info
# echo "API Response: $HTTP_RESPONSE"
echo "Proceeding with job steps as a safety measure."
exit 0 # Exit script successfully to proceed safely
fi
echo "Successfully determined Target Branch via REST API: $TARGET_BRANCH"
# --- 4. Perform git diff ---
echo "Checking for *.go file changes against base branch."
echo "git diff --name-only --no-prefix origin/$TARGET_BRANCH $CIRCLE_BRANCH"
if ! git diff --name-only --no-prefix origin/$TARGET_BRANCH $CIRCLE_BRANCH > changed_files.list; then
echo "Error: 'git diff \"origin/$TARGET_BRANCH\" \"$CIRCLE_BRANCH\"' command failed (Base branch likely not found)."
rm -f changed_files.list
exit 0 # Proceed safely
fi
# --- 5. Check diff output ---
echo "Checking for *.go file changes in diff output..."
if ! grep -q '\.go$' changed_files.list; then
echo "No *.go files changed. Halting subsequent steps."
rm -f changed_files.list
circleci-agent step halt
else
echo "*.go files changed. Proceeding."
rm -f changed_files.list
exit 0
fi
呼び出し部分のstepsの実装
jobs:
build_and_test:
docker:
- image: cimg/go:1.24.2 # Or any other image where you can install curl/jq
steps:
- checkout
# Call the command
- check_go_changes_in_pr_and_halt
# Subsequent steps (only run if halt was not called)
- run:
name: Build Go Project (if changes detected)
# ...
- run:
name: Test Go Project (if changes detected)
# ...