From e3e9ea51a43366dac86b98874d4feac71512a19c Mon Sep 17 00:00:00 2001 From: fru1tworld Date: Fri, 22 May 2026 13:52:49 +0900 Subject: [PATCH 1/2] Add --skip-install option to fedify init --- CHANGES.md | 10 +++++++++ docs/cli.md | 22 ++++++++++++++++++++ packages/init/src/action/configs.test.ts | 5 +++++ packages/init/src/action/mod.ts | 7 ++++++- packages/init/src/action/notice.ts | 10 +++++++++ packages/init/src/action/patch.test.ts | 1 + packages/init/src/action/utils.ts | 5 +++++ packages/init/src/command.ts | 4 ++++ packages/init/src/package.test.ts | 1 + packages/init/src/skip-install.test.ts | 26 ++++++++++++++++++++++++ packages/init/src/webframeworks.test.ts | 4 ++++ 11 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 packages/init/src/skip-install.test.ts diff --git a/CHANGES.md b/CHANGES.md index 9398d5286..6e44d9440 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -265,6 +265,16 @@ To be released. - Added `SqliteMessageQueue.getDepth()` for reporting queued, ready, and delayed message counts. [[#735], [#748]] +### @fedify/init + + - Added a `--skip-install` option to `fedify init` that skips automatic + dependency installation after scaffolding. This is useful for CI + environments, monorepo workspaces that install dependencies from the + root, or when you want to inspect the generated files before + installing. [[#720]] + +[#720]: https://github.com/fedify-dev/fedify/issues/720 + ### Claude Code plugin - Added a Claude Code plugin at *claude-plugin/*, installable with: diff --git a/docs/cli.md b/docs/cli.md index a724338f2..394ebf845 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -372,6 +372,28 @@ the selected framework scaffolder accepts them. Some scaffolders, such as own confirmation prompt, while a freshly initialized *.git* directory remains acceptable. +### `--skip-install`: Skip installing dependencies + +*This option is available since Fedify 2.3.0.* + +By default, `fedify init` runs the selected package manager's install command +right after scaffolding the project. The `--skip-install` option scaffolds the +files without running install, which is useful when: + + - installation is handled separately in a CI pipeline; + - the new project lives inside a monorepo whose dependencies are installed + from the workspace root; or + - you want to inspect the generated files before installing. + +~~~~ sh +fedify init my-fedify-project --skip-install +~~~~ + +After scaffolding, `fedify init` prints the command to run to install +dependencies later. Other steps such as creating files, applying patches, and +running the framework-specific scaffolder (e.g., *create-next-app*) still +happen as usual; only the final install step is skipped. + `fedify lookup`: Looking up an ActivityPub object ------------------------------------------------- diff --git a/packages/init/src/action/configs.test.ts b/packages/init/src/action/configs.test.ts index ef6190fcd..00ecba5c8 100644 --- a/packages/init/src/action/configs.test.ts +++ b/packages/init/src/action/configs.test.ts @@ -21,6 +21,7 @@ function createInitData(): InitCommandData { messageQueue: "denokv", dryRun: false, allowNonEmpty: false, + skipInstall: false, testMode: false, dir: "/tmp/example", initializer: { @@ -166,6 +167,7 @@ async function createNpmInitData(dir: string): Promise { messageQueue: "in-process", dryRun: false, allowNonEmpty: false, + skipInstall: false, testMode: false, dir, }); @@ -179,6 +181,7 @@ async function createNpmInitData(dir: string): Promise { messageQueue: "in-process", dryRun: false, allowNonEmpty: false, + skipInstall: false, testMode: false, dir, initializer, @@ -199,6 +202,7 @@ async function createNuxtNpmInitData(dir: string): Promise { messageQueue: "in-process", dryRun: false, allowNonEmpty: false, + skipInstall: false, testMode: false, dir, }); @@ -212,6 +216,7 @@ async function createNuxtNpmInitData(dir: string): Promise { messageQueue: "in-process", dryRun: false, allowNonEmpty: false, + skipInstall: false, testMode: false, dir, initializer, diff --git a/packages/init/src/action/mod.ts b/packages/init/src/action/mod.ts index 79ce01550..cb21536df 100644 --- a/packages/init/src/action/mod.ts +++ b/packages/init/src/action/mod.ts @@ -11,6 +11,7 @@ import { noticeHowToRun, noticeOptions, noticePrecommand, + noticeSkippedInstall, } from "./notice.ts"; import { assertNoGeneratedFileConflicts, @@ -23,6 +24,7 @@ import { hasCommand, installDependencies, isDry, + isSkipInstall, runPrecommand, } from "./utils.ts"; @@ -40,6 +42,8 @@ import { * - Otherwise, executes `handleHydRun`. * 7. Recommends configuration environment via `recommendConfigEnv`. * 8. Shows how to run the project via `noticeHowToRun`. + * 9. If installation was skipped and not a dry run, prints how to install + * dependencies manually via `noticeSkippedInstall`. */ const runInit = (options: InitCommand) => pipe( @@ -53,6 +57,7 @@ const runInit = (options: InitCommand) => unless(isDry, handleHydRun), tap(recommendConfigEnv), tap(noticeHowToRun), + tap(unless(isDry, when(isSkipInstall, noticeSkippedInstall))), ); export default runInit; @@ -76,5 +81,5 @@ const handleHydRun = (data: InitCommandData) => tap(assertNoGeneratedFileConflicts), tap(when(hasCommand, runPrecommand)), tap(patchFiles), - tap(installDependencies), + tap(unless(isSkipInstall, installDependencies)), ); diff --git a/packages/init/src/action/notice.ts b/packages/init/src/action/notice.ts index 4fe343069..e2d527205 100644 --- a/packages/init/src/action/notice.ts +++ b/packages/init/src/action/notice.ts @@ -117,6 +117,16 @@ ${instruction} Start by editing the ${text(federationFile)} file to define your federation! `; +/** Prints a notice that dependency installation was skipped and how to install them manually. */ +export const noticeSkippedInstall = ( + { packageManager }: InitCommandData, +) => + printMessage` +Dependencies were not installed. Run ${ + text(`${packageManager} install`) + } in the project directory to install them. +`; + /** * Returns an error handler that prints a formatted error message when * a dependency installation command fails, then throws. diff --git a/packages/init/src/action/patch.test.ts b/packages/init/src/action/patch.test.ts index 77bd6c2da..3c6c7d4a2 100644 --- a/packages/init/src/action/patch.test.ts +++ b/packages/init/src/action/patch.test.ts @@ -74,6 +74,7 @@ function createInitData( messageQueue: "in-process", dryRun: false, allowNonEmpty, + skipInstall: false, testMode: false, dir, initializer: { diff --git a/packages/init/src/action/utils.ts b/packages/init/src/action/utils.ts index 019dddb71..88dd1be22 100644 --- a/packages/init/src/action/utils.ts +++ b/packages/init/src/action/utils.ts @@ -5,6 +5,11 @@ import type { InitCommandData } from "../types.ts"; /** Returns `true` if the current run is in dry-run mode. */ export const isDry = ({ dryRun }: InitCommandData) => dryRun; +/** Returns `true` if the current run skips dependency installation. */ +export const isSkipInstall = ( + { skipInstall }: Pick, +) => skipInstall; + /** Returns `true` if the framework initializer has a precommand to execute. */ export const hasCommand = (data: InitCommandData) => !!data.initializer.command; diff --git a/packages/init/src/command.ts b/packages/init/src/command.ts index e2b610648..374340c64 100644 --- a/packages/init/src/command.ts +++ b/packages/init/src/command.ts @@ -77,6 +77,10 @@ export const initOptions = object("Initialization options", { description: message`Allow initializing in a non-empty directory when the selected framework scaffolder supports it, failing if any generated file already exists.`, }), + skipInstall: option("--skip-install", { + description: + message`Skip installing dependencies after scaffolding the project.`, + }), }); /** diff --git a/packages/init/src/package.test.ts b/packages/init/src/package.test.ts index 6e545b7fe..97a33247b 100644 --- a/packages/init/src/package.test.ts +++ b/packages/init/src/package.test.ts @@ -46,6 +46,7 @@ test( dir: packageDir, dryRun: true, allowNonEmpty: false, + skipInstall: false, kvStore: "in-memory", messageQueue: "in-process", packageManager: "bun", diff --git a/packages/init/src/skip-install.test.ts b/packages/init/src/skip-install.test.ts new file mode 100644 index 000000000..ab46482a4 --- /dev/null +++ b/packages/init/src/skip-install.test.ts @@ -0,0 +1,26 @@ +import { parse } from "@optique/core/parser"; +import { ok, strictEqual } from "node:assert/strict"; +import test from "node:test"; +import { isSkipInstall } from "./action/utils.ts"; +import { initOptions } from "./command.ts"; + +test("initOptions parses --skip-install as true", () => { + const result = parse(initOptions, ["--skip-install"]); + ok(result.success); + if (result.success) { + strictEqual(result.value.skipInstall, true); + } +}); + +test("initOptions defaults skipInstall to false when the flag is absent", () => { + const result = parse(initOptions, []); + ok(result.success); + if (result.success) { + strictEqual(result.value.skipInstall, false); + } +}); + +test("isSkipInstall mirrors the skipInstall field", () => { + strictEqual(isSkipInstall({ skipInstall: false }), false); + strictEqual(isSkipInstall({ skipInstall: true }), true); +}); diff --git a/packages/init/src/webframeworks.test.ts b/packages/init/src/webframeworks.test.ts index 4ce04fdc5..e0b1c1bee 100644 --- a/packages/init/src/webframeworks.test.ts +++ b/packages/init/src/webframeworks.test.ts @@ -17,6 +17,7 @@ test("Nitro template loads LogTape during server startup", async () => { testMode: false, dryRun: true, allowNonEmpty: false, + skipInstall: false, }); ok(files); @@ -38,6 +39,7 @@ test("Next.js template loads LogTape through instrumentation", async () => { testMode: false, dryRun: true, allowNonEmpty: false, + skipInstall: false, }); ok(files); @@ -61,6 +63,7 @@ test("Astro template loads LogTape through middleware", async () => { testMode: false, dryRun: true, allowNonEmpty: false, + skipInstall: false, }); ok(files); @@ -81,6 +84,7 @@ test("SolidStart template loads LogTape through middleware", async () => { testMode: false, dryRun: true, allowNonEmpty: false, + skipInstall: false, }); ok(files); From c5dd7346873e5d09fccba610c9c6069282f248bf Mon Sep 17 00:00:00 2001 From: fru1tworld Date: Fri, 22 May 2026 16:00:35 +0900 Subject: [PATCH 2/2] Credit PR #776 and author in CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6e44d9440..d5344a401 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -271,9 +271,10 @@ To be released. dependency installation after scaffolding. This is useful for CI environments, monorepo workspaces that install dependencies from the root, or when you want to inspect the generated files before - installing. [[#720]] + installing. [[#720], [#776] by fru1tworld] [#720]: https://github.com/fedify-dev/fedify/issues/720 +[#776]: https://github.com/fedify-dev/fedify/pull/776 ### Claude Code plugin