|
| 1 | +name: 'Should Skip Build' |
| 2 | +description: 'Check if container build should be skipped based on existing images and file changes' |
| 3 | +inputs: |
| 4 | + registry: |
| 5 | + description: 'Container registry URL (e.g., quay.io)' |
| 6 | + required: false |
| 7 | + image: |
| 8 | + description: 'Full image name including namespace (e.g., my-org/my-image)' |
| 9 | + required: false |
| 10 | + commit-sha: |
| 11 | + description: 'Git commit SHA to check' |
| 12 | + required: true |
| 13 | + check-image: |
| 14 | + description: 'Whether to check for existing container images (true/false)' |
| 15 | + required: false |
| 16 | + default: 'true' |
| 17 | + file-patterns: |
| 18 | + description: 'File extensions to check for skip logic (space or comma-separated, e.g., ".md")' |
| 19 | + required: false |
| 20 | + default: '.md' |
| 21 | +outputs: |
| 22 | + should_skip: |
| 23 | + description: 'Whether the build should be skipped (true/false)' |
| 24 | + value: ${{ steps.check.outputs.should_skip }} |
| 25 | + skip_reason: |
| 26 | + description: 'Human-readable reason for skipping' |
| 27 | + value: ${{ steps.check.outputs.skip_reason }} |
| 28 | +runs: |
| 29 | + using: 'composite' |
| 30 | + steps: |
| 31 | + - name: Install skopeo |
| 32 | + if: inputs.check-image == 'true' |
| 33 | + shell: bash |
| 34 | + run: | |
| 35 | + if ! command -v skopeo &> /dev/null; then |
| 36 | + echo "Installing skopeo" |
| 37 | + sudo apt-get update -q |
| 38 | + sudo apt-get install -y -q skopeo |
| 39 | + else |
| 40 | + echo "skopeo already installed" |
| 41 | + fi |
| 42 | +
|
| 43 | + - name: Check if build should be skipped |
| 44 | + id: check |
| 45 | + shell: bash |
| 46 | + run: | |
| 47 | + set +e # Don't exit on error, we handle them explicitly |
| 48 | + |
| 49 | + SHOULD_SKIP="false" |
| 50 | + SKIP_REASON="" |
| 51 | + |
| 52 | + # Check if image exists (if enabled) |
| 53 | + if [ "${{ inputs.check-image }}" = "true" ]; then |
| 54 | + echo "::group::Check if image exists with commit SHA" |
| 55 | + SHORT_SHA=$(echo "${{ inputs.commit-sha }}" | cut -c1-7) |
| 56 | + REPOSITORY="docker://${{ inputs.registry }}/${{ inputs.image }}" |
| 57 | + IMAGE_TAG="${REPOSITORY}:${SHORT_SHA}" |
| 58 | + |
| 59 | + echo "Checking for image: ${IMAGE_TAG}" |
| 60 | + |
| 61 | + # Try to inspect the image - if it exists, skopeo returns 0 |
| 62 | + skopeo inspect "${IMAGE_TAG}" &>/dev/null |
| 63 | + IMAGE_EXISTS=$? |
| 64 | + |
| 65 | + if [ $IMAGE_EXISTS -eq 0 ]; then |
| 66 | + SHOULD_SKIP="true" |
| 67 | + SKIP_REASON="Image with commit SHA ${SHORT_SHA} already exists in registry" |
| 68 | + echo "✓ Image exists - build will be skipped" |
| 69 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 70 | + echo "skip_reason=${SKIP_REASON}" >> $GITHUB_OUTPUT |
| 71 | + echo "::endgroup::" |
| 72 | + exit 0 |
| 73 | + else |
| 74 | + echo "✗ Image does not exist - continuing checks" |
| 75 | + fi |
| 76 | + echo "::endgroup::" |
| 77 | + else |
| 78 | + echo "Image checking disabled - skipping image existence check" |
| 79 | + fi |
| 80 | + |
| 81 | + echo "::group::Check if only documentation/ignored files changed" |
| 82 | + |
| 83 | + # Get the list of changed files based on event type |
| 84 | + CHANGED_FILES="" |
| 85 | + |
| 86 | + case "${{ github.event_name }}" in |
| 87 | + workflow_dispatch) |
| 88 | + echo "Workflow triggered manually - skipping file change check" |
| 89 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 90 | + echo "skip_reason=Manual trigger" >> $GITHUB_OUTPUT |
| 91 | + echo "::endgroup::" |
| 92 | + exit 0 |
| 93 | + ;; |
| 94 | + |
| 95 | + pull_request|pull_request_target) |
| 96 | + echo "Pull request event detected" |
| 97 | + if [ -n "${{ github.event.pull_request.base.sha }}" ]; then |
| 98 | + # Find the merge base between PR base and HEAD |
| 99 | + BASE_COMMIT=$(git merge-base ${{ github.event.pull_request.base.sha }} HEAD) |
| 100 | + echo "Base commit (merge-base): $BASE_COMMIT" |
| 101 | + CHANGED_FILES=$(git diff --name-only "$BASE_COMMIT" HEAD) |
| 102 | +
|
| 103 | + else |
| 104 | + echo "Warning: No PR base SHA available, skipping file check" |
| 105 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 106 | + echo "skip_reason=No base commit available for PR" >> $GITHUB_OUTPUT |
| 107 | + echo "::endgroup::" |
| 108 | + exit 0 |
| 109 | + fi |
| 110 | + ;; |
| 111 | + |
| 112 | + push) |
| 113 | + echo "Push event detected" |
| 114 | + if [ -n "${{ github.event.before }}" ] && [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then |
| 115 | + echo "Comparing with previous commit: ${{ github.event.before }}" |
| 116 | + CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }}) |
| 117 | + else |
| 118 | + echo "First push or no previous commit available" |
| 119 | + # Get all files in the current commit |
| 120 | + CHANGED_FILES=$(git diff-tree --name-only -r ${{ github.sha }}) |
| 121 | + fi |
| 122 | + ;; |
| 123 | + |
| 124 | + *) |
| 125 | + echo "Unsupported event type: ${{ github.event_name }}" |
| 126 | + echo "Proceeding with build as a safety measure" |
| 127 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 128 | + echo "skip_reason=Unsupported event type" >> $GITHUB_OUTPUT |
| 129 | + echo "::endgroup::" |
| 130 | + exit 0 |
| 131 | + ;; |
| 132 | + esac |
| 133 | + |
| 134 | + if [ -z "$CHANGED_FILES" ]; then |
| 135 | + echo "No files changed - build will be skipped" |
| 136 | + SHOULD_SKIP="true" |
| 137 | + SKIP_REASON="No files changed" |
| 138 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 139 | + echo "skip_reason=${SKIP_REASON}" >> $GITHUB_OUTPUT |
| 140 | + echo "::endgroup::" |
| 141 | + exit 0 |
| 142 | + fi |
| 143 | + |
| 144 | + echo "Changed files:" |
| 145 | + echo "$CHANGED_FILES" |
| 146 | + echo "" |
| 147 | + |
| 148 | + # Parse file patterns (handle both space and comma-separated) |
| 149 | + FILE_PATTERNS="${{ inputs.file-patterns }}" |
| 150 | + FILE_PATTERNS=$(echo "$FILE_PATTERNS" | tr ',' ' ') |
| 151 | + |
| 152 | + echo "Checking for file patterns: $FILE_PATTERNS" |
| 153 | + echo "" |
| 154 | + |
| 155 | + # Check if all changed files match the ignore patterns |
| 156 | + ALL_FILES_MATCH_PATTERN="true" |
| 157 | + NON_MATCHING_FILES=() |
| 158 | + |
| 159 | + while IFS= read -r file; do |
| 160 | + if [ -z "$file" ]; then |
| 161 | + continue |
| 162 | + fi |
| 163 | + |
| 164 | + # Check if file matches any of the patterns |
| 165 | + FILE_MATCHES="false" |
| 166 | + for pattern in $FILE_PATTERNS; do |
| 167 | + if [[ "$file" =~ ${pattern}$ ]]; then |
| 168 | + FILE_MATCHES="true" |
| 169 | + break |
| 170 | + fi |
| 171 | + done |
| 172 | + |
| 173 | + if [ "$FILE_MATCHES" = "false" ]; then |
| 174 | + ALL_FILES_MATCH_PATTERN="false" |
| 175 | + NON_MATCHING_FILES+=("$file") |
| 176 | + fi |
| 177 | + done <<< "$CHANGED_FILES" |
| 178 | + |
| 179 | + if [ "$ALL_FILES_MATCH_PATTERN" = "true" ]; then |
| 180 | + SHOULD_SKIP="true" |
| 181 | + SKIP_REASON="All changed files match skip patterns: $FILE_PATTERNS" |
| 182 | + echo "✓ All changed files match skip patterns - build will be skipped" |
| 183 | + else |
| 184 | + echo "✗ Found ${#NON_MATCHING_FILES[@]} file(s) not matching skip patterns:" |
| 185 | + printf ' - %s\n' "${NON_MATCHING_FILES[@]}" |
| 186 | + echo "Build will proceed" |
| 187 | + fi |
| 188 | + |
| 189 | + echo "::endgroup::" |
| 190 | + |
| 191 | + echo "should_skip=${SHOULD_SKIP}" >> $GITHUB_OUTPUT |
| 192 | + echo "skip_reason=${SKIP_REASON}" >> $GITHUB_OUTPUT |
| 193 | +
|
0 commit comments