|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Prints which checks a pull request's changes can affect, as `<flag>=true|false` lines for |
| 3 | +# $GITHUB_OUTPUT. A flag is `false` only when every changed file is on an allowlist whose jobs |
| 4 | +# fully cover it. A file on no allowlist sets every flag but `desktop_live` (which keeps its own |
| 5 | +# skip list) to `true`; a change under .github/, an empty diff, or any failure to work out the |
| 6 | +# diff sets all of them. |
| 7 | +# |
| 8 | +# full integration, e2e, e2e-build, e2e-mobile: any file not allowlisted below |
| 9 | +# test the Vitest shards and test-types: content, docs app, and desktop changes, |
| 10 | +# plus `full` |
| 11 | +# build the app's `next build`: content changes, plus `full` |
| 12 | +# desktop_live the live Electron suite: any file outside its own skip list |
| 13 | +# |
| 14 | +# lint always runs and is not gated. |
| 15 | +# |
| 16 | +# Usage: ci-changes.sh <base-sha> |
| 17 | +set -u |
| 18 | + |
| 19 | +# Posts, author profiles, and the public assets only posts reference. The test shards read them |
| 20 | +# (lib/content/og-image.test.ts) and `next build` prerenders every post with its MDX components, |
| 21 | +# which check:library-content compiles but never renders. Components under content/ are code. |
| 22 | +content='^apps/sim/content/([^/]+/)*[^/]+\.mdx?$|^apps/sim/content/authors/[^/]+\.json$|^apps/sim/public/(authors|blog|changelog|library)/' |
| 23 | +# Its own workspace tests, its type-check and docs-manifest audit in lint, and apps/sim tests |
| 24 | +# that read its pages (tools/netsuite/netsuite.test.ts). |
| 25 | +docs='^apps/docs/' |
| 26 | +# Nothing outside the Electron app imports it; the desktop bridge and protocol packages are |
| 27 | +# consumed by apps/sim and stay unlisted. |
| 28 | +desktop='^apps/desktop/' |
| 29 | +# Agent guidance, read only by the lint job's audits. |
| 30 | +guidance='^(\.agents|\.claude|\.cursor)/|^([^/]+/)*(AGENTS|CLAUDE)\.md$|^README\.md$' |
| 31 | +# The live desktop suite drives apps/desktop against the app, so it skips more than the rest. |
| 32 | +desktop_live_skip="$guidance"'|^(apps/docs/|apps/pii/|apps/sim/content/|apps/sim/public/(authors|blog|changelog|library)/|packages/(python-sdk|ts-sdk)/)|\.mdx?$|(^|/)LICENSE$' |
| 33 | + |
| 34 | +emit() { |
| 35 | + printf 'full=%s\ntest=%s\nbuild=%s\ndesktop_live=%s\n' "$1" "$2" "$3" "$4" |
| 36 | +} |
| 37 | +run_all() { |
| 38 | + emit true true true true |
| 39 | + echo "Running every check: $1" >&2 |
| 40 | + exit 0 |
| 41 | +} |
| 42 | +matches() { printf '%s\n' "$names" | grep -qE "$1"; } |
| 43 | +outside() { printf '%s\n' "$names" | grep -qvE "$1"; } |
| 44 | + |
| 45 | +base=${1:-} |
| 46 | +[ -n "$base" ] || run_all 'no base commit' |
| 47 | +git fetch --quiet --depth=1 origin "$base" || run_all "could not fetch $base" |
| 48 | +names=$(git diff --no-renames --name-only "$base" HEAD) || run_all "could not diff against $base" |
| 49 | +[ -n "$names" ] || run_all 'no changed files listed' |
| 50 | +if matches '^\.github/'; then run_all 'a CI file changed'; fi |
| 51 | + |
| 52 | +desktop_live=false |
| 53 | +if outside "$desktop_live_skip"; then desktop_live=true; fi |
| 54 | +if outside "$content|$docs|$desktop|$guidance"; then |
| 55 | + emit true true true "$desktop_live" |
| 56 | + unlisted=$(printf '%s\n' "$names" | grep -vE "$content|$docs|$desktop|$guidance" | head -n 1) |
| 57 | + echo "Running every check (desktop_live=$desktop_live): $unlisted is not on an allowlist" >&2 |
| 58 | + exit 0 |
| 59 | +fi |
| 60 | + |
| 61 | +test=false build=false |
| 62 | +if matches "$content|$docs|$desktop"; then test=true; fi |
| 63 | +if matches "$content"; then build=true; fi |
| 64 | +emit false "$test" "$build" "$desktop_live" |
| 65 | +echo "Scoped checks: test=$test build=$build desktop_live=$desktop_live" >&2 |
0 commit comments