@tinyrack/devsync 1.1.0 → 1.2.2
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 +230 -62
- package/dist/cli/base-command.d.ts +14 -0
- package/dist/cli/base-command.d.ts.map +1 -0
- package/dist/cli/base-command.js +22 -0
- package/dist/cli/base-command.js.map +1 -0
- package/dist/cli/commands/dir.d.ts +8 -0
- package/dist/cli/commands/dir.d.ts.map +1 -0
- package/dist/cli/commands/dir.js +16 -0
- package/dist/cli/commands/dir.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +18 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.d.ts +23 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +23 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +43 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/machine/list.d.ts +7 -0
- package/dist/cli/commands/machine/list.d.ts.map +1 -0
- package/dist/cli/commands/machine/list.js +12 -0
- package/dist/cli/commands/machine/list.js.map +1 -0
- package/dist/cli/commands/machine/use.d.ts +11 -0
- package/dist/cli/commands/machine/use.d.ts.map +1 -0
- package/dist/cli/commands/machine/use.js +28 -0
- package/dist/cli/commands/machine/use.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +12 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +34 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/push.d.ts +12 -0
- package/dist/cli/commands/push.d.ts.map +1 -0
- package/dist/cli/commands/push.js +34 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/status.d.ts +11 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +27 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/track.d.ts +16 -0
- package/dist/cli/commands/track.d.ts.map +1 -0
- package/dist/cli/commands/track.js +82 -0
- package/dist/cli/commands/track.js.map +1 -0
- package/dist/cli/commands/untrack.d.ts +11 -0
- package/dist/cli/commands/untrack.d.ts.map +1 -0
- package/dist/cli/commands/untrack.js +28 -0
- package/dist/cli/commands/untrack.js.map +1 -0
- package/dist/config/global-config.d.ts +21 -0
- package/dist/config/global-config.d.ts.map +1 -0
- package/dist/config/global-config.js +106 -0
- package/dist/config/global-config.js.map +1 -0
- package/dist/config/sync.d.ts +96 -0
- package/dist/config/sync.d.ts.map +1 -0
- package/dist/config/sync.js +412 -0
- package/dist/config/sync.js.map +1 -0
- package/dist/config/xdg.d.ts +11 -0
- package/dist/config/xdg.d.ts.map +1 -0
- package/dist/config/xdg.js +79 -0
- package/dist/config/xdg.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -1
- package/dist/index.js.map +1 -0
- package/dist/lib/file-mode.d.ts +3 -0
- package/dist/lib/file-mode.d.ts.map +1 -0
- package/dist/lib/file-mode.js +7 -0
- package/dist/lib/file-mode.js.map +1 -0
- package/dist/lib/output.d.ts +31 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +198 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/path.d.ts +5 -0
- package/dist/lib/path.d.ts.map +1 -0
- package/dist/lib/path.js +25 -0
- package/dist/lib/path.js.map +1 -0
- package/dist/lib/string.d.ts +2 -0
- package/dist/lib/string.d.ts.map +1 -0
- package/dist/lib/string.js +4 -0
- package/dist/lib/string.js.map +1 -0
- package/dist/lib/validation.d.ts +3 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +9 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/services/add.d.ts +20 -0
- package/dist/services/add.d.ts.map +1 -0
- package/dist/services/add.js +161 -0
- package/dist/services/add.js.map +1 -0
- package/dist/services/config-file.d.ts +30 -0
- package/dist/services/config-file.d.ts.map +1 -0
- package/dist/services/config-file.js +34 -0
- package/dist/services/config-file.js.map +1 -0
- package/dist/services/crypto.d.ts +9 -0
- package/dist/services/crypto.d.ts.map +1 -0
- package/dist/services/crypto.js +75 -0
- package/dist/services/crypto.js.map +1 -0
- package/dist/services/doctor.d.ts +16 -0
- package/dist/services/doctor.d.ts.map +1 -0
- package/dist/services/doctor.js +84 -0
- package/dist/services/doctor.js.map +1 -0
- package/dist/services/error.d.ts +14 -0
- package/dist/services/error.d.ts.map +1 -0
- package/dist/services/error.js +38 -0
- package/dist/services/error.js.map +1 -0
- package/dist/services/filesystem.d.ts +15 -0
- package/dist/services/filesystem.d.ts.map +1 -0
- package/dist/services/filesystem.js +113 -0
- package/dist/services/filesystem.js.map +1 -0
- package/dist/services/forget.d.ts +14 -0
- package/dist/services/forget.d.ts.map +1 -0
- package/dist/services/forget.js +124 -0
- package/dist/services/forget.js.map +1 -0
- package/dist/services/git.d.ts +10 -0
- package/dist/services/git.d.ts.map +1 -0
- package/dist/services/git.js +57 -0
- package/dist/services/git.js.map +1 -0
- package/dist/services/init.d.ts +19 -0
- package/dist/services/init.d.ts.map +1 -0
- package/dist/services/init.js +203 -0
- package/dist/services/init.js.map +1 -0
- package/dist/services/local-materialization.d.ts +28 -0
- package/dist/services/local-materialization.d.ts.map +1 -0
- package/dist/services/local-materialization.js +262 -0
- package/dist/services/local-materialization.js.map +1 -0
- package/dist/services/local-snapshot.d.ts +25 -0
- package/dist/services/local-snapshot.d.ts.map +1 -0
- package/dist/services/local-snapshot.js +93 -0
- package/dist/services/local-snapshot.js.map +1 -0
- package/dist/services/machine.d.ts +40 -0
- package/dist/services/machine.d.ts.map +1 -0
- package/dist/services/machine.js +113 -0
- package/dist/services/machine.js.map +1 -0
- package/dist/services/paths.d.ts +12 -0
- package/dist/services/paths.d.ts.map +1 -0
- package/dist/services/paths.js +71 -0
- package/dist/services/paths.js.map +1 -0
- package/dist/services/pull.d.ts +28 -0
- package/dist/services/pull.d.ts.map +1 -0
- package/dist/services/pull.js +67 -0
- package/dist/services/pull.js.map +1 -0
- package/dist/services/push.d.ts +35 -0
- package/dist/services/push.d.ts.map +1 -0
- package/dist/services/push.js +96 -0
- package/dist/services/push.js.map +1 -0
- package/dist/services/repo-artifacts.d.ts +52 -0
- package/dist/services/repo-artifacts.d.ts.map +1 -0
- package/dist/services/repo-artifacts.js +251 -0
- package/dist/services/repo-artifacts.js.map +1 -0
- package/dist/services/repo-snapshot.d.ts +6 -0
- package/dist/services/repo-snapshot.d.ts.map +1 -0
- package/dist/services/repo-snapshot.js +163 -0
- package/dist/services/repo-snapshot.js.map +1 -0
- package/dist/services/runtime.d.ts +40 -0
- package/dist/services/runtime.d.ts.map +1 -0
- package/dist/services/runtime.js +71 -0
- package/dist/services/runtime.js.map +1 -0
- package/dist/services/set.d.ts +38 -0
- package/dist/services/set.d.ts.map +1 -0
- package/dist/services/set.js +184 -0
- package/dist/services/set.js.map +1 -0
- package/dist/services/status.d.ts +30 -0
- package/dist/services/status.d.ts.map +1 -0
- package/dist/services/status.js +35 -0
- package/dist/services/status.js.map +1 -0
- package/package.json +15 -7
- package/src/cli/commands/add.ts +0 -40
- package/src/cli/commands/cd.ts +0 -80
- package/src/cli/commands/doctor.ts +0 -20
- package/src/cli/commands/forget.ts +0 -32
- package/src/cli/commands/index.ts +0 -23
- package/src/cli/commands/init.ts +0 -43
- package/src/cli/commands/list.ts +0 -17
- package/src/cli/commands/pull.ts +0 -31
- package/src/cli/commands/push.ts +0 -31
- package/src/cli/commands/set.ts +0 -47
- package/src/cli/commands/status.ts +0 -18
- package/src/cli/sync-output.test.ts +0 -173
- package/src/cli/sync-output.ts +0 -200
- package/src/config/sync.test.ts +0 -609
- package/src/config/sync.ts +0 -572
- package/src/config/xdg.ts +0 -138
- package/src/lib/string.test.ts +0 -13
- package/src/lib/string.ts +0 -3
- package/src/lib/validation.test.ts +0 -32
- package/src/lib/validation.ts +0 -11
- package/src/services/add.ts +0 -178
- package/src/services/config-file.test.ts +0 -161
- package/src/services/config-file.ts +0 -101
- package/src/services/crypto.test.ts +0 -132
- package/src/services/crypto.ts +0 -83
- package/src/services/doctor.ts +0 -142
- package/src/services/error.ts +0 -6
- package/src/services/filesystem.test.ts +0 -171
- package/src/services/filesystem.ts +0 -183
- package/src/services/forget.ts +0 -261
- package/src/services/git.test.ts +0 -83
- package/src/services/git.ts +0 -74
- package/src/services/init.test.ts +0 -109
- package/src/services/init.ts +0 -244
- package/src/services/list.ts +0 -63
- package/src/services/local-materialization.ts +0 -421
- package/src/services/local-snapshot.ts +0 -173
- package/src/services/paths.test.ts +0 -74
- package/src/services/paths.ts +0 -98
- package/src/services/pull.ts +0 -144
- package/src/services/push.ts +0 -168
- package/src/services/repo-artifacts.ts +0 -262
- package/src/services/repo-snapshot.ts +0 -197
- package/src/services/runtime.ts +0 -57
- package/src/services/set.ts +0 -383
- package/src/services/status.ts +0 -57
- package/src/services/sync.dry-run.test.ts +0 -179
- package/src/services/sync.runtime.test.ts +0 -756
- package/src/services/sync.service.test.ts +0 -1169
- package/src/test/helpers/sync-fixture.ts +0 -47
package/src/services/set.ts
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
findOwningSyncEntry,
|
|
5
|
-
type ResolvedSyncConfigEntry,
|
|
6
|
-
type ResolvedSyncOverride,
|
|
7
|
-
readSyncConfig,
|
|
8
|
-
resolveEntryRelativeRepoPath,
|
|
9
|
-
resolveRelativeSyncMode,
|
|
10
|
-
type SyncMode,
|
|
11
|
-
} from "#app/config/sync.ts";
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
createSyncConfigDocument,
|
|
15
|
-
createSyncConfigDocumentEntry,
|
|
16
|
-
sortSyncConfigEntries,
|
|
17
|
-
writeValidatedSyncConfig,
|
|
18
|
-
} from "./config-file.ts";
|
|
19
|
-
import { DevsyncError } from "./error.ts";
|
|
20
|
-
import { getPathStats } from "./filesystem.ts";
|
|
21
|
-
import {
|
|
22
|
-
buildRepoPathWithinRoot,
|
|
23
|
-
isExplicitLocalPath,
|
|
24
|
-
resolveCommandTargetPath,
|
|
25
|
-
tryBuildRepoPathWithinRoot,
|
|
26
|
-
tryNormalizeRepoPathInput,
|
|
27
|
-
} from "./paths.ts";
|
|
28
|
-
import { ensureSyncRepository, type SyncContext } from "./runtime.ts";
|
|
29
|
-
|
|
30
|
-
export type SyncSetRequest = Readonly<{
|
|
31
|
-
recursive: boolean;
|
|
32
|
-
state: SyncMode;
|
|
33
|
-
target: string;
|
|
34
|
-
}>;
|
|
35
|
-
|
|
36
|
-
type SyncSetScope = "default" | "exact" | "subtree";
|
|
37
|
-
type SyncSetAction = "added" | "removed" | "unchanged" | "updated";
|
|
38
|
-
|
|
39
|
-
export type SyncSetResult = Readonly<{
|
|
40
|
-
action: SyncSetAction;
|
|
41
|
-
configPath: string;
|
|
42
|
-
entryRepoPath: string;
|
|
43
|
-
localPath: string;
|
|
44
|
-
mode: SyncMode;
|
|
45
|
-
repoPath: string;
|
|
46
|
-
scope: SyncSetScope;
|
|
47
|
-
syncDirectory: string;
|
|
48
|
-
}>;
|
|
49
|
-
|
|
50
|
-
const resolveTargetPath = async (
|
|
51
|
-
target: string,
|
|
52
|
-
entry: ResolvedSyncConfigEntry,
|
|
53
|
-
context: Pick<SyncContext, "cwd" | "environment" | "paths">,
|
|
54
|
-
) => {
|
|
55
|
-
if (isExplicitLocalPath(target)) {
|
|
56
|
-
const localPath = resolveCommandTargetPath(
|
|
57
|
-
target,
|
|
58
|
-
context.environment,
|
|
59
|
-
context.cwd,
|
|
60
|
-
);
|
|
61
|
-
const stats = await getPathStats(localPath);
|
|
62
|
-
|
|
63
|
-
if (stats === undefined) {
|
|
64
|
-
throw new DevsyncError(`Sync set target does not exist: ${localPath}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
localPath,
|
|
69
|
-
repoPath: buildRepoPathWithinRoot(
|
|
70
|
-
localPath,
|
|
71
|
-
context.paths.homeDirectory,
|
|
72
|
-
"Sync set target",
|
|
73
|
-
),
|
|
74
|
-
stats,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const repoPath = tryNormalizeRepoPathInput(target);
|
|
79
|
-
|
|
80
|
-
if (repoPath === undefined) {
|
|
81
|
-
throw new DevsyncError(
|
|
82
|
-
`Sync set target must be a local path or repository path: ${target}`,
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const relativePath = resolveEntryRelativeRepoPath(entry, repoPath);
|
|
87
|
-
const localPath =
|
|
88
|
-
relativePath === undefined || relativePath === ""
|
|
89
|
-
? entry.localPath
|
|
90
|
-
: join(entry.localPath, ...relativePath.split("/"));
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
localPath,
|
|
94
|
-
repoPath,
|
|
95
|
-
stats: await getPathStats(localPath),
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const resolveSetTarget = async (
|
|
100
|
-
target: string,
|
|
101
|
-
config: Awaited<ReturnType<typeof readSyncConfig>>,
|
|
102
|
-
context: Pick<SyncContext, "cwd" | "environment" | "paths">,
|
|
103
|
-
) => {
|
|
104
|
-
const trimmedTarget = target.trim();
|
|
105
|
-
|
|
106
|
-
if (trimmedTarget.length === 0) {
|
|
107
|
-
throw new DevsyncError("Target path is required.");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const homeDirectory = context.paths.homeDirectory;
|
|
111
|
-
const explicitLocalPath = isExplicitLocalPath(trimmedTarget);
|
|
112
|
-
const localTargetPath = resolveCommandTargetPath(
|
|
113
|
-
trimmedTarget,
|
|
114
|
-
context.environment,
|
|
115
|
-
context.cwd,
|
|
116
|
-
);
|
|
117
|
-
const localRepoPath = explicitLocalPath
|
|
118
|
-
? buildRepoPathWithinRoot(localTargetPath, homeDirectory, "Sync set target")
|
|
119
|
-
: tryBuildRepoPathWithinRoot(
|
|
120
|
-
localTargetPath,
|
|
121
|
-
homeDirectory,
|
|
122
|
-
"Sync set target",
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
if (localRepoPath !== undefined) {
|
|
126
|
-
const localStats = await getPathStats(localTargetPath);
|
|
127
|
-
|
|
128
|
-
if (explicitLocalPath && localStats === undefined) {
|
|
129
|
-
throw new DevsyncError(
|
|
130
|
-
`Sync set target does not exist: ${localTargetPath}`,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const entry = findOwningSyncEntry(config, localRepoPath);
|
|
135
|
-
|
|
136
|
-
if (entry?.kind === "directory") {
|
|
137
|
-
const relativePath = resolveEntryRelativeRepoPath(entry, localRepoPath);
|
|
138
|
-
|
|
139
|
-
if (relativePath !== undefined) {
|
|
140
|
-
return {
|
|
141
|
-
entry,
|
|
142
|
-
localPath: localTargetPath,
|
|
143
|
-
relativePath,
|
|
144
|
-
repoPath: localRepoPath,
|
|
145
|
-
stats: localStats,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (explicitLocalPath) {
|
|
151
|
-
throw new DevsyncError(
|
|
152
|
-
`Sync set target must be inside a tracked directory entry: ${trimmedTarget}`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const repoPath = tryNormalizeRepoPathInput(trimmedTarget);
|
|
158
|
-
|
|
159
|
-
if (repoPath === undefined) {
|
|
160
|
-
throw new DevsyncError(
|
|
161
|
-
`Sync set target must be a local path or repository path: ${trimmedTarget}`,
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const entry = findOwningSyncEntry(config, repoPath);
|
|
166
|
-
|
|
167
|
-
if (entry === undefined || entry.kind !== "directory") {
|
|
168
|
-
throw new DevsyncError(
|
|
169
|
-
`Sync set target must be inside a tracked directory entry: ${trimmedTarget}`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const resolvedTarget = await resolveTargetPath(trimmedTarget, entry, context);
|
|
174
|
-
const relativePath = resolveEntryRelativeRepoPath(
|
|
175
|
-
entry,
|
|
176
|
-
resolvedTarget.repoPath,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
if (relativePath === undefined) {
|
|
180
|
-
throw new DevsyncError(
|
|
181
|
-
`Sync set target must be inside a tracked directory entry: ${trimmedTarget}`,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
entry,
|
|
187
|
-
localPath: resolvedTarget.localPath,
|
|
188
|
-
relativePath,
|
|
189
|
-
repoPath: resolvedTarget.repoPath,
|
|
190
|
-
stats: resolvedTarget.stats,
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const updateEntryMode = (
|
|
195
|
-
entry: ResolvedSyncConfigEntry,
|
|
196
|
-
mode: SyncMode,
|
|
197
|
-
): {
|
|
198
|
-
action: SyncSetAction;
|
|
199
|
-
entry: ResolvedSyncConfigEntry;
|
|
200
|
-
} => {
|
|
201
|
-
if (entry.mode === mode) {
|
|
202
|
-
return {
|
|
203
|
-
action: "unchanged",
|
|
204
|
-
entry,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
action: "updated",
|
|
210
|
-
entry: {
|
|
211
|
-
...entry,
|
|
212
|
-
mode,
|
|
213
|
-
},
|
|
214
|
-
};
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const updateChildOverride = (
|
|
218
|
-
entry: ResolvedSyncConfigEntry,
|
|
219
|
-
input: Readonly<{
|
|
220
|
-
match: Extract<SyncSetScope, "exact" | "subtree">;
|
|
221
|
-
mode: SyncMode;
|
|
222
|
-
relativePath: string;
|
|
223
|
-
}>,
|
|
224
|
-
): {
|
|
225
|
-
action: SyncSetAction;
|
|
226
|
-
entry: ResolvedSyncConfigEntry;
|
|
227
|
-
} => {
|
|
228
|
-
const existingOverride = entry.overrides.find((override) => {
|
|
229
|
-
return (
|
|
230
|
-
override.match === input.match && override.path === input.relativePath
|
|
231
|
-
);
|
|
232
|
-
});
|
|
233
|
-
const remainingOverrides = entry.overrides.filter((override) => {
|
|
234
|
-
return !(
|
|
235
|
-
override.match === input.match && override.path === input.relativePath
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
|
-
const inheritedMode = resolveRelativeSyncMode(
|
|
239
|
-
entry.mode,
|
|
240
|
-
remainingOverrides,
|
|
241
|
-
input.relativePath,
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
if (input.mode === inheritedMode) {
|
|
245
|
-
if (existingOverride === undefined) {
|
|
246
|
-
return {
|
|
247
|
-
action: "unchanged",
|
|
248
|
-
entry,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
action: "removed",
|
|
254
|
-
entry: {
|
|
255
|
-
...entry,
|
|
256
|
-
overrides: remainingOverrides,
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (existingOverride?.mode === input.mode) {
|
|
262
|
-
return {
|
|
263
|
-
action: "unchanged",
|
|
264
|
-
entry,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const nextOverride = {
|
|
269
|
-
match: input.match,
|
|
270
|
-
mode: input.mode,
|
|
271
|
-
path: input.relativePath,
|
|
272
|
-
} satisfies ResolvedSyncOverride;
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
action: existingOverride === undefined ? "added" : "updated",
|
|
276
|
-
entry: {
|
|
277
|
-
...entry,
|
|
278
|
-
overrides: [...remainingOverrides, nextOverride],
|
|
279
|
-
},
|
|
280
|
-
};
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
export const setSyncTargetMode = async (
|
|
284
|
-
request: SyncSetRequest,
|
|
285
|
-
context: SyncContext,
|
|
286
|
-
): Promise<SyncSetResult> => {
|
|
287
|
-
await ensureSyncRepository(context);
|
|
288
|
-
|
|
289
|
-
const config = await readSyncConfig(
|
|
290
|
-
context.paths.syncDirectory,
|
|
291
|
-
context.environment,
|
|
292
|
-
);
|
|
293
|
-
const target = await resolveSetTarget(request.target, config, context);
|
|
294
|
-
|
|
295
|
-
if (target.relativePath === "") {
|
|
296
|
-
if (!request.recursive) {
|
|
297
|
-
throw new DevsyncError(
|
|
298
|
-
"Tracked directory roots require --recursive to update the entry mode.",
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const update = updateEntryMode(target.entry, request.state);
|
|
303
|
-
const nextConfig = createSyncConfigDocument(config);
|
|
304
|
-
|
|
305
|
-
nextConfig.entries = sortSyncConfigEntries(
|
|
306
|
-
nextConfig.entries.map((entry) => {
|
|
307
|
-
if (entry.repoPath !== target.entry.repoPath) {
|
|
308
|
-
return entry;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return createSyncConfigDocumentEntry(update.entry);
|
|
312
|
-
}),
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
if (update.action !== "unchanged") {
|
|
316
|
-
await writeValidatedSyncConfig(context.paths.syncDirectory, nextConfig, {
|
|
317
|
-
environment: context.environment,
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
action: update.action,
|
|
323
|
-
configPath: context.paths.configPath,
|
|
324
|
-
entryRepoPath: target.entry.repoPath,
|
|
325
|
-
localPath: target.localPath,
|
|
326
|
-
mode: request.state,
|
|
327
|
-
repoPath: target.repoPath,
|
|
328
|
-
scope: "default",
|
|
329
|
-
syncDirectory: context.paths.syncDirectory,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (target.stats?.isDirectory() && !request.recursive) {
|
|
334
|
-
throw new DevsyncError(
|
|
335
|
-
"Directory targets require --recursive. Use a file path for exact overrides.",
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
request.recursive &&
|
|
341
|
-
target.stats !== undefined &&
|
|
342
|
-
!target.stats.isDirectory()
|
|
343
|
-
) {
|
|
344
|
-
throw new DevsyncError(
|
|
345
|
-
"--recursive can only be used with directories or tracked directory roots.",
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const scope = request.recursive ? "subtree" : "exact";
|
|
350
|
-
const update = updateChildOverride(target.entry, {
|
|
351
|
-
match: scope,
|
|
352
|
-
mode: request.state,
|
|
353
|
-
relativePath: target.relativePath,
|
|
354
|
-
});
|
|
355
|
-
const nextConfig = createSyncConfigDocument(config);
|
|
356
|
-
|
|
357
|
-
nextConfig.entries = sortSyncConfigEntries(
|
|
358
|
-
nextConfig.entries.map((entry) => {
|
|
359
|
-
if (entry.repoPath !== target.entry.repoPath) {
|
|
360
|
-
return entry;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return createSyncConfigDocumentEntry(update.entry);
|
|
364
|
-
}),
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
if (update.action !== "unchanged") {
|
|
368
|
-
await writeValidatedSyncConfig(context.paths.syncDirectory, nextConfig, {
|
|
369
|
-
environment: context.environment,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return {
|
|
374
|
-
action: update.action,
|
|
375
|
-
configPath: context.paths.configPath,
|
|
376
|
-
entryRepoPath: target.entry.repoPath,
|
|
377
|
-
localPath: target.localPath,
|
|
378
|
-
mode: request.state,
|
|
379
|
-
repoPath: target.repoPath,
|
|
380
|
-
scope,
|
|
381
|
-
syncDirectory: context.paths.syncDirectory,
|
|
382
|
-
};
|
|
383
|
-
};
|
package/src/services/status.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { readSyncConfig } from "#app/config/sync.ts";
|
|
2
|
-
|
|
3
|
-
import { countConfiguredRules } from "./config-file.ts";
|
|
4
|
-
import {
|
|
5
|
-
buildPullPlan,
|
|
6
|
-
buildPullPlanPreview,
|
|
7
|
-
buildPullResultFromPlan,
|
|
8
|
-
} from "./pull.ts";
|
|
9
|
-
import {
|
|
10
|
-
buildPushPlan,
|
|
11
|
-
buildPushPlanPreview,
|
|
12
|
-
buildPushResultFromPlan,
|
|
13
|
-
} from "./push.ts";
|
|
14
|
-
import { ensureSyncRepository, type SyncContext } from "./runtime.ts";
|
|
15
|
-
|
|
16
|
-
export type SyncStatusResult = Readonly<{
|
|
17
|
-
configPath: string;
|
|
18
|
-
entryCount: number;
|
|
19
|
-
pull: ReturnType<typeof buildPullResultFromPlan> & {
|
|
20
|
-
preview: readonly string[];
|
|
21
|
-
};
|
|
22
|
-
push: ReturnType<typeof buildPushResultFromPlan> & {
|
|
23
|
-
preview: readonly string[];
|
|
24
|
-
};
|
|
25
|
-
recipientCount: number;
|
|
26
|
-
ruleCount: number;
|
|
27
|
-
syncDirectory: string;
|
|
28
|
-
}>;
|
|
29
|
-
|
|
30
|
-
export const getSyncStatus = async (
|
|
31
|
-
context: SyncContext,
|
|
32
|
-
): Promise<SyncStatusResult> => {
|
|
33
|
-
await ensureSyncRepository(context);
|
|
34
|
-
|
|
35
|
-
const config = await readSyncConfig(
|
|
36
|
-
context.paths.syncDirectory,
|
|
37
|
-
context.environment,
|
|
38
|
-
);
|
|
39
|
-
const pushPlan = await buildPushPlan(config, context);
|
|
40
|
-
const pullPlan = await buildPullPlan(config, context);
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
configPath: context.paths.configPath,
|
|
44
|
-
entryCount: config.entries.length,
|
|
45
|
-
pull: {
|
|
46
|
-
...buildPullResultFromPlan(pullPlan, context, true),
|
|
47
|
-
preview: buildPullPlanPreview(pullPlan),
|
|
48
|
-
},
|
|
49
|
-
push: {
|
|
50
|
-
...buildPushResultFromPlan(pushPlan, context, true),
|
|
51
|
-
preview: buildPushPlanPreview(pushPlan),
|
|
52
|
-
},
|
|
53
|
-
recipientCount: config.age.recipients.length,
|
|
54
|
-
ruleCount: countConfiguredRules(config),
|
|
55
|
-
syncDirectory: context.paths.syncDirectory,
|
|
56
|
-
};
|
|
57
|
-
};
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
-
|
|
6
|
-
import { syncSecretArtifactSuffix } from "#app/config/sync.ts";
|
|
7
|
-
import { addSyncTarget } from "#app/services/add.ts";
|
|
8
|
-
import { initializeSync } from "#app/services/init.ts";
|
|
9
|
-
import { pullSync } from "#app/services/pull.ts";
|
|
10
|
-
import { pushSync } from "#app/services/push.ts";
|
|
11
|
-
import { createSyncContext } from "#app/services/runtime.ts";
|
|
12
|
-
import { setSyncTargetMode } from "#app/services/set.ts";
|
|
13
|
-
import {
|
|
14
|
-
createAgeKeyPair,
|
|
15
|
-
createTemporaryDirectory,
|
|
16
|
-
writeIdentityFile,
|
|
17
|
-
} from "../test/helpers/sync-fixture.ts";
|
|
18
|
-
|
|
19
|
-
const temporaryDirectories: string[] = [];
|
|
20
|
-
|
|
21
|
-
const createWorkspace = async () => {
|
|
22
|
-
const directory = await createTemporaryDirectory("devsync-dry-run-");
|
|
23
|
-
|
|
24
|
-
temporaryDirectories.push(directory);
|
|
25
|
-
|
|
26
|
-
return directory;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const createEnvironment = (
|
|
30
|
-
homeDirectory: string,
|
|
31
|
-
xdgConfigHome: string,
|
|
32
|
-
): NodeJS.ProcessEnv => {
|
|
33
|
-
return {
|
|
34
|
-
HOME: homeDirectory,
|
|
35
|
-
XDG_CONFIG_HOME: xdgConfigHome,
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
afterEach(async () => {
|
|
40
|
-
while (temporaryDirectories.length > 0) {
|
|
41
|
-
const directory = temporaryDirectories.pop();
|
|
42
|
-
|
|
43
|
-
if (directory !== undefined) {
|
|
44
|
-
await rm(directory, { force: true, recursive: true });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("sync dry runs", () => {
|
|
50
|
-
it("reports push changes without mutating repository artifacts", async () => {
|
|
51
|
-
const workspace = await createWorkspace();
|
|
52
|
-
const homeDirectory = join(workspace, "home");
|
|
53
|
-
const xdgConfigHome = join(workspace, "xdg");
|
|
54
|
-
const bundleDirectory = join(homeDirectory, "bundle");
|
|
55
|
-
const plainFile = join(bundleDirectory, "plain.txt");
|
|
56
|
-
const secretFile = join(bundleDirectory, "token.txt");
|
|
57
|
-
const ageKeys = await createAgeKeyPair();
|
|
58
|
-
const context = createSyncContext({
|
|
59
|
-
environment: createEnvironment(homeDirectory, xdgConfigHome),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
await writeIdentityFile(xdgConfigHome, ageKeys.identity);
|
|
63
|
-
await mkdir(bundleDirectory, { recursive: true });
|
|
64
|
-
await writeFile(plainFile, "plain\n", "utf8");
|
|
65
|
-
await writeFile(secretFile, "secret\n", "utf8");
|
|
66
|
-
|
|
67
|
-
await initializeSync(
|
|
68
|
-
{
|
|
69
|
-
identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
|
|
70
|
-
recipients: [ageKeys.recipient],
|
|
71
|
-
},
|
|
72
|
-
context,
|
|
73
|
-
);
|
|
74
|
-
await addSyncTarget(
|
|
75
|
-
{
|
|
76
|
-
secret: false,
|
|
77
|
-
target: bundleDirectory,
|
|
78
|
-
},
|
|
79
|
-
context,
|
|
80
|
-
);
|
|
81
|
-
await setSyncTargetMode(
|
|
82
|
-
{
|
|
83
|
-
recursive: false,
|
|
84
|
-
state: "secret",
|
|
85
|
-
target: secretFile,
|
|
86
|
-
},
|
|
87
|
-
context,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const result = await pushSync(
|
|
91
|
-
{
|
|
92
|
-
dryRun: true,
|
|
93
|
-
},
|
|
94
|
-
context,
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
expect(result.dryRun).toBe(true);
|
|
98
|
-
expect(result.directoryCount).toBe(1);
|
|
99
|
-
expect(result.plainFileCount).toBe(1);
|
|
100
|
-
expect(result.encryptedFileCount).toBe(1);
|
|
101
|
-
await expect(
|
|
102
|
-
readFile(
|
|
103
|
-
join(xdgConfigHome, "devsync", "sync", "files", "bundle", "plain.txt"),
|
|
104
|
-
"utf8",
|
|
105
|
-
),
|
|
106
|
-
).rejects.toMatchObject({
|
|
107
|
-
code: "ENOENT",
|
|
108
|
-
});
|
|
109
|
-
await expect(
|
|
110
|
-
readFile(
|
|
111
|
-
join(
|
|
112
|
-
xdgConfigHome,
|
|
113
|
-
"devsync",
|
|
114
|
-
"sync",
|
|
115
|
-
"files",
|
|
116
|
-
"bundle",
|
|
117
|
-
`token.txt${syncSecretArtifactSuffix}`,
|
|
118
|
-
),
|
|
119
|
-
"utf8",
|
|
120
|
-
),
|
|
121
|
-
).rejects.toMatchObject({
|
|
122
|
-
code: "ENOENT",
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("reports pull changes without mutating local files", async () => {
|
|
127
|
-
const workspace = await createWorkspace();
|
|
128
|
-
const homeDirectory = join(workspace, "home");
|
|
129
|
-
const xdgConfigHome = join(workspace, "xdg");
|
|
130
|
-
const bundleDirectory = join(homeDirectory, "bundle");
|
|
131
|
-
const plainFile = join(bundleDirectory, "plain.txt");
|
|
132
|
-
const extraFile = join(bundleDirectory, "extra.txt");
|
|
133
|
-
const ageKeys = await createAgeKeyPair();
|
|
134
|
-
const context = createSyncContext({
|
|
135
|
-
environment: createEnvironment(homeDirectory, xdgConfigHome),
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
await writeIdentityFile(xdgConfigHome, ageKeys.identity);
|
|
139
|
-
await mkdir(bundleDirectory, { recursive: true });
|
|
140
|
-
await writeFile(plainFile, "plain\n", "utf8");
|
|
141
|
-
|
|
142
|
-
await initializeSync(
|
|
143
|
-
{
|
|
144
|
-
identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
|
|
145
|
-
recipients: [ageKeys.recipient],
|
|
146
|
-
},
|
|
147
|
-
context,
|
|
148
|
-
);
|
|
149
|
-
await addSyncTarget(
|
|
150
|
-
{
|
|
151
|
-
secret: false,
|
|
152
|
-
target: bundleDirectory,
|
|
153
|
-
},
|
|
154
|
-
context,
|
|
155
|
-
);
|
|
156
|
-
await pushSync(
|
|
157
|
-
{
|
|
158
|
-
dryRun: false,
|
|
159
|
-
},
|
|
160
|
-
context,
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
await writeFile(plainFile, "changed locally\n", "utf8");
|
|
164
|
-
await writeFile(extraFile, "leave me\n", "utf8");
|
|
165
|
-
|
|
166
|
-
const result = await pullSync(
|
|
167
|
-
{
|
|
168
|
-
dryRun: true,
|
|
169
|
-
},
|
|
170
|
-
context,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
expect(result.dryRun).toBe(true);
|
|
174
|
-
expect(result.plainFileCount).toBe(1);
|
|
175
|
-
expect(result.deletedLocalCount).toBeGreaterThanOrEqual(1);
|
|
176
|
-
expect(await readFile(plainFile, "utf8")).toBe("changed locally\n");
|
|
177
|
-
expect(await readFile(extraFile, "utf8")).toBe("leave me\n");
|
|
178
|
-
});
|
|
179
|
-
});
|