codetime-cli 0.7.0 → 0.7.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/bin/codetime.mjs +154 -40
- package/package.json +1 -1
package/bin/codetime.mjs
CHANGED
|
@@ -19,10 +19,13 @@ __export(fs_exports, {
|
|
|
19
19
|
parseJsonFile: () => parseJsonFile,
|
|
20
20
|
pathExists: () => pathExists,
|
|
21
21
|
readJsonIfExists: () => readJsonIfExists,
|
|
22
|
+
readJsonIfExistsTolerant: () => readJsonIfExistsTolerant,
|
|
22
23
|
readTextIfExists: () => readTextIfExists,
|
|
24
|
+
writeFileAtomic: () => writeFileAtomic,
|
|
23
25
|
writeGeneratedFile: () => writeGeneratedFile
|
|
24
26
|
});
|
|
25
|
-
import {
|
|
27
|
+
import { randomBytes } from "node:crypto";
|
|
28
|
+
import { mkdir, open, readdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
26
29
|
import path from "node:path";
|
|
27
30
|
async function pathExists(filePath) {
|
|
28
31
|
try {
|
|
@@ -56,6 +59,59 @@ function parseJsonFile(filePath, text) {
|
|
|
56
59
|
throw new Error(`Could not parse JSON file ${filePath}: ${error.message}`);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
62
|
+
async function readJsonIfExistsTolerant(filePath, onCorrupt) {
|
|
63
|
+
const text = await readTextIfExists(filePath);
|
|
64
|
+
if (text === null) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(text);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
onCorrupt?.(error);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function writeFileAtomic(filePath, data, options = {}) {
|
|
75
|
+
const dir = path.dirname(filePath);
|
|
76
|
+
await mkdir(dir, { recursive: true });
|
|
77
|
+
const tmpPath = path.join(
|
|
78
|
+
dir,
|
|
79
|
+
`.${path.basename(filePath)}.${process.pid}.${Date.now()}.${randomBytes(6).toString("hex")}.tmp`
|
|
80
|
+
);
|
|
81
|
+
let handle;
|
|
82
|
+
try {
|
|
83
|
+
handle = await open(tmpPath, "wx", options.mode ?? 438);
|
|
84
|
+
await handle.writeFile(data, "utf8");
|
|
85
|
+
if (options.mode !== void 0) {
|
|
86
|
+
await handle.chmod(options.mode);
|
|
87
|
+
}
|
|
88
|
+
await handle.close();
|
|
89
|
+
handle = void 0;
|
|
90
|
+
await renameWithRetry(tmpPath, filePath);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (handle) {
|
|
93
|
+
await handle.close().catch(() => {
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
await rm(tmpPath, { force: true }).catch(() => {
|
|
97
|
+
});
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function renameWithRetry(from, to, attempts = 5) {
|
|
102
|
+
for (let i = 0; ; i++) {
|
|
103
|
+
try {
|
|
104
|
+
await rename(from, to);
|
|
105
|
+
return;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const code = error.code;
|
|
108
|
+
if (i >= attempts - 1 || code !== "EPERM" && code !== "EACCES" && code !== "EBUSY") {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, 10 * (i + 1)));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
59
115
|
async function writeGeneratedFile(filePath, content, { dryRun, force, onWrite }) {
|
|
60
116
|
if (dryRun) {
|
|
61
117
|
onWrite(`Would write ${filePath}`);
|
|
@@ -152,7 +208,7 @@ var init_fs = __esm({
|
|
|
152
208
|
|
|
153
209
|
// src/cli.ts
|
|
154
210
|
import { spawn } from "node:child_process";
|
|
155
|
-
import {
|
|
211
|
+
import { rm as rm2, stat as stat6 } from "node:fs/promises";
|
|
156
212
|
import os3 from "node:os";
|
|
157
213
|
import path13 from "node:path";
|
|
158
214
|
import { fileURLToPath } from "node:url";
|
|
@@ -974,7 +1030,7 @@ import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
|
974
1030
|
import path2 from "node:path";
|
|
975
1031
|
|
|
976
1032
|
// src/lib/constants.ts
|
|
977
|
-
var PACKAGE_VERSION = true ? "0.7.
|
|
1033
|
+
var PACKAGE_VERSION = true ? "0.7.1" : "0.1.1";
|
|
978
1034
|
var DEFAULT_API_URL = "https://codetime.dev";
|
|
979
1035
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
980
1036
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -4516,7 +4572,7 @@ async function installEntry(entry, options) {
|
|
|
4516
4572
|
});
|
|
4517
4573
|
}
|
|
4518
4574
|
async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
4519
|
-
const { mkdir:
|
|
4575
|
+
const { mkdir: mkdir3, writeFile: writeFile2 } = await import("node:fs/promises");
|
|
4520
4576
|
const pathMod = await import("node:path");
|
|
4521
4577
|
if (dryRun) {
|
|
4522
4578
|
onWrite(`Would merge ${filePath}`);
|
|
@@ -4537,8 +4593,8 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
|
4537
4593
|
onWrite(`Already installed ${filePath}`);
|
|
4538
4594
|
return;
|
|
4539
4595
|
}
|
|
4540
|
-
await
|
|
4541
|
-
await
|
|
4596
|
+
await mkdir3(pathMod.dirname(filePath), { recursive: true });
|
|
4597
|
+
await writeFile2(filePath, nextText, "utf8");
|
|
4542
4598
|
onWrite(`Installed ${filePath}`);
|
|
4543
4599
|
}
|
|
4544
4600
|
function mergeHookObjects(existing, addition) {
|
|
@@ -4576,7 +4632,7 @@ function hookCommandFromGroup(group) {
|
|
|
4576
4632
|
|
|
4577
4633
|
// src/lib/config.ts
|
|
4578
4634
|
import { randomUUID } from "node:crypto";
|
|
4579
|
-
import { existsSync, mkdirSync, readFileSync, rmSync,
|
|
4635
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, unlinkSync, writeSync } from "node:fs";
|
|
4580
4636
|
import { homedir, hostname } from "node:os";
|
|
4581
4637
|
import path11 from "node:path";
|
|
4582
4638
|
function configDir(home = homedir()) {
|
|
@@ -4600,30 +4656,87 @@ function readConfig(home = homedir()) {
|
|
|
4600
4656
|
return {};
|
|
4601
4657
|
}
|
|
4602
4658
|
}
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4659
|
+
var atomicTmpCounter = 0;
|
|
4660
|
+
function writeFileAtomicSync(filePath, data, mode) {
|
|
4661
|
+
const dir = path11.dirname(filePath);
|
|
4662
|
+
mkdirSync(dir, { recursive: true });
|
|
4663
|
+
const tmp = path11.join(dir, `.${path11.basename(filePath)}.${process.pid}.${Date.now()}.${(atomicTmpCounter++).toString(36)}.tmp`);
|
|
4664
|
+
let fd;
|
|
4665
|
+
try {
|
|
4666
|
+
fd = openSync(tmp, "wx", mode);
|
|
4667
|
+
writeSync(fd, data);
|
|
4668
|
+
closeSync(fd);
|
|
4669
|
+
fd = void 0;
|
|
4670
|
+
chmodSync(tmp, mode);
|
|
4671
|
+
renameSyncWithRetry(tmp, filePath);
|
|
4672
|
+
} catch (error) {
|
|
4673
|
+
if (fd !== void 0) {
|
|
4674
|
+
try {
|
|
4675
|
+
closeSync(fd);
|
|
4676
|
+
} catch {
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
try {
|
|
4680
|
+
unlinkSync(tmp);
|
|
4681
|
+
} catch {
|
|
4682
|
+
}
|
|
4683
|
+
throw error;
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
function renameSyncWithRetry(from, to, attempts = 5) {
|
|
4687
|
+
for (let i = 0; ; i++) {
|
|
4688
|
+
try {
|
|
4689
|
+
renameSync(from, to);
|
|
4690
|
+
return;
|
|
4691
|
+
} catch (error) {
|
|
4692
|
+
const code = error.code;
|
|
4693
|
+
if (i >= attempts - 1 || code !== "EPERM" && code !== "EACCES" && code !== "EBUSY") {
|
|
4694
|
+
throw error;
|
|
4695
|
+
}
|
|
4696
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10 * (i + 1));
|
|
4697
|
+
}
|
|
4607
4698
|
}
|
|
4608
|
-
|
|
4609
|
-
|
|
4699
|
+
}
|
|
4700
|
+
function writeConfig(config, home = homedir()) {
|
|
4701
|
+
writeFileAtomicSync(configPath(home), `${JSON.stringify(config, null, 2)}
|
|
4702
|
+
`, 384);
|
|
4610
4703
|
}
|
|
4611
4704
|
function ensureLocalMachineId(home = homedir()) {
|
|
4612
4705
|
const file = machineIdPath(home);
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
return value;
|
|
4617
|
-
}
|
|
4706
|
+
const existing = readMachineId(file);
|
|
4707
|
+
if (existing) {
|
|
4708
|
+
return existing;
|
|
4618
4709
|
}
|
|
4710
|
+
mkdirSync(configDir(home), { recursive: true });
|
|
4619
4711
|
const id = randomUUID();
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4712
|
+
try {
|
|
4713
|
+
const fd = openSync(file, "wx", 384);
|
|
4714
|
+
try {
|
|
4715
|
+
writeSync(fd, `${id}
|
|
4716
|
+
`);
|
|
4717
|
+
} finally {
|
|
4718
|
+
closeSync(fd);
|
|
4719
|
+
}
|
|
4720
|
+
return id;
|
|
4721
|
+
} catch (error) {
|
|
4722
|
+
if (error.code !== "EEXIST") {
|
|
4723
|
+
throw error;
|
|
4724
|
+
}
|
|
4725
|
+
const peer = readMachineId(file);
|
|
4726
|
+
if (peer) {
|
|
4727
|
+
return peer;
|
|
4728
|
+
}
|
|
4729
|
+
writeFileAtomicSync(file, `${id}
|
|
4730
|
+
`, 384);
|
|
4731
|
+
return id;
|
|
4623
4732
|
}
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4733
|
+
}
|
|
4734
|
+
function readMachineId(file) {
|
|
4735
|
+
if (!existsSync(file)) {
|
|
4736
|
+
return null;
|
|
4737
|
+
}
|
|
4738
|
+
const value = readFileSync(file, "utf8").trim();
|
|
4739
|
+
return value.length > 0 ? value : null;
|
|
4627
4740
|
}
|
|
4628
4741
|
function defaultMachineName() {
|
|
4629
4742
|
return hostname();
|
|
@@ -4633,7 +4746,7 @@ function defaultMachineName() {
|
|
|
4633
4746
|
init_fs();
|
|
4634
4747
|
|
|
4635
4748
|
// src/lib/logger.ts
|
|
4636
|
-
import { appendFile, mkdir as mkdir2, rename, stat as stat5 } from "node:fs/promises";
|
|
4749
|
+
import { appendFile, mkdir as mkdir2, rename as rename2, stat as stat5 } from "node:fs/promises";
|
|
4637
4750
|
import { homedir as homedir2 } from "node:os";
|
|
4638
4751
|
import path12 from "node:path";
|
|
4639
4752
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -4653,7 +4766,7 @@ async function rotateIfNeeded(file) {
|
|
|
4653
4766
|
try {
|
|
4654
4767
|
const info = await stat5(file);
|
|
4655
4768
|
if (info.size > MAX_BYTES) {
|
|
4656
|
-
await
|
|
4769
|
+
await rename2(file, `${file}.1`).catch(() => {
|
|
4657
4770
|
});
|
|
4658
4771
|
}
|
|
4659
4772
|
} catch {
|
|
@@ -5487,7 +5600,7 @@ async function importBackfillPlan(plan, options, ctx, registry) {
|
|
|
5487
5600
|
}
|
|
5488
5601
|
async function purgeForcedSources(sourceDefs, home, options, ctx) {
|
|
5489
5602
|
try {
|
|
5490
|
-
await
|
|
5603
|
+
await rm2(backfillIncrementalStatePath(home), { force: true });
|
|
5491
5604
|
} catch (error) {
|
|
5492
5605
|
debug(ctx, `Failed to clear backfill watermark: ${error.message}
|
|
5493
5606
|
`);
|
|
@@ -5685,7 +5798,11 @@ function syncLocalTriggerLockPath(home) {
|
|
|
5685
5798
|
}
|
|
5686
5799
|
async function readBackfillIncrementalState(home, ctx) {
|
|
5687
5800
|
const statePath = backfillIncrementalStatePath(home);
|
|
5688
|
-
const state = await
|
|
5801
|
+
const state = await readJsonIfExistsTolerant(
|
|
5802
|
+
statePath,
|
|
5803
|
+
ctx ? (error) => debug(ctx, `backfill-state unreadable at ${statePath}; dropping watermarks and re-importing (${error.message})
|
|
5804
|
+
`) : void 0
|
|
5805
|
+
);
|
|
5689
5806
|
if (state === null) {
|
|
5690
5807
|
return { version: BACKFILL_STATE_SCHEMA_VERSION, sources: {} };
|
|
5691
5808
|
}
|
|
@@ -5721,12 +5838,11 @@ async function updateBackfillIncrementalState(home, state, selectedFilesBySource
|
|
|
5721
5838
|
state.sources[source] = { watermarkTs: latest };
|
|
5722
5839
|
}
|
|
5723
5840
|
const statePath = backfillIncrementalStatePath(home);
|
|
5724
|
-
await
|
|
5725
|
-
|
|
5726
|
-
`, "utf8");
|
|
5841
|
+
await writeFileAtomic(statePath, `${JSON.stringify(state, null, 2)}
|
|
5842
|
+
`);
|
|
5727
5843
|
}
|
|
5728
5844
|
async function readSyncLocalTriggerState(statePath) {
|
|
5729
|
-
const state = await
|
|
5845
|
+
const state = await readJsonIfExistsTolerant(statePath);
|
|
5730
5846
|
if (!isPlainObject(state)) {
|
|
5731
5847
|
return { version: 1 };
|
|
5732
5848
|
}
|
|
@@ -5749,12 +5865,11 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
5749
5865
|
return nextState;
|
|
5750
5866
|
}
|
|
5751
5867
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
5752
|
-
await
|
|
5753
|
-
|
|
5754
|
-
`, "utf8");
|
|
5868
|
+
await writeFileAtomic(statePath, `${JSON.stringify(state, null, 2)}
|
|
5869
|
+
`);
|
|
5755
5870
|
}
|
|
5756
5871
|
async function readSyncLocalLock(lockPath) {
|
|
5757
|
-
const lock = await
|
|
5872
|
+
const lock = await readJsonIfExistsTolerant(lockPath);
|
|
5758
5873
|
if (!isPlainObject(lock)) {
|
|
5759
5874
|
return void 0;
|
|
5760
5875
|
}
|
|
@@ -5764,13 +5879,12 @@ async function readSyncLocalLock(lockPath) {
|
|
|
5764
5879
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
5765
5880
|
}
|
|
5766
5881
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
5767
|
-
await
|
|
5768
|
-
|
|
5769
|
-
`, "utf8");
|
|
5882
|
+
await writeFileAtomic(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
5883
|
+
`);
|
|
5770
5884
|
}
|
|
5771
5885
|
async function clearSyncLocalLock(lockPath) {
|
|
5772
5886
|
try {
|
|
5773
|
-
await
|
|
5887
|
+
await rm2(lockPath, { force: true });
|
|
5774
5888
|
} catch (error) {
|
|
5775
5889
|
if (error.code !== "ENOENT") {
|
|
5776
5890
|
throw error;
|
package/package.json
CHANGED