Repository navigation
Add CI workflow with lint, tests, build and semgrep #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| # CI - the gate everything else keys off. No Claude, no tokens. | ||
| # Keep the workflow name "CI" and the job ids "ci" and "semgrep": branch | ||
| # protection requires both checks, and the review workflow triggers on | ||
| # workflow_run of the workflow named "CI". | ||
| # | ||
| # Lint / Test / Build below are this repo's real commands, verified locally | ||
| # 2026-08-20 before this file was written: | ||
| # Lint = frontend eslint -> 14 errors (pre-existing, see PR) | ||
| # Test = backend unittest, 23 tests -> passes (1 skipped: the live Gemini | ||
| # call, gated behind NUTRI_LIVE, which needs a paid key) | ||
| # Build = frontend vite build -> passes | ||
| # The project block in CLAUDE.md still says "no test suite yet" and gives | ||
| # py_compile as the backend check. That is stale - backend/tests/ exists and | ||
| # runs - so the real suite is wired here instead. | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
|
|
||
| concurrency: | ||
| group: ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| ci: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | ||
|
|
||
| # --- Python (backend/) ------------------------------------------------ | ||
| - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 | ||
| with: | ||
| enable-cache: true | ||
| - name: Install backend | ||
| run: | | ||
| uv venv .venv --python 3.11 | ||
| uv pip install -r backend/requirements.txt | ||
|
|
||
| # --- Node (frontend/) ------------------------------------------------- | ||
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
| cache-dependency-path: frontend/package-lock.json | ||
| - name: Install frontend | ||
| working-directory: frontend | ||
| run: npm ci | ||
|
|
||
| # --- The three commands ---------------------------------------------- | ||
| # `if: always()` on Test and Build is deliberate. Lint is red today (14 | ||
| # pre-existing errors), and without it the job would stop at Lint and | ||
| # tell us nothing about whether Test and Build pass in CI. All three run, | ||
| # the job still fails, and one PR shows the complete picture instead of | ||
| # three red cycles. Remove it once main is green if you prefer fail-fast. | ||
| - name: Lint | ||
| working-directory: frontend | ||
| run: npm run lint | ||
|
Comment on lines
+60
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Do not make the required The workflow documents 14 existing Fix the lint errors before requiring this check, or add a documented baseline that fails only on new lint errors. 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Test | ||
| if: always() | ||
| run: .venv/bin/python -m unittest discover -s backend/tests -t . -v | ||
|
|
||
| - name: Build | ||
| if: always() | ||
| working-directory: frontend | ||
|
Comment on lines
+64
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
file=".github/workflows/ci.yml"
sed -n '1,110p' "$file"
printf '\nRelevant expressions:\n'
rg -n -C 3 'always\(\)|cancelled\(\)|cancel-in-progress|Test|Build' "$file"Repository: MrTig-afk/NutritionalTracker Length of output: 5543 🌐 Web query:
💡 Result: In GitHub Actions, there are two distinct concepts related to cancellation: concurrency control (which determines if a workflow run is cancelled before it starts or while it is running) and the execution of steps within a job during cancellation [1][2][3]. Concurrency Control (cancel-in-progress) The Citations:
Skip superseded Test and Build steps after cancellation. With 🤖 Prompt for AI Agents |
||
| run: npm run build | ||
|
|
||
| semgrep: | ||
| runs-on: ubuntu-latest | ||
| container: | ||
| image: semgrep/semgrep | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow context ---'
sed -n '68,100p' .github/workflows/ci.yml
printf '%s\n' '--- all container image references in this workflow ---'
rg -n '(^|[[:space:]])(image|uses):' .github/workflows/ci.ymlRepository: MrTig-afk/NutritionalTracker Length of output: 1731 🏁 Script executed: #!/bin/bash
set -eu
token="$(
curl -fsSL \
'https://auth.docker.io/token?service=registry.docker.io&scope=repository:semgrep/semgrep:pull' |
jq -r .token
)"
curl -fsSL \
-H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json' \
'https://registry-1.docker.io/v2/semgrep/semgrep/manifests/latest' |
jq '{schemaVersion, mediaType, manifests}'Repository: MrTig-afk/NutritionalTracker Length of output: 1776 Pin the Semgrep image by digest. 🧰 Tools🪛 zizmor (1.29.0)[error] 76-76: unpinned image references (unpinned-images): container image is unpinned (unpinned-images) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| steps: | ||
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | ||
| # Rulesets are PINNED, not `--config auto`. `auto` resolves the registry | ||
| # at run time, so the rule set drifts underneath you: the same commit | ||
| # scanned twice can give different results, and a green main can go red | ||
| # overnight with no code change. That is disqualifying for a check that | ||
| # branch protection requires - the gate must mean the same thing tomorrow | ||
| # as it does today. Proven here on 2026-08-20: a local semgrep 1.173.0 | ||
| # run and this container's `auto` run returned different findings on the | ||
| # identical tree. | ||
| # Adding a ruleset is a deliberate edit to this line, and the PR that | ||
| # does it shows exactly what new class of finding was turned on. | ||
| - run: > | ||
| semgrep scan --error | ||
| --config p/python | ||
| --config p/javascript | ||
| --config p/secrets | ||
|
Comment on lines
+89
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow context ---'
sed -n '1,115p' .github/workflows/ci.yml
printf '%s\n' '--- Semgrep references ---'
rg -n --hidden -g '!node_modules' -g '!dist' -g '!build' 'semgrep|p/python|p/javascript|p/secrets|PINNED' .github README.md . 2>/dev/null | head -200Repository: MrTig-afk/NutritionalTracker Length of output: 5150 🌐 Web query:
💡 Result: Semgrep does not natively support an "immutable pin" syntax (e.g., version tagging or hash-based pinning) directly within the command-line interface when referencing Semgrep Registry rulesets like p/python, p/javascript, or p/secrets [1][2][3]. Registry rulesets are dynamically fetched from the Semgrep Registry and are managed by the Semgrep team, meaning they update automatically as new rules are added [1][2]. If you require immutable behavior for production or CI environments, the following approaches are standard practice: 1. Download and Version Control Rules: Manually download the ruleset file (e.g., via Citations:
🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from urllib.request import Request, urlopen
urls = [
"https://docs.semgrep.dev/running-rules",
"https://semgrep.dev/c/p/python",
"https://semgrep.dev/c/p/javascript",
"https://semgrep.dev/c/p/secrets",
]
for url in urls:
print(f"--- {url} ---")
try:
data = urlopen(Request(url, headers={"User-Agent": "Mozilla/5.0"}), timeout=15).read()
text = data.decode("utf-8", "replace")
print(f"status=ok bytes={len(data)}")
for term in ("p/<RULESET>", "Registry", "version", "ruleset"):
print(f"{term}: {text.lower().count(term.lower())}")
except Exception as exc:
print(f"status=error error={exc}")
PYRepository: MrTig-afk/NutritionalTracker Length of output: 951 Vendor the Semgrep rulesets and scan local, version-controlled YAML files.
🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Disable checkout credential persistence in both jobs.
Both checkout steps use the default persisted credential behavior. Neither job requires authenticated Git operations. Add
persist-credentials: falseat both sites. This is especially important because thecijob executes pull-request-controlled commands, while the Semgrep job passes the workspace to containerized tooling. (github.com).github/workflows/ci.yml#L33-L33: disable credential persistence before lint, test, and build steps..github/workflows/ci.yml#L78-L78: disable credential persistence before the Semgrep container runs.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 33-35: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
📍 Affects 1 file
.github/workflows/ci.yml#L33-L33(this comment).github/workflows/ci.yml#L78-L78🤖 Prompt for AI Agents