Skip to main content
Version: Next (unreleased)

Exclude Patterns

GrillMyCode automatically detects your repository's language and framework stack and applies the appropriate exclude patterns at runtime — no manual configuration required in most cases.

Binary files are never assessed

Regardless of any include or exclude settings, binary files are always skipped before being sent to the AI. Any file whose content contains a null byte is automatically filtered out. Only text-based source files are eligible for assessment.

How the decision flow works

For each file in the changed diff, the action applies this logic in order:

1. Is it a binary file (contains a null byte)? → always skip, regardless of everything else
2. Does it match any exclude pattern? → skip, UNLESS step 3 applies
3. Does it match an exclude_pattern_overrides entry? → eligible for assessment (overrides win)
4. None of the above → eligible for assessment

The exclude patterns themselves come from three sources, merged in this order:

SourceInputPurpose
Always-excluded(hardcoded)Lock files, env files, OS noise, source maps, logs, Markdown — never relevant to assessment
Auto-detected stack(automatic)Build artifacts, dependency dirs, generated files for your specific language/framework
Instructor additionsadditional_exclude_patternsAssignment-specific files the auto-detection wouldn't know about

The final exclude list is the union of all three. exclude_pattern_overrides can punch individual files back through after the fact.

How auto-detection works

When the action runs it performs up to seven lookups using the already-available github_token:

  1. GitHub Languages API — queries /repos/{owner}/{repo}/languages to identify all languages present in the repository (the same data shown on the repo's language bar).
  2. Repository root inspection — checks for well-known config files and directories (package.json, pom.xml, Cargo.toml, go.mod, artisan, wp-config.php, grails-app/, project.godot, firebase.json, angular.json, deno.json, .vscode/, .idea/, etc.) to detect frameworks and editors.
  3. Root filename suffix scan — detects frameworks whose project file includes a variable component by checking whether any root entry ends with a known suffix: .xcodeproj / .xcworkspace → Xcode, .uproject → Unreal Engine, .pro → Qt, .ipynb → Jupyter Notebooks.
  4. package.json dependency scan (JS/TS repos only) — if a package.json is found in the root, its dependencies and devDependencies are read and matched against known framework packages (next, @angular/core, svelte, vue, nuxt, @tauri-apps/api, etc.). This catches the correct framework regardless of which config filename convention the project uses.
  5. composer.json dependency scan (PHP repos only) — if a composer.json is found in the root, its require and require-dev entries are read and matched against known framework packages (laravel/framework, symfony/framework-bundle, drupal/core, codeigniter4/framework, yiisoft/yii2, cakephp/cakephp, WordPress installers like roots/wordpress, etc.). Like the package.json scan, this identifies the framework even when its config files aren't at the repo root — for example Bedrock relocates wp-config.php, and Symfony Flex projects may not commit symfony.lock.
  6. Gemfile dependency scan (Ruby repos only) — if a Gemfile is found in the root, its gem declarations are read and matched against known framework gems (rails, jekyll, nanoc). This is more reliable than inferring the framework from a Rakefile, since many non-Rails projects ship a Rakefile and many Rails apps don't.
  7. mix.exs dependency scan (Elixir repos only) — if a mix.exs is found in the root, its dependency tuples (e.g. {:phoenix, "~> 1.7"}) are read and matched against known framework packages (phoenix). The base Elixir template already covers _build/ and deps/; this adds the Phoenix web artifacts (priv/static/**, tmp/) on top.

Each detected signal is mapped to one or more github/gitignore templates, or to a set of known artifact paths for frameworks that have no upstream template (e.g. SvelteKit's .svelte-kit/, Nuxt's .nuxt/ and .output/). The action ships with all 300+ templates bundled in the Docker image (kept current via a weekly automated PR).

Template patterns are emitted depth-independently: node_modules/ in the upstream template becomes **/node_modules/**, so a nested frontend/node_modules/ is excluded just as a root-level one is. Only patterns the upstream template anchors with a leading slash (e.g. /build/) stay root-anchored. The examples below name each template's patterns in their unprefixed form for brevity.

Examples:

  • A plain Node repo → Node template: node_modules/**, dist/**, coverage/**, etc.
  • A Next.js repo → Node + Nextjs templates: adds .next/** on top of the Node exclusions.
  • A SvelteKit repo → Node template + .svelte-kit/** (no upstream template exists for Svelte).
  • A Angular repo → Node + Angular templates: adds .angular/**.
  • A plain PHP repo with a composer.jsonComposer template: vendor/**, composer.phar, etc.
  • A Laravel repo → Composer template + Laravel template: vendor/**, bootstrap/compiled.php, storage cache dirs, etc.
  • A Symfony / Drupal / CodeIgniter / Yii / CakePHP / WordPress repo → Composer template + the matching framework template, detected from the composer.json dependency scan.
  • A Unity repo → Dotnet template + Unity template: Library/**, Temp/**, obj/**, etc.
  • A Godot repo → Godot template: .godot/**, *.import, export presets, etc.
  • A plain Ruby repo with a GemfileRuby template: *.gem, /.bundle/, /vendor/bundle, etc.
  • A Rails repo → Ruby + Rails templates: adds /log/**, /tmp/**, storage/**, public/assets, etc., detected from the Gemfile gem scan.
  • A Jekyll repo → Ruby + Jekyll templates: adds _site/, .jekyll-cache/, .jekyll-metadata.
  • A plain Elixir repo with a mix.exsElixir template: _build/**, deps/**, *.beam, erl_crash.dump, etc.
  • A Phoenix repo → Elixir + community/Elixir/Phoenix templates: adds priv/static/**, tmp/**, assets/node_modules, detected from the mix.exs dependency scan.
  • A Python repo → Python template: __pycache__/**, *.pyc, .venv/**, *.egg-info/**, etc.
  • A Jupyter Notebooks repo (any .ipynb in root) → Python + community/Python/JupyterNotebooks templates.
  • A Java repo with a pom.xmlJava + Maven templates: target/**, .gradle/**, *.class, etc.
  • A Grails repo (has a grails-app/ directory) → Java + Gradle + Grails templates: adds web-app/WEB-INF/classes, *Db.*, stacktrace.log, etc.
  • A mixed JS + Python repo → gets the union of all matched template sets.
Python frameworks need no per-framework detection

Unlike JavaScript and PHP — where each framework ships its own gitignore template or build directory — Python's upstream Python template is a single comprehensive file that already folds in the artifacts for Django (db.sqlite3, local_settings.py), Flask (instance/**, .webassets-cache), Scrapy (.scrapy), Celery (celerybeat-*), Sphinx/MkDocs, and more. A Django or Flask repo is therefore fully covered the moment Python is detected — there is no requirements.txt / pyproject.toml dependency scan because it would add nothing the Python template doesn't already exclude. The only Python tool caches not in that template — .gradio/** and .dvc/cache/** — are added to the always-excluded list below.

If detection fails (e.g. the GitHub API is unreachable) the action falls back to a broad built-in list covering the most common languages.

Patterns always excluded

The following are excluded from every run regardless of detected stack:

PatternReason
**/.git/**Git internals
**/.gitignore, **/.gitattributes, **/.gitmodules, **/.mailmap, **/.git-blame-ignore-revsVCS config, not student code
**/.classroom50.yamlClassroom 50 accept-time metadata, written by gh student accept under the student's own commit identity — not student-authored code
.github/workflows/**GitHub Actions workflow files — usually not student-authored code. You can override certain files for evaluation if needed.
**/*.lock, **/package-lock.json, **/yarn.lock, **/pnpm-lock.yaml, **/Pipfile.lock, **/poetry.lockLock files — machine-generated, often enormous
**/*.min.js, **/*.min.cssMinified assets — unreadable by design
**/.env, **/.env.*Environment files — may contain secrets
**/*.tsbuildinfoTypeScript incremental build metadata
**/.gradio/**, **/.dvc/cache/**Python tool caches (Gradio, DVC) not covered by the bundled Python template
**/.DS_Store, **/Thumbs.dbOS-generated noise
**/*.mapSource maps (generated, not authored)
**/*.logLog output
**/*.mdMarkdown docs — pass assignment briefs via assignment_context instead
**/*.svgSVG assets

Pattern syntax

This section describes the patterns you write in additional_exclude_patterns and exclude_pattern_overrides. The built-in and auto-detected patterns above already carry explicit **/ prefixes, so they never depend on the matchBase behaviour described here.

Patterns use minimatch glob syntax with two options enabled: dot: true (matches dotfiles) and matchBase: true (a pattern with no / matches against the filename only, regardless of directory depth).

PatternWhat it matches
tests/**Everything inside a tests/ directory at any depth
*.pycAny file ending in .pyc in any directory (matchBase)
data/*.csv.csv files directly inside a data/ directory
**/*.test.jsAny .test.js file at any depth
provided_starter/**All files inside provided_starter/
config.jsonAny file named exactly config.json at any depth (matchBase)
src/config.jsonOnly src/config.json specifically (has a /, so anchored)

Key behaviours to know:

  • Patterns with no / in them match on filename only — *.log matches logs/server.log, not just server.log at the root.
  • Patterns with a / are matched against the full path — src/*.js only matches JS files directly in src/, not src/utils/helper.js.
  • ** matches across directory separators — tests/** matches tests/unit/foo.test.js.

Worked examples:

File pathPatternMatch?Why
src/index.jssrc/**** covers all descendants
src/utils/helper.jssrc/**nested path, still under src/
src/utils/helper.jssrc/*.js* doesn't cross / — only direct children of src/
tests/unit/auth.test.js**/*.test.js** matches any prefix path
tests/fixtures/users.jsontests/fixtures/**anchored subfolder glob
e2e/fixtures/users.jsontests/fixtures/**anchored — wrong top-level dir
config.jsonconfig.jsonmatchBase — no slash, matches filename anywhere
src/config.jsonconfig.jsonmatchBase applies at any depth
src/config.jsonsrc/config.jsonanchored exact path
lib/config.jsonsrc/config.jsonanchored — path doesn't match
provided_code/solution.pyprovided_code/**everything under the dir
src/provided_code/solution.pyprovided_code/**anchored — not at root level
src/provided_code/solution.py**/provided_code/**leading ** matches any prefix

Additional patterns

Use additional_exclude_patterns for files specific to your assignment that the auto-detected templates wouldn't know about:

- uses: NSCC-ITC-Assessment/GrillMyCode@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
api_key: ${{ secrets.OPENROUTER_API_KEY }}
additional_exclude_patterns: 'data/**, tests/fixtures/**, provided_starter/**'

Common use cases for instructors:

ScenarioPattern
Exclude provided starter filesprovided_starter/**
Exclude test fixtures or sample datatests/fixtures/**, data/**
Exclude a specific config file you providedconfig.json
Exclude everything in a specific subdirectorysrc/lib/**
Exclude all SQL migration files**/*.sql

Override exclude patterns

If a file is excluded but you want it assessed, use exclude_pattern_overrides. This takes precedence over everything — both auto-detected patterns and additional_exclude_patterns.

Each entry can be:

  • An exact pattern — re-includes all files matching that pattern:
    exclude_pattern_overrides: '**/*.md' # re-includes all Markdown files
  • A specific file path — only that one file passes through:
    exclude_pattern_overrides: 'README.md' # only README.md; other .md files stay excluded

Both forms can be combined:

exclude_pattern_overrides: 'README.md, SOLUTION.md'
When to use overrides vs additional patterns

Use additional_exclude_patterns to narrow what gets assessed (exclude more). Use exclude_pattern_overrides to widen what gets assessed (re-include something excluded by default).

Workflow files

GitHub Actions workflow files (.github/workflows/**) are always excluded. This prevents questions from being generated about the GrillMyCode workflow file itself.

Confirming what was applied

The action logs the full exclude list on every run. Look for these lines in the workflow step output:

Detected languages: JavaScript, TypeScript
Scanned package.json — 42 deps
Using gitignore templates: Node, Nextjs, Global/VisualStudioCode
Additional exclude patterns (from input): data/**, tests/fixtures/**
Exclude pattern overrides (re-included): README.md
Exclude patterns applied (94):
.git/**
.gitignore
node_modules/**
...
Assessing 3 file(s): src/index.js, src/utils.js, src/api.js

The Scanned … line reflects whichever manifest matched your stack — composer.json for PHP, Gemfile for Ruby, mix.exs for Elixir — and Using gitignore templates: lists the resolved templates accordingly (e.g. Composer, Laravel; Ruby, Rails; Elixir, community/Elixir/Phoenix).

If a file you expected to be assessed is missing from the Assessing N file(s) line, it was excluded — the logged pattern list shows exactly which patterns are active so you can identify the culprit and decide whether to add an override.

Troubleshooting

A file I want assessed isn't showing up.

Check the Exclude patterns applied list in the log. Find the pattern that matches your file and either:

  • Add it to exclude_pattern_overrides if you want just that file through.
  • Add the specific pattern to exclude_pattern_overrides if you want all files of that type through.

More files are being assessed than I want.

Add the unwanted files or directories to additional_exclude_patterns.

The auto-detected language looks wrong.

The Languages API reflects GitHub's language detection, which is based on file extensions and heuristics. If the wrong templates are applied you can verify by checking the Using gitignore templates: log line. Use additional_exclude_patterns to fill any gaps, or exclude_pattern_overrides to recover files incorrectly excluded by a mismatched template.

No files are being assessed at all.

When every changed file is removed by the patterns, the run reports All N changed file(s) were removed by the exclude patterns and writes a job summary listing the excluded files. Identify the over-broad pattern in the Exclude patterns applied log line, then use exclude_pattern_overrides to recover the files you need.

If the summary instead says the commit range contains no changed files, the patterns are not the cause — nothing was compared in the first place. See the FAQ entry for that case.

By default such a run still succeeds. Set fail_on_empty_assessment to 'true' to have it fail instead.