@savvy-web/cli 2.4.2 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/cli/index.js +1 -1
- package/commands/lint/fmt.js +18 -5
- package/commands/repos/commands/remove.js +4 -0
- package/commands/repos/commands/restore.js +2 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -59,7 +59,9 @@ npx savvy clean --globs dist,.turbo,coverage
|
|
|
59
59
|
|
|
60
60
|
## Vendored repos are read-only
|
|
61
61
|
|
|
62
|
-
`savvy repos sync`, `add`, `pin`, `remove`, `rename` and `restore` leave every vendored
|
|
62
|
+
`savvy repos sync`, `add`, `pin`, `remove`, `rename` and `restore` leave every vendored worktree under `.repos/` read-only at the OS level — files `0444`, directories `0555` (an executable file locks at `0555` instead, and unlocks back to `0755` rather than losing its executable bit). Reading a vendored source needs no extra step; writing to one fails with `EACCES` by design, because those trees mirror an upstream repo at a pinned ref and any local edit is lost on the next sync. A dirty tree from a hand bypass recovers with `savvy repos restore <name...>` (or with no names, every dirty tree) rather than a manual `git reset`. Restore is destructive by design: it hard-resets each tree it touches to the pinned commit, so uncommitted edits and untracked files in that tree are gone. When a reset runs but the worktree is still dirty afterwards, restore says so per repo and exits `1`, so a script cannot read an incomplete restore as a clean one.
|
|
63
|
+
|
|
64
|
+
The lock stops at the worktree. Each submodule's git metadata under `.git/modules/` stays writable, so a plain `git pull` and any GUI client that keeps its own per-gitdir state work normally in a repo that vendors sources this way. `sync` and `add` also declare the boundary to git rather than leaving clients to discover it by failing: they write `submodule.<path>.update = none` per entry and `fetch.recurseSubmodules = false` into the repo's local config, so ordinary git commands skip these trees. A `git checkout` inside a vendored worktree is therefore detected rather than blocked — `savvy repos status --drift` reports it and `savvy repos restore` repairs it.
|
|
63
65
|
|
|
64
66
|
```bash
|
|
65
67
|
npx savvy repos sync
|
package/cli/index.js
CHANGED
|
@@ -76,7 +76,7 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
|
|
|
76
76
|
* CLI application: reads argv from the Stdio service provided by NodeServices.
|
|
77
77
|
* (v4's `Command.run` takes only `version` — the name comes from the root command.)
|
|
78
78
|
*/
|
|
79
|
-
const cli = Command.run(rootCommand, { version: "2.
|
|
79
|
+
const cli = Command.run(rootCommand, { version: "2.5.1" });
|
|
80
80
|
/**
|
|
81
81
|
* Shared workspace services from `@effected/workspaces`, wired as a
|
|
82
82
|
* self-contained unit and built ONCE (layers memoize by reference).
|
package/commands/lint/fmt.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Lint } from "@savvy-web/silk-effects";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { Argument, Command } from "effect/unstable/cli";
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { Yaml } from "@effected/yaml";
|
|
6
6
|
|
|
@@ -17,6 +17,15 @@ import { Yaml } from "@effected/yaml";
|
|
|
17
17
|
*/
|
|
18
18
|
/** Repeated file path arguments. */
|
|
19
19
|
const filesArg = Argument.file("files", { mustExist: true }).pipe(Argument.variadic());
|
|
20
|
+
/**
|
|
21
|
+
* Formatting options forwarded from `Lint.Yaml.fmtCommand`.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* This subcommand runs in its own process, so options set on the lint-staged
|
|
25
|
+
* handler only reach the formatter across this flag. Omitting it would make
|
|
26
|
+
* `fmtCommand({ format })` and `create({ format })` write different bytes.
|
|
27
|
+
*/
|
|
28
|
+
const yamlFormatOption = Flag.string("format").pipe(Flag.withDescription("JSON-encoded @effected/yaml formatting options"), Flag.optional);
|
|
20
29
|
/** Sort package.json files with sort-package-json. */
|
|
21
30
|
const packageJsonCommand = Command.make("package-json", { files: filesArg }, ({ files }) => Effect.sync(() => {
|
|
22
31
|
for (const filepath of files) {
|
|
@@ -35,9 +44,13 @@ const pnpmWorkspaceCommand = Command.make("pnpm-workspace", {}, () => Effect.gen
|
|
|
35
44
|
const formatted = yield* Effect.promise(() => Lint.PnpmWorkspace.formatContent(sorted));
|
|
36
45
|
writeFileSync(filepath, formatted, "utf-8");
|
|
37
46
|
}));
|
|
38
|
-
/** Format YAML files with
|
|
39
|
-
const yamlCommand = Command.make("yaml", {
|
|
40
|
-
|
|
47
|
+
/** Format YAML files with @effected/yaml. */
|
|
48
|
+
const yamlCommand = Command.make("yaml", {
|
|
49
|
+
files: filesArg,
|
|
50
|
+
format: yamlFormatOption
|
|
51
|
+
}, ({ files, format }) => Effect.sync(() => {
|
|
52
|
+
const options = Option.isSome(format) ? Lint.Yaml.parseFormatOptions(format.value) : void 0;
|
|
53
|
+
for (const filepath of files) Lint.Yaml.formatFile(filepath, options);
|
|
41
54
|
}));
|
|
42
55
|
/** Parent fmt command with formatting subcommands. */
|
|
43
56
|
const _fmtCommand = Command.make("fmt").pipe(Command.withSubcommands([
|
|
@@ -47,6 +47,10 @@ const runReposRemove = (cwd, name) => Effect.gen(function* () {
|
|
|
47
47
|
yield* Effect.log(result.commitMessage);
|
|
48
48
|
yield* Effect.log("staged — review and commit");
|
|
49
49
|
for (const note of result.removedNotes) yield* Effect.log(`warning: note ${note.id} (${note.ref}) was removed with the entry — promote first if durable`);
|
|
50
|
+
if (result.removedEntry.orientation) {
|
|
51
|
+
yield* Effect.log(`warning: the orientation block for ${result.name} was removed with the entry and add will NOT restore it — re-vendoring? capture it now:`);
|
|
52
|
+
yield* Effect.log(JSON.stringify(result.removedEntry.orientation, null, 2));
|
|
53
|
+
}
|
|
50
54
|
}).pipe(Effect.catchTag("ReposConfigError", (error) => {
|
|
51
55
|
if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
|
|
52
56
|
process.exitCode = 1;
|
|
@@ -52,6 +52,8 @@ const runReposRestore = (cwd, names) => Effect.gen(function* () {
|
|
|
52
52
|
const result = yield* (yield* Repos.ReposManager).restore(cwd, names.length > 0 ? names : void 0);
|
|
53
53
|
for (const entry of result.restored) yield* Effect.log(`${entry.name}: restored to ${entry.commit}`);
|
|
54
54
|
for (const name of result.skippedClean) yield* Effect.log(`${name}: clean — skipped`);
|
|
55
|
+
for (const name of result.stillDirty) yield* Effect.log(`${name}: WARNING — reset ran but the worktree is STILL dirty; run \`savvy repos status --drift\``);
|
|
56
|
+
if (result.stillDirty.length > 0) process.exitCode = 1;
|
|
55
57
|
if (result.restored.length === 0 && result.skippedClean.length === 0) yield* Effect.log("nothing to restore");
|
|
56
58
|
}).pipe(Effect.catchTag("ReposConfigError", (error) => {
|
|
57
59
|
if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The savvy CLI — unified commit, changeset, and lint commands for the Silk Suite",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/cli",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@effected/git": "^0.7.0",
|
|
37
37
|
"@effected/jsonc": "^0.6.0",
|
|
38
38
|
"@effected/templates": "^0.2.0",
|
|
39
|
-
"@effected/workspaces": "^0.11.
|
|
40
|
-
"@effected/yaml": "^0.
|
|
41
|
-
"@savvy-web/silk-effects": "5.
|
|
39
|
+
"@effected/workspaces": "^0.11.2",
|
|
40
|
+
"@effected/yaml": "^0.8.0",
|
|
41
|
+
"@savvy-web/silk-effects": "5.7.0",
|
|
42
42
|
"effect": "4.0.0-beta.107"
|
|
43
43
|
}
|
|
44
44
|
}
|