name: Classify CI changes on: workflow_call: outputs: build_and_test: description: Run the test and coverage matrix, compatibility tests, and mod build. value: ${{ jobs.changes.outputs.build_and_test }} js_lint: description: Run the JavaScript/TypeScript linter. value: ${{ jobs.changes.outputs.js_lint }} lua_lint: description: Run the Lua linter. value: ${{ jobs.changes.outputs.lua_lint }} jobs: changes: runs-on: ubuntu-latest timeout-minutes: 5 outputs: build_and_test: ${{ steps.syntax.outputs.build_and_test || steps.paths.outputs.code }} js_lint: ${{ steps.lint.outputs.js_lint }} lua_lint: ${{ steps.lint.outputs.lua_lint }} steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: dorny/paths-filter@v4 id: paths with: token: '' base: ${{ github.event.pull_request.base.sha || github.event.before }} ref: ${{ github.sha }} list-files: json predicate-quantifier: every filters: | # Files requiring validation beyond documentation checks. code: - '**' - '!**/*.md' - '!docs/**/*.{png,jpg,svg,gif}' # Sources and configuration owned by each linter. # Workflow scripts and shared build configuration run both linters. js: - '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}' - '!.github/**' - '!**/{webpack,babel,rollup,vite,jest}.config.*' lua: - '{*.lua,**/*.lua,.emmyrc.json}' - '!.github/**' # Modified JS/TS source eligible for the comment-only shortcut. # TypeScript type changes still count as substantive changes. syntax: - modified: '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}' # Source fixtures and templates always receive full validation. - '!test/file/**' - '!packages/create/templates/**' - '!.github/**' - '!**/{eslint,webpack,babel,rollup,vite,jest}.config.*' - name: Select linters id: lint env: CODE_FILES: ${{ steps.paths.outputs.code_files }} JS_FILES: ${{ steps.paths.outputs.js_files }} LUA_FILES: ${{ steps.paths.outputs.lua_files }} run: | # Unclassified non-documentation files can affect either language. jq -nr --argjson code "$CODE_FILES" --argjson js "$JS_FILES" --argjson lua "$LUA_FILES" ' ($code - $js - $lua | length > 0) as $shared | "js_lint=\($shared or ($js | length > 0))", "lua_lint=\($shared or ($lua | length > 0))" ' >> "$GITHUB_OUTPUT" - name: Install Difftastic if: steps.paths.outputs.syntax == 'true' env: DIFFT_VERSION: 0.71.0 DIFFT_SHA256: 61aea5394a53c56f144637cf39b3a0c0dafa27769731745e5851ff276e6478da run: | mkdir -p "$RUNNER_TEMP/difftastic" curl -fsSL "https://github.com/Wilfred/difftastic/releases/download/$DIFFT_VERSION/difft-$DIFFT_VERSION-x86_64-unknown-linux-gnu.tar.gz" -o "$RUNNER_TEMP/difftastic.tar.gz" echo "$DIFFT_SHA256 $RUNNER_TEMP/difftastic.tar.gz" | sha256sum -c tar -xzf "$RUNNER_TEMP/difftastic.tar.gz" -C "$RUNNER_TEMP/difftastic" echo "$RUNNER_TEMP/difftastic" >> "$GITHUB_PATH" - name: Check for substantive changes id: syntax if: steps.paths.outputs.code == 'true' env: CODE_FILES: ${{ steps.paths.outputs.code_files }} SYNTAX_FILES: ${{ steps.paths.outputs.syntax_files }} BASE: ${{ github.event.pull_request.base.sha || github.event.before }} HEAD_SHA: ${{ github.sha }} run: | # Skip only when every non-documentation file is an ordinary source edit. echo 'build_and_test=true' >> "$GITHUB_OUTPUT" # A new branch has no previous commit; keep full validation on its first push. if [[ "$BASE" == 0000000000000000000000000000000000000000 ]]; then exit 0 fi if ! jq -en --argjson code "$CODE_FILES" --argjson syntax "$SYNTAX_FILES" \ '$code - $syntax | length == 0' > /dev/null; then exit 0 fi git cat-file -e "$BASE^{commit}" jq -j '.[] | ., "\u0000"' <<< "$SYNTAX_FILES" > "$RUNNER_TEMP/syntax-files" while IFS= read -r -d '' file; do # Mode changes and symlinks are substantive even when text is identical. [[ -z "$(git diff --summary --no-renames "$BASE" "$HEAD_SHA" -- "$file")" ]] || exit 0 git ls-tree "$HEAD_SHA" -- "$file" | grep -qE '^100(644|755) blob ' || exit 0 before="$RUNNER_TEMP/before.${file##*.}" after="$RUNNER_TEMP/after.${file##*.}" git show "$BASE:$file" > "$before" git show "$HEAD_SHA:$file" > "$after" # Difftastic ignores ALL comments; retain compiler/bundler/coverage directives. if grep -Eq '@ts-|@jsx|@internal|> "$GITHUB_OUTPUT"