Conversation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis pull request updates the Copier template from v0.0.117 to v0.0.118, bumping numerous tool and dependency versions, restructuring the frontend Node.js module layout to the workspace root, refactoring pre-commit linting rules, adding a utility script for cleaning ChangesTemplate v0.0.118 Release with Infrastructure Updates
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
template/.devcontainer/install-ci-tooling.py.jinja (1)
106-119: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueShell=True with list argument on non-Windows path.
On non-Windows,
run_cmd = [cmd]creates a single-element list like["npm -v"]. When passed tosubprocess.run(..., shell=True), Python uses only the first element as the shell command string, which works but is unconventional. Consider using the string directly instead of wrapping in a list:♻️ Suggested simplification
run_cmd = ( [ pwsh, # type: ignore[reportPossiblyUnboundVariable] # this matches the conditional above that defines pwsh "-NoProfile", "-NonInteractive", "-Command", cmd, ] if is_windows - else [cmd] + else cmd ) - _ = subprocess.run(run_cmd, shell=True, check=True) # noqa: S602 # we need shell=True for npm commands, and this is all our own input + _ = subprocess.run(run_cmd, shell=not is_windows, check=True) # noqa: S602 # we need shell=True for npm commands on non-Windows, and this is all our own input🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/.devcontainer/install-ci-tooling.py.jinja` around lines 106 - 119, The loop builds run_cmd as a list for non-Windows which is unnecessary when calling subprocess.run(..., shell=True); update the assignment so that when is_windows is True you keep the pwsh list (pwsh, "-NoProfile", "-NonInteractive", "-Command", cmd) but when is_windows is False set run_cmd to the raw string cmd (not [cmd]); adjust the branch around pnpm_install_sequence, run_cmd, and the subprocess.run call so subprocess.run receives a string on non-Windows and retains the existing list form for Windows (referencing pnpm_install_sequence, run_cmd, is_windows, pwsh, and the subprocess.run invocation).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@template/.devcontainer/devcontainer.json.jinja`:
- Around line 29-34: The Node feature entry
("ghcr.io/devcontainers/features/node:2.0.0") is pinned to a non-existent 2.0.0
release and includes an unsupported "npmVersion" option; update the feature to a
released tag (e.g., change "version" to "1.7.1") and remove the "npmVersion" key
(keep "pnpmVersion" and "nvmVersion" as-is), or if you intentionally target a
future release, revert the tag back to a valid released version and drop
unsupported options until the feature adds "npmVersion".
In `@template/.github/workflows/confirm-on-tagged-copier-template.yaml`:
- Around line 24-25: The PATTERN variable is being set via direct expression
interpolation which risks shell injection; instead pass
inputs.answers_file_pattern into the job/step environment (env: PATTERN: ${{
inputs.answers_file_pattern }}) and then reference that env var in the shell
(leave the find | grep line using the shell $PATTERN) so the input is not
expanded by the shell before being assigned; ensure the step still populates
ANSWERS_FILES via mapfile -t ANSWERS_FILES < <(find . -type f -printf '%P\n' |
grep -E "$PATTERN") but obtain PATTERN from the environment rather than inline
expression.
In `@template/frontend/kiota_nullable_fixer.py`:
- Line 551: The print message in inject_missing_typescript_fields fires whenever
interface_props is non-empty even if no replacements occurred; fix this by
capturing original_content = content at the start of
inject_missing_typescript_fields, run the existing conditional replacements (the
checks for empty_interface in content, deser_func_idx != -1, etc.), then only
emit the print for model_name (and list fields.keys()) if content !=
original_content (i.e., replacements actually modified the text); this mirrors
the backend pattern and prevents false "Injected missing fields" messages.
- Line 555: Update the function signature for main to mark parameters as
keyword-only and add the explicit None return type: change def main(schema:
dict[str, Any] | None = None): to a signature using the keyword-only marker and
return annotation (i.e., include * before schema and add -> None). Keep the
parameter name and type (schema: dict[str, Any] | None = None) unchanged
otherwise and ensure the function body remains compatible with the new
signature.
- Around line 555-572: main() currently references the global MODELS_DIR (used
at "index_file = MODELS_DIR / 'index.ts'") which is only set inside the if
__name__ == "__main__" block, causing a NameError for external callers; change
main signature to accept a models_dir parameter (e.g., def main(schema:
dict[str, Any] | None = None, models_dir: Path | None = None)), use models_dir
or fallback to DEFAULT_MODELS_DIR when computing index_file, and remove or stop
relying on any global mutation of MODELS_DIR in the __main__ block (pass the
desired models_dir into main there instead).
In `@template/ruff-test.toml`:
- Around line 22-23: Update the per-file ignore pattern under
[lint.per-file-ignores] so the negated glob covers nested test directories:
replace the existing "!tests/**" entry with a recursive pattern like
"!**/tests/**" (i.e., change the key string currently written as "!tests/**" to
"!**/tests/**") to ensure INP001 is suppressed for any tests/ directory at any
depth while keeping the same ignore list.
---
Outside diff comments:
In `@template/.devcontainer/install-ci-tooling.py.jinja`:
- Around line 106-119: The loop builds run_cmd as a list for non-Windows which
is unnecessary when calling subprocess.run(..., shell=True); update the
assignment so that when is_windows is True you keep the pwsh list (pwsh,
"-NoProfile", "-NonInteractive", "-Command", cmd) but when is_windows is False
set run_cmd to the raw string cmd (not [cmd]); adjust the branch around
pnpm_install_sequence, run_cmd, and the subprocess.run call so subprocess.run
receives a string on non-Windows and retains the existing list form for Windows
(referencing pnpm_install_sequence, run_cmd, is_windows, pwsh, and the
subprocess.run invocation).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3da97f8b-763e-42a9-8466-9c21c28302c9
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (27)
.copier-answers.yml.devcontainer/devcontainer.json.devcontainer/install-ci-tooling.py.devcontainer/manual-setup-deps.py.github/dependabot.yml.github/workflows/confirm-on-tagged-copier-template.yaml.github/workflows/hash_git_files.py.github/workflows/replace_private_package_registries.py.pre-commit-config.yamlcopier.ymlextensions/context.pypyproject.tomlruff-test.tomlscripts/delete_false_positive_rej.pytemplate/.devcontainer/devcontainer.json.jinjatemplate/.devcontainer/docker-compose.yml.jinjatemplate/.devcontainer/install-ci-tooling.py.jinjatemplate/.devcontainer/manual-setup-deps.pytemplate/.github/dependabot.yml.jinjatemplate/.github/workflows/confirm-on-tagged-copier-template.yamltemplate/.github/workflows/hash_git_files.pytemplate/.pre-commit-config.yaml.jinjatemplate/frontend/kiota_nullable_fixer.pytemplate/pnpm-workspace.yamltemplate/ruff-test.tomltemplate/scripts/delete_false_positive_rej.pytemplate/{% if has_backend %}backend{% endif %}/kiota_nullable_fixer.py
| content = content[:early_idx] + filled_body + content[early_idx + len(early_return) :] | ||
|
|
||
| print(f" Injected missing fields into {model_name}: {', '.join(fields.keys())}") | ||
| print(f" Injected missing fields into {model_name}: {', '.join(fields.keys())}") # noqa: T201 # this just runs as a simple script, so using print instead of log |
There was a problem hiding this comment.
Print fires unconditionally even when injection patterns are not found.
inject_missing_typescript_fields prints "Injected missing fields into {model_name}" whenever interface_props is non-empty, but the actual string replacements at lines 527–549 are all conditional on finding specific patterns (empty_interface in content, deser_func_idx != -1, etc.). If those patterns are absent, nothing is modified but the message still claims injection occurred.
✏️ Suggested fix
+ if content == original_content:
+ return content
+ print(f" Injected missing fields into {model_name}: {', '.join(fields.keys())}") # noqa: T201 # this just runs as a simple script, so using print instead of log
return content(Requires capturing original_content = content at the start of the function, mirroring the backend counterpart's pattern.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/frontend/kiota_nullable_fixer.py` at line 551, The print message in
inject_missing_typescript_fields fires whenever interface_props is non-empty
even if no replacements occurred; fix this by capturing original_content =
content at the start of inject_missing_typescript_fields, run the existing
conditional replacements (the checks for empty_interface in content,
deser_func_idx != -1, etc.), then only emit the print for model_name (and list
fields.keys()) if content != original_content (i.e., replacements actually
modified the text); this mirrors the backend pattern and prevents false
"Injected missing fields" messages.
| return content | ||
|
|
||
|
|
||
| def main(schema: dict[str, Any] | None = None): |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Missing -> None return type and keyword-only parameter marker.
def main(schema: dict[str, Any] | None = None): lacks the return type annotation required by pyright and the * separator for keyword-only parameters required by the project style.
✏️ Proposed fix
-def main(schema: dict[str, Any] | None = None):
+def main(*, schema: dict[str, Any] | None = None) -> None:As per coding guidelines, "Always include type hints for pyright in Python" and "Prefer keyword-only parameters (unless a very clear single-argument function): use * in Python signatures."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def main(schema: dict[str, Any] | None = None): | |
| def main(*, schema: dict[str, Any] | None = None) -> None: |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/frontend/kiota_nullable_fixer.py` at line 555, Update the function
signature for main to mark parameters as keyword-only and add the explicit None
return type: change def main(schema: dict[str, Any] | None = None): to a
signature using the keyword-only marker and return annotation (i.e., include *
before schema and add -> None). Keep the parameter name and type (schema:
dict[str, Any] | None = None) unchanged otherwise and ensure the function body
remains compatible with the new signature.
| def main(schema: dict[str, Any] | None = None): | ||
| """Main function to fix TypeScript models. | ||
| """Fix nullable handling in generated TypeScript models. | ||
|
|
||
| Args: | ||
| schema: OpenAPI schema dict. If None, loads from default path for backward compatibility. | ||
| """ | ||
| if schema is None: | ||
| # Backward compatibility: load from default path if no schema provided | ||
| if not DEFAULT_OPENAPI_SCHEMA.exists(): | ||
| print(f"Error: Default OpenAPI schema file not found: {DEFAULT_OPENAPI_SCHEMA}") | ||
| print(f"Error: Default OpenAPI schema file not found: {DEFAULT_OPENAPI_SCHEMA}") # noqa: T201 # this just runs as a simple script, so using print instead of log | ||
| sys.exit(1) | ||
| with DEFAULT_OPENAPI_SCHEMA.open() as f: | ||
| schema = json.load(f) | ||
| if schema is None: | ||
| print("Error: Failed to load OpenAPI schema from default path") | ||
| print("Error: Failed to load OpenAPI schema from default path") # noqa: T201 # this just runs as a simple script, so using print instead of log | ||
| sys.exit(1) | ||
|
|
||
| index_file = MODELS_DIR / "index.ts" |
There was a problem hiding this comment.
NameError at runtime when main() is called outside __main__.
main() references MODELS_DIR at line 572, but the only assignment to MODELS_DIR is at line 647 inside the if __name__ == "__main__": block. The new schema parameter added to the main() signature explicitly invites external callers (e.g., from ... import main; main(schema)), who will hit a NameError immediately.
The fix is to either initialise MODELS_DIR at module level to DEFAULT_MODELS_DIR, or — cleaner — pass models_dir as a parameter to main().
🐛 Proposed fix: add `models_dir` parameter and remove global mutation
-def main(schema: dict[str, Any] | None = None):
+def main(*, schema: dict[str, Any] | None = None, models_dir: Path = DEFAULT_MODELS_DIR) -> None:
"""Fix nullable handling in generated TypeScript models.
Args:
schema: OpenAPI schema dict. If None, loads from default path for backward compatibility.
+ models_dir: Path to the directory containing generated Kiota models.
"""
if schema is None:
...
- index_file = MODELS_DIR / "index.ts"
+ index_file = models_dir / "index.ts"And in __main__:
- MODELS_DIR = models_dir
- main(openapi_schema)
+ main(schema=openapi_schema, models_dir=models_dir)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/frontend/kiota_nullable_fixer.py` around lines 555 - 572, main()
currently references the global MODELS_DIR (used at "index_file = MODELS_DIR /
'index.ts'") which is only set inside the if __name__ == "__main__" block,
causing a NameError for external callers; change main signature to accept a
models_dir parameter (e.g., def main(schema: dict[str, Any] | None = None,
models_dir: Path | None = None)), use models_dir or fallback to
DEFAULT_MODELS_DIR when computing index_file, and remove or stop relying on any
global mutation of MODELS_DIR in the __main__ block (pass the desired models_dir
into main there instead).
Why is this change necessary?
Keep up to date with upstream template
How does this change address the issue?
Fixes linting violations in kiota fixer scripts
Adds a pnpm-workspace.yaml file to support the new requirements of v11
What side effects does this change have?
N/A
How is this change tested?
Downstream repo
Summary by CodeRabbit
Release Notes