Verify runtime version in fedify init - #981
Conversation
Fedify has minimum runtime versions, and some frameworks require even newer ones. `fedify init` did not check this, so it could scaffold a project on a runtime below the minimum, which can then fail at runtime. Verify the selected runtime version before creating any files. The base minimums (Deno 2.0.0, Node.js 22.0.0, Bun 1.1.0) mirror the values declared by `@fedify/fedify` and are defined directly in `rt.json`. Frameworks with stricter requirements, such as Astro's Node.js 22.12, raise the minimum through `minRuntimeVersions`. Assisted-by: Claude Code:claude-opus-4-8
Cover `verifyRuntimeVersion` for versions below, equal to, and above the minimum, and `resolveRequiredVersion` for framework overrides that raise the base minimum. Assisted-by: Claude Code:claude-opus-4-8
Assisted-by: Claude Code:claude-opus-4-8
✅ Deploy Preview for fedify-json-schema canceled.
|
📝 WalkthroughWalkthrough
ChangesRuntime verification
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant FedifyInit
participant fillPackageManager
participant checkAllRuntimes
participant RuntimeCommands
FedifyInit->>fillPackageManager: resolve package manager
fillPackageManager->>checkAllRuntimes: validate runtime requirements
checkAllRuntimes->>RuntimeCommands: execute version commands
RuntimeCommands-->>checkAllRuntimes: return runtime statuses
checkAllRuntimes-->>fillPackageManager: return compatible options
fillPackageManager-->>FedifyInit: continue or exit
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/init/src/ask/pm.ts`:
- Around line 40-64: Update the explicit package-manager branch before its
availability/runtime checks to validate isWfSupportsPm(options.webFramework,
pm), matching the interactive selection path. Reject unsupported combinations
using the existing error/exit behavior, while preserving the current checks and
successful return for compatible package managers.
- Around line 53-55: Update the result.status === "missing" branch to include
actionable runtime installation guidance, using runtimes[pmToRt(pm)].label and
an install URL. Extend the installer metadata or add a runtime helper to support
the getInstallUrl signature needed for this path, while preserving the existing
missing-runtime error message.
In `@packages/init/src/lib.ts`:
- Around line 221-224: Update the return type of checkRuntimeVersion to use the
imported RuntimeCheck type instead of redeclaring the equivalent union, matching
checkRuntimeRequirement and keeping the shared contract centralized.
- Around line 239-249: Update checkRuntimeVersion so unexpected runtime-check
errors are converted into the same result shape used for missing, unsupported,
and malformed runtimes instead of being rethrown. Ensure checkAllRuntimes and
askPackageManager handle that result by printing an actionable CLI message and
exiting cleanly, while preserving the existing not-found behavior.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6e67122f-54e4-4ac4-a1fb-5b08cbe4b156
📒 Files selected for processing (9)
CHANGES.mdchanges.d/init/verify-runtime-version.mdpackages/init/src/ask/pm.tspackages/init/src/const.tspackages/init/src/json/rt.jsonpackages/init/src/lib.test.tspackages/init/src/lib.tspackages/init/src/types.tspackages/init/src/webframeworks/astro.ts
2chanhaeng
left a comment
There was a problem hiding this comment.
Great work! Thanks for your contribution to Fedify. Before merging this PR, some changes are needed, and I have left suggestions to make your PR better. Please read them and consider applying them!
| const reason = !isWfSupportsPm(wf, value) | ||
| ? `not supported with ${webFrameworks[wf].label}` | ||
| : check.status === "unsupported" | ||
| ? `requires ${label} ${check.required} or later` | ||
| : check.status === "missing" | ||
| ? `requires ${label} which is not installed` | ||
| : check.status === "malformed" | ||
| ? `could not detect ${label} version` | ||
| : ""; | ||
| const disabled = !isWfSupportsPm(wf, value) || check.status !== "ok"; | ||
| return { | ||
| name: disabled ? `${value} (${reason})` : value, | ||
| value, | ||
| disabled, | ||
| }; |
There was a problem hiding this comment.
It would be better const reason = /* ... */ : ""; return disabled === "" ? { name, value } : { name, value, disabled } because select from @inquirer/prompts show disabled if it is string.
`askPackageManager` recomputed the runtime checks through `checkAllRuntimes` every time it built the prompt. Following the review, that computation was separated into `calculateChoices` to run once. When `fillPackageManager` already has a `packageManager`, it now looks the value up in `choices` instead of calling `checkRuntimeRequirement` again. `checkRuntimeRequirement` is no longer used outside `lib.ts`, so its export was removed. Assisted-by: Claude Code:claude-opus-4-8
When the chosen package manager was not installed, `fillPackageManager` recursed to prompt again, recomputing `calculateChoices` on every retry, so replace the recursion with a loop that reuses the `choices` computed once at the start. Assisted-by: Claude Code:claude-opus-4-8
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
CHANGES.md (1)
183-195: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCorrect the PR reference.
Line 183 escapes the closing citation bracket. Line 195 links PR
#981as an issue. Remove the escape and change the reference URL tohttps://github.com/fedify-dev/fedify/pull/981.Based on PR objectives: this change is PR
#981.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@CHANGES.md` around lines 183 - 195, In CHANGES.md, correct the PR `#981` citation by removing the unnecessary escape from its closing bracket, and update the `#981` reference URL from the issues endpoint to the pull endpoint. Leave the other changelog references unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@CHANGES.md`:
- Around line 183-195: In CHANGES.md, correct the PR `#981` citation by removing
the unnecessary escape from its closing bracket, and update the `#981` reference
URL from the issues endpoint to the pull endpoint. Leave the other changelog
references unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 408742d3-7138-47d9-9b17-033403cf539d
📒 Files selected for processing (4)
CHANGES.mdchanges.d/init/verify-runtime-version.mdpackages/init/src/ask/pm.tspackages/init/src/lib.ts
|
I applied changes. Could you take another look? |
Codecov Report❌ Patch coverage is
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
2chanhaeng
left a comment
There was a problem hiding this comment.
OK, you've addressed my comments well, but it seems there are still some areas for improvement. Could you take a look at these points?
`checkRuntimeRequirement` only forwarded to `checkRuntimeVersion`, so it was inlined into `checkAllRuntimes` to make the chain simpler. Assisted-by: Claude Code:claude-opus-4-8
`choices` recorded each runtime's version but not whether a package manager's binary was installed, so `fillPackageManager` checked the binary separately and looped to retry. Move that check into `choicePackageManager` so `choices` also reflects binary availability. `fillPackageManager` then trusts `choices` and drops the re-check, the retry `while` loop, and the `pm` alias. A disabled choice falls back to `askPackageManager` instead of exiting. Assisted-by: Claude Code:claude-opus-4-8
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/init/src/ask/pm.ts (1)
61-68: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winReport the runtime failure details when all choices are disabled.
The error only states that no supported package manager is available. It omits the disabled reasons already computed for each choice. A user cannot identify the missing runtime, malformed version output, or required minimum version.
Include the relevant disabled reason or reasons in this exit path. The PR requires clear errors for missing, malformed, and unsupported runtimes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/init/src/ask/pm.ts` around lines 61 - 68, Update the all-disabled branch after the PACKAGE_MANAGER choices are computed to include each disabled choice’s existing reason in the printErrorMessage output. Preserve the current package-manager availability message while reporting missing, malformed, and unsupported runtime details from the choice objects before exiting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/init/src/ask/pm.ts`:
- Around line 42-52: The explicit disabled package-manager path in the async
package-manager prompt must terminate after printing the error, regardless of
test mode. Update the branch handling choice.disabled in the packageManager
selection flow to exit unconditionally, while preserving the existing successful
return and interactive prompt behavior.
---
Outside diff comments:
In `@packages/init/src/ask/pm.ts`:
- Around line 61-68: Update the all-disabled branch after the PACKAGE_MANAGER
choices are computed to include each disabled choice’s existing reason in the
printErrorMessage output. Preserve the current package-manager availability
message while reporting missing, malformed, and unsupported runtime details from
the choice objects before exiting.
🪄 Autofix
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: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 02b29fbf-5fc3-4845-abb8-1beb9e08cfb3
📒 Files selected for processing (2)
packages/init/src/ask/pm.tspackages/init/src/lib.ts
| async ({ packageManager, ...options }) => { | ||
| const pm = packageManager ?? await askPackageManager(options.webFramework); | ||
| if (await isPackageManagerAvailable(pm)) { | ||
| return ({ ...options, packageManager: pm }); | ||
| const choices = await calculateChoices(options.webFramework); | ||
| if (packageManager != null) { | ||
| const choice = choices.find(({ value }) => value === packageManager)!; | ||
| if (choice.disabled == null) { | ||
| return { ...options, packageManager }; | ||
| } | ||
| print(message`${optionName(choice.name)} ${text(choice.disabled)}`); | ||
| if (isTest(options)) process.exit(1); | ||
| } | ||
| noticeInstallUrl(pm); | ||
| return await fillPackageManager(options) as // | ||
| typeof options & { packageManager: PackageManager }; | ||
| return { ...options, packageManager: await askPackageManager(choices) }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the source and propagation of testMode.
rg -n -C4 '\btestMode\b' packages/init/src packages/cli/src
# Locate package-manager option parsing and fillPackageManager callers.
rg -n -C4 'fillPackageManager\s*\(|packageManager|package-manager' \
packages/init/src packages/cli/src --glob '*.ts'Repository: fedify-dev/fedify
Length of output: 50374
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- initialization command entry ---'
sed -n '1,120p' packages/init/src/action/mod.ts
printf '%s\n' '--- package-manager choice logic ---'
sed -n '1,180p' packages/init/src/ask/pm.ts
printf '%s\n' '--- command registration and init invocation ---'
rg -n -C6 'runInit|initOptions|test-init|FEDIFY_TEST_MODE|action' packages/init/src packages/cli/src --glob '*.ts' \
| head -n 320
printf '%s\n' '--- CLI package entry files ---'
git ls-files packages/cli packages/init | rg '(^|/)(main|mod|command|cli|index)\.ts$|action/mod\.ts$'Repository: fedify-dev/fedify
Length of output: 27400
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- every FEDIFY_TEST_MODE reference ---'
rg -n -C5 'FEDIFY_TEST_MODE|runTestInit|test-init' . \
--glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' \
| head -n 240
printf '%s\n' '--- normal init parser and registration ---'
sed -n '55,115p' packages/cli/src/commands.ts
sed -n '85,155p' packages/init/src/command.ts
printf '%s\n' '--- test-init action ---'
sed -n '1,180p' packages/init/src/test/action.ts
sed -n '1,100p' packages/init/src/test/mod.ts
printf '%s\n' '--- package scripts and executable entrypoints ---'
rg -n -C3 '"(init|test-init|fedify)|FEDIFY_' package.json packages/*/package.json deno.json \
--glob 'package.json' --glob 'deno.json' 2>/dev/null | head -n 240Repository: fedify-dev/fedify
Length of output: 19461
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
import re
action = Path("packages/init/src/action/mod.ts").read_text()
pm = Path("packages/init/src/ask/pm.ts").read_text()
commands = Path("packages/cli/src/commands.ts").read_text()
deno = Path("packages/init/deno.json").read_text()
checks = {
"normal init delegates to runInit": re.search(
r'path:\s*\["init"\].*?run:\s*runInit', commands, re.S
),
"runInit derives testMode from FEDIFY_TEST_MODE": re.search(
r'setTestMode.*?Boolean\(process\.env\["FEDIFY_TEST_MODE"\]\)', action, re.S
),
"disabled explicit selection exits only in test mode": re.search(
r'print\(message.*?if\s*\(isTest\(options\)\)\s*process\.exit\(1\)', pm, re.S
),
"disabled explicit selection falls through to prompt": re.search(
r'if\s*\(isTest\(options\)\)\s*process\.exit\(1\).*?return .*?askPackageManager\(choices\)', pm, re.S
),
"internal test-init task sets test mode": "FEDIFY_TEST_MODE=true" in deno,
}
for name, matched in checks.items():
print(f"{name}: {'yes' if matched else 'no'}")
assert all(checks.values())
PYRepository: fedify-dev/fedify
Length of output: 403
Exit when an explicitly selected package manager is disabled. Normal fedify init does not set FEDIFY_TEST_MODE, so the disabled selection falls through to askPackageManager(choices) instead of exiting with the displayed error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/init/src/ask/pm.ts` around lines 42 - 52, The explicit disabled
package-manager path in the async package-manager prompt must terminate after
printing the error, regardless of test mode. Update the branch handling
choice.disabled in the packageManager selection flow to exit unconditionally,
while preserving the existing successful return and interactive prompt behavior.
Summary
Added runtime version verification to fedify init to enhance runtime safety by pre-checking minimum requirements.
Related issue
fedify init#964Changes
minVersion) tojson/rt.json, derived aRUNTIMElist inconst.ts, and addedRuntime/Runtimes/RuntimeChecktypes.verifyRuntimeVersion,checkRuntimeVersion,resolveRequiredVersion,checkRuntimeRequirement, andcheckAllRuntimestolib.ts.minRuntimeVersionsfield toWebFrameworkDescriptionand set Astro's Node.js 22.12 requirement.ask/pm.tsto exit with an actionable error for an unsupported runtime in non-interactive mode, and to disable the affected package managers in the interactive menu.verifyRuntimeVersionandresolveRequiredVersion.AI disclosure
Claude Code (
claude-opus-4-8) assisted with this change: it proposed the design and drafted much of the implementation, tests, and this description. I reviewed and edited the work, made the design decisions, and verified the behaviour myself by runningfedify initand the test suite.