@shellus/way 0.5.1 → 0.5.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 CHANGED
@@ -67,8 +67,14 @@
67
67
 
68
68
  ## 依赖
69
69
 
70
- - [restic](https://restic.net/) - 备份引擎
71
70
  - Node.js >= 18
71
+ - Linux x64 平台内置 [restic](https://restic.net/) 0.16.4,其他平台需自行安装 restic
72
+
73
+ `way` 查找 restic 的顺序:
74
+
75
+ 1. `WAY_RESTIC_BIN` 指定的二进制
76
+ 2. Linux x64 包内置的 restic
77
+ 3. 系统 `PATH` 中的 `restic`
72
78
 
73
79
  ## 安装
74
80
 
@@ -76,6 +82,12 @@
76
82
  npm install -g @shellus/way
77
83
  ```
78
84
 
85
+ Linux x64 用户无需额外安装 restic。如需使用自定义 restic,可设置:
86
+
87
+ ```bash
88
+ WAY_RESTIC_BIN=/usr/local/bin/restic way snapshots
89
+ ```
90
+
79
91
  ## 快速开始
80
92
 
81
93
  ### 1. 初始化配置
package/dist/cli.js CHANGED
@@ -23,6 +23,38 @@ function loadConfig(wayDir, remoteName) {
23
23
 
24
24
  // src/core/restic.ts
25
25
  import { execa } from "execa";
26
+
27
+ // src/core/restic-bin.ts
28
+ import fs2 from "fs";
29
+ import path2 from "path";
30
+ import { fileURLToPath } from "url";
31
+ function getBundledResticBin(packageRoot) {
32
+ return path2.join(packageRoot, "vendor/restic/linux-x64/restic");
33
+ }
34
+ function findPackageRoot(startDir = path2.dirname(fileURLToPath(import.meta.url))) {
35
+ let current = startDir;
36
+ while (true) {
37
+ if (fs2.existsSync(path2.join(current, "package.json"))) return current;
38
+ const parent = path2.dirname(current);
39
+ if (parent === current) return startDir;
40
+ current = parent;
41
+ }
42
+ }
43
+ function resolveResticBin(options = {}) {
44
+ const env = options.env ?? process.env;
45
+ const platform = options.platform ?? process.platform;
46
+ const arch = options.arch ?? process.arch;
47
+ const packageRoot = options.packageRoot ?? findPackageRoot();
48
+ const existsSync = options.existsSync ?? fs2.existsSync;
49
+ if (env.WAY_RESTIC_BIN) return env.WAY_RESTIC_BIN;
50
+ if (platform === "linux" && arch === "x64") {
51
+ const bundled = getBundledResticBin(packageRoot);
52
+ if (existsSync(bundled)) return bundled;
53
+ }
54
+ return "restic";
55
+ }
56
+
57
+ // src/core/restic.ts
26
58
  function buildResticEnv(repo) {
27
59
  const env = {};
28
60
  switch (repo.type) {
@@ -57,11 +89,11 @@ function buildS3Options(repo) {
57
89
  }
58
90
  async function execRestic(args, env, s3Options = []) {
59
91
  try {
60
- await execa("restic", [...s3Options, ...args], { env: { ...process.env, ...env }, stdio: "inherit" });
92
+ await execa(resolveResticBin(), [...s3Options, ...args], { env: { ...process.env, ...env }, stdio: "inherit" });
61
93
  } catch (error) {
62
94
  if (error.code === "ENOENT") {
63
- console.error("Error: restic not found. Please install restic first.");
64
- console.error("Visit: https://restic.net/");
95
+ console.error("Error: restic not found. Linux x64 packages include restic; other platforms must install it first.");
96
+ console.error("Set WAY_RESTIC_BIN to use a custom restic binary, or visit: https://restic.net/");
65
97
  process.exit(1);
66
98
  }
67
99
  throw error;
@@ -76,6 +108,8 @@ async function run(options) {
76
108
  const s3Options = buildS3Options(config.repository);
77
109
  const globalExcludes = config.rules.global_excludes || [];
78
110
  const projects = options.projects && options.projects.length > 0 ? options.projects : Object.keys(config.rules.projects);
111
+ const dryRun = options.dryRun || options.extraArgs?.includes("--dry-run") || false;
112
+ const extraArgs = options.extraArgs?.filter((arg) => arg !== "--dry-run") || [];
79
113
  const succeeded = [];
80
114
  const failed = [];
81
115
  const startTime = Date.now();
@@ -89,7 +123,8 @@ async function run(options) {
89
123
  console.log(`=== Backing up: ${projectName} ===`);
90
124
  try {
91
125
  const args = buildBackupArgs(projectName, project, globalExcludes);
92
- if (options.extraArgs) args.push(...options.extraArgs);
126
+ if (dryRun) args.push("--dry-run");
127
+ args.push(...extraArgs);
93
128
  await execRestic(args, env, s3Options);
94
129
  succeeded.push(projectName);
95
130
  } catch (error) {
@@ -101,7 +136,7 @@ async function run(options) {
101
136
  console.log("\n=== Summary ===");
102
137
  if (succeeded.length > 0) console.log("Succeeded:", succeeded.join(", "));
103
138
  if (failed.length > 0) console.log("Failed:", failed.join(", "));
104
- if (config.rules.uptime_kuma?.push_url) {
139
+ if (!dryRun && config.rules.uptime_kuma?.push_url) {
105
140
  await notifyUptimeKuma({ succeeded, failed, duration }, config.rules.uptime_kuma.push_url);
106
141
  }
107
142
  return { succeeded, failed, duration };
@@ -149,8 +184,8 @@ async function gc(options) {
149
184
 
150
185
  // src/commands/systemd.ts
151
186
  import { execSync } from "child_process";
152
- import fs2 from "fs";
153
- import path2 from "path";
187
+ import fs3 from "fs";
188
+ import path3 from "path";
154
189
  async function systemd(options) {
155
190
  const wayDir = process.env.WAY_DIR || `${process.env.HOME}/.way`;
156
191
  const config = loadConfig(wayDir, options.remote);
@@ -178,9 +213,9 @@ WantedBy=multi-user.target
178
213
  return;
179
214
  }
180
215
  const systemdDir = "/etc/systemd/system";
181
- const servicePath = path2.join(systemdDir, "way-backup.service");
216
+ const servicePath = path3.join(systemdDir, "way-backup.service");
182
217
  if (options.action === "install") {
183
- fs2.writeFileSync(servicePath, serviceContent);
218
+ fs3.writeFileSync(servicePath, serviceContent);
184
219
  execSync("systemctl daemon-reload");
185
220
  execSync("systemctl enable way-backup.service");
186
221
  execSync("systemctl start way-backup.service");
@@ -196,7 +231,7 @@ WantedBy=multi-user.target
196
231
  execSync("systemctl disable way-backup.service", { stdio: "ignore" });
197
232
  } catch {
198
233
  }
199
- if (fs2.existsSync(servicePath)) fs2.unlinkSync(servicePath);
234
+ if (fs3.existsSync(servicePath)) fs3.unlinkSync(servicePath);
200
235
  execSync("systemctl daemon-reload");
201
236
  console.log("Systemd service uninstalled");
202
237
  }
@@ -268,7 +303,7 @@ async function daemon(options) {
268
303
 
269
304
  // src/cli.ts
270
305
  var program = new Command();
271
- program.name("way").version("0.5.1").description("\u7B56\u7565\u5907\u4EFD\u5DE5\u5177 - \u57FA\u4E8E restic \u7684\u7B56\u7565\u5C01\u88C5").option("--remote <name>", "\u6307\u5B9A\u4ED3\u5E93", "default").addHelpText("after", `
306
+ program.name("way").version("0.5.2").description("\u7B56\u7565\u5907\u4EFD\u5DE5\u5177 - \u57FA\u4E8E restic \u7684\u7B56\u7565\u5C01\u88C5").option("--remote <name>", "\u6307\u5B9A\u4ED3\u5E93", "default").addHelpText("after", `
272
307
  \u793A\u4F8B:
273
308
  $ way run \u6267\u884C\u6240\u6709\u9879\u76EE\u5907\u4EFD
274
309
  $ way run data \u53EA\u5907\u4EFD data \u9879\u76EE
@@ -282,9 +317,9 @@ program.name("way").version("0.5.1").description("\u7B56\u7565\u5907\u4EFD\u5DE5
282
317
  `);
283
318
  program.command("run [projects...]").description("\u6267\u884C\u5907\u4EFD").option("--dry-run", "\u6A21\u62DF\u5907\u4EFD\uFF08\u4E0D\u5B9E\u9645\u5199\u5165\uFF09").allowUnknownOption().allowExcessArguments().action(async function(projects) {
284
319
  const remote = this.parent.opts().remote;
320
+ const dryRun = this.opts().dryRun;
285
321
  const extraArgs = this.args.filter((a) => a.startsWith("-") && !["--dry-run"].includes(a));
286
- if (this.opts().dryRun) extraArgs.push("--dry-run");
287
- await run({ remote, projects: projects.filter((p) => !p.startsWith("-")), extraArgs });
322
+ await run({ remote, projects: projects.filter((p) => !p.startsWith("-")), extraArgs, dryRun });
288
323
  });
289
324
  program.command("gc").description("\u6E05\u7406\u65E7\u5FEB\u7167").option("--dry-run", "\u6A21\u62DF\u6E05\u7406\uFF08\u4E0D\u5B9E\u9645\u5220\u9664\uFF09").action(async function(cmdOptions) {
290
325
  const remote = this.parent.opts().remote;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shellus/way",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "将备份作为持续运营的项目,而非一次性任务。基于 restic 的策略封装。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "dist",
18
+ "vendor",
18
19
  "repositories.yaml.example",
19
20
  "rules.yaml.example",
20
21
  "migrate-to-v0.5.sh"
@@ -38,9 +39,6 @@
38
39
  "engines": {
39
40
  "node": ">=18.0.0"
40
41
  },
41
- "peerDependencies": {
42
- "restic": "*"
43
- },
44
42
  "dependencies": {
45
43
  "commander": "^14.0.3",
46
44
  "execa": "^9.6.1",
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification,
7
+ are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
20
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ restic 0.16.4 compiled with go1.22.2 on linux/amd64
Binary file