@tinyrack/devsync 1.0.0 → 1.0.3
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 +14 -0
- package/package.json +8 -5
- package/src/cli/sync-output.test.ts +105 -0
- package/src/config/sync.test.ts +609 -0
- package/src/lib/string.test.ts +13 -0
- package/src/lib/validation.test.ts +32 -0
- package/src/services/config-file.test.ts +161 -0
- package/src/services/crypto.test.ts +132 -0
- package/src/services/filesystem.test.ts +171 -0
- package/src/services/git.test.ts +83 -0
- package/src/services/init.test.ts +109 -0
- package/src/services/paths.test.ts +74 -0
- package/src/services/sync.dry-run.test.ts +179 -0
- package/src/services/sync.runtime.test.ts +756 -0
- package/src/services/sync.service.test.ts +1025 -0
- package/src/test/helpers/sync-fixture.ts +47 -0
package/README.md
CHANGED
|
@@ -53,6 +53,20 @@ If you want the `devsync` command available from this checkout during developmen
|
|
|
53
53
|
npm link
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## Release
|
|
57
|
+
|
|
58
|
+
- CI runs on every push and pull request with `npm run check` on Node.js 24.
|
|
59
|
+
- npm publishing runs automatically when a Git tag matching `v*.*.*` is pushed.
|
|
60
|
+
- The release workflow uses npm Trusted Publishing, so npm access is granted through GitHub Actions OIDC instead of an `NPM_TOKEN` secret.
|
|
61
|
+
- The release workflow fails if the pushed tag does not match `package.json` `version`.
|
|
62
|
+
|
|
63
|
+
Typical release flow:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm version patch
|
|
67
|
+
git push --follow-tags
|
|
68
|
+
```
|
|
69
|
+
|
|
56
70
|
## Storage Layout
|
|
57
71
|
|
|
58
72
|
- Sync repo: `~/.config/devsync/sync`
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinyrack/devsync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A personal CLI tool for git-backed configuration sync.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "winetree94",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://
|
|
10
|
+
"url": "git+https://github.com/tinyrack-net/devsync.git"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://
|
|
12
|
+
"homepage": "https://github.com/tinyrack-net/devsync#readme",
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://
|
|
14
|
+
"url": "https://github.com/tinyrack-net/devsync/issues"
|
|
15
15
|
},
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
"coverage": "vitest run --coverage",
|
|
63
63
|
"check": "npm run typecheck && biome check . && npm run test",
|
|
64
64
|
"check:fix": "biome check --write .",
|
|
65
|
-
"format": "biome format --write ."
|
|
65
|
+
"format": "biome format --write .",
|
|
66
|
+
"release:patch": "npm version patch -m \"chore(release): %s\"",
|
|
67
|
+
"release:minor": "npm version minor -m \"chore(release): %s\"",
|
|
68
|
+
"release:major": "npm version major -m \"chore(release): %s\""
|
|
66
69
|
},
|
|
67
70
|
"dependencies": {
|
|
68
71
|
"@oclif/core": "^4.9.0",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatSyncAddResult,
|
|
5
|
+
formatSyncForgetResult,
|
|
6
|
+
formatSyncInitResult,
|
|
7
|
+
formatSyncPullResult,
|
|
8
|
+
formatSyncPushResult,
|
|
9
|
+
formatSyncSetResult,
|
|
10
|
+
} from "#app/cli/sync-output.ts";
|
|
11
|
+
|
|
12
|
+
describe("sync output formatting", () => {
|
|
13
|
+
it("formats init results for cloned repositories", () => {
|
|
14
|
+
expect(
|
|
15
|
+
formatSyncInitResult({
|
|
16
|
+
alreadyInitialized: false,
|
|
17
|
+
configPath: "/tmp/sync/config.json",
|
|
18
|
+
entryCount: 2,
|
|
19
|
+
generatedIdentity: true,
|
|
20
|
+
gitAction: "cloned",
|
|
21
|
+
gitSource: "/tmp/remote",
|
|
22
|
+
identityFile: "/tmp/xdg/devsync/age/keys.txt",
|
|
23
|
+
recipientCount: 3,
|
|
24
|
+
ruleCount: 4,
|
|
25
|
+
syncDirectory: "/tmp/sync",
|
|
26
|
+
}),
|
|
27
|
+
).toBe(
|
|
28
|
+
[
|
|
29
|
+
"Initialized sync directory.",
|
|
30
|
+
"Sync directory: /tmp/sync",
|
|
31
|
+
"Config file: /tmp/sync/config.json",
|
|
32
|
+
"Age identity file: /tmp/xdg/devsync/age/keys.txt",
|
|
33
|
+
"Git repository: cloned from /tmp/remote",
|
|
34
|
+
"Age bootstrap: generated a new local identity.",
|
|
35
|
+
"Summary: 3 recipients, 2 entries, 4 rules.",
|
|
36
|
+
"",
|
|
37
|
+
].join("\n"),
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("formats add, forget, and set results", () => {
|
|
42
|
+
expect(
|
|
43
|
+
formatSyncAddResult({
|
|
44
|
+
alreadyTracked: false,
|
|
45
|
+
configPath: "/tmp/sync/config.json",
|
|
46
|
+
kind: "file",
|
|
47
|
+
localPath: "/tmp/home/.zshrc",
|
|
48
|
+
mode: "secret",
|
|
49
|
+
repoPath: ".zshrc",
|
|
50
|
+
syncDirectory: "/tmp/sync",
|
|
51
|
+
}),
|
|
52
|
+
).toContain("Added sync target.\n");
|
|
53
|
+
expect(
|
|
54
|
+
formatSyncForgetResult({
|
|
55
|
+
configPath: "/tmp/sync/config.json",
|
|
56
|
+
localPath: "/tmp/home/.zshrc",
|
|
57
|
+
plainArtifactCount: 1,
|
|
58
|
+
repoPath: ".zshrc",
|
|
59
|
+
secretArtifactCount: 2,
|
|
60
|
+
syncDirectory: "/tmp/sync",
|
|
61
|
+
}),
|
|
62
|
+
).toContain("Removed repo artifacts: 1 plain, 2 secret.\n");
|
|
63
|
+
expect(
|
|
64
|
+
formatSyncSetResult({
|
|
65
|
+
action: "updated",
|
|
66
|
+
configPath: "/tmp/sync/config.json",
|
|
67
|
+
entryRepoPath: "bundle",
|
|
68
|
+
localPath: "/tmp/home/bundle/private.json",
|
|
69
|
+
mode: "ignore",
|
|
70
|
+
repoPath: "bundle/private.json",
|
|
71
|
+
scope: "exact",
|
|
72
|
+
syncDirectory: "/tmp/sync",
|
|
73
|
+
}),
|
|
74
|
+
).toContain("Scope: exact rule\nAction: updated\n");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("formats push and pull dry-run summaries", () => {
|
|
78
|
+
expect(
|
|
79
|
+
formatSyncPushResult({
|
|
80
|
+
configPath: "/tmp/sync/config.json",
|
|
81
|
+
deletedArtifactCount: 4,
|
|
82
|
+
directoryCount: 1,
|
|
83
|
+
dryRun: true,
|
|
84
|
+
encryptedFileCount: 2,
|
|
85
|
+
plainFileCount: 3,
|
|
86
|
+
symlinkCount: 1,
|
|
87
|
+
syncDirectory: "/tmp/sync",
|
|
88
|
+
}),
|
|
89
|
+
).toContain("No filesystem changes were made.\n");
|
|
90
|
+
expect(
|
|
91
|
+
formatSyncPullResult({
|
|
92
|
+
configPath: "/tmp/sync/config.json",
|
|
93
|
+
decryptedFileCount: 2,
|
|
94
|
+
deletedLocalCount: 5,
|
|
95
|
+
directoryCount: 1,
|
|
96
|
+
dryRun: true,
|
|
97
|
+
plainFileCount: 3,
|
|
98
|
+
symlinkCount: 1,
|
|
99
|
+
syncDirectory: "/tmp/sync",
|
|
100
|
+
}),
|
|
101
|
+
).toContain(
|
|
102
|
+
"local paths would be removed.\nNo filesystem changes were made.\n",
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
});
|