sftp-push-sync 1.0.13 → 1.0.15
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/sftp-push-sync.mjs +53 -35
- package/package.json +1 -1
package/bin/sftp-push-sync.mjs
CHANGED
|
@@ -53,7 +53,11 @@ const EXC = pc.redBright("-"); // Excluded
|
|
|
53
53
|
|
|
54
54
|
const hr1 = () => "─".repeat(65); // horizontal line -
|
|
55
55
|
const hr2 = () => "=".repeat(65); // horizontal line =
|
|
56
|
-
const tab_a = () => " ".repeat(3); // indentation for formatting the output.
|
|
56
|
+
const tab_a = () => " ".repeat(3); // indentation for formatting the terminal output.
|
|
57
|
+
const tab_b = () => " ".repeat(6);
|
|
58
|
+
const cr_a = () => "\n".repeat(1);
|
|
59
|
+
const cr_b = () => "\n".repeat(2);
|
|
60
|
+
|
|
57
61
|
|
|
58
62
|
// ---------------------------------------------------------------------------
|
|
59
63
|
// CLI arguments
|
|
@@ -72,7 +76,7 @@ if (args.includes("--laconic")) cliLogLevel = "laconic";
|
|
|
72
76
|
|
|
73
77
|
if (!TARGET) {
|
|
74
78
|
console.error(pc.red("❌ Please specify a connection profile:"));
|
|
75
|
-
console.error(pc.yellow(
|
|
79
|
+
console.error(pc.yellow(`${tab_a()}sftp-push-sync staging --dry-run`));
|
|
76
80
|
process.exit(1);
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -242,8 +246,11 @@ let progressActive = false;
|
|
|
242
246
|
function clearProgressLine() {
|
|
243
247
|
if (!process.stdout.isTTY || !progressActive) return;
|
|
244
248
|
const width = process.stdout.columns || 80;
|
|
245
|
-
const blank = " ".repeat(width
|
|
246
|
-
|
|
249
|
+
const blank = " ".repeat(width);
|
|
250
|
+
|
|
251
|
+
// Beide Progress-Zeilen leeren
|
|
252
|
+
process.stdout.write("\r" + blank + cr_a() + blank + "\r");
|
|
253
|
+
|
|
247
254
|
progressActive = false;
|
|
248
255
|
}
|
|
249
256
|
|
|
@@ -346,7 +353,7 @@ function updateProgress2(prefix, current, total, rel = "") {
|
|
|
346
353
|
if (line2.length > width) line2 = line2.slice(0, width - 1);
|
|
347
354
|
|
|
348
355
|
// zwei Zeilen überschreiben
|
|
349
|
-
process.stdout.write("\r" + line1.padEnd(width) +
|
|
356
|
+
process.stdout.write("\r" + line1.padEnd(width) + cr_a());
|
|
350
357
|
process.stdout.write(line2.padEnd(width));
|
|
351
358
|
|
|
352
359
|
// Cursor wieder nach oben (auf die Fortschrittszeile)
|
|
@@ -421,8 +428,8 @@ async function walkLocal(root) {
|
|
|
421
428
|
scanned += 1;
|
|
422
429
|
const chunk = IS_VERBOSE ? 1 : SCAN_CHUNK;
|
|
423
430
|
if (scanned === 1 || scanned % chunk === 0) {
|
|
424
|
-
// totally unknown →
|
|
425
|
-
updateProgress2(
|
|
431
|
+
// totally unknown → total = 0 → no automatic \n
|
|
432
|
+
updateProgress2(`${tab_a()}Scan local: `, scanned, 0, rel);
|
|
426
433
|
}
|
|
427
434
|
}
|
|
428
435
|
}
|
|
@@ -432,8 +439,8 @@ async function walkLocal(root) {
|
|
|
432
439
|
|
|
433
440
|
if (scanned > 0) {
|
|
434
441
|
// last line + neat finish
|
|
435
|
-
updateProgress2(
|
|
436
|
-
process.stdout.write(
|
|
442
|
+
updateProgress2(`${tab_a()}Scan local: `, scanned, 0, "fertig");
|
|
443
|
+
process.stdout.write(cr_a());
|
|
437
444
|
progressActive = false;
|
|
438
445
|
}
|
|
439
446
|
|
|
@@ -473,7 +480,7 @@ async function walkRemote(sftp, remoteRoot) {
|
|
|
473
480
|
scanned += 1;
|
|
474
481
|
const chunk = IS_VERBOSE ? 1 : SCAN_CHUNK;
|
|
475
482
|
if (scanned === 1 || scanned % chunk === 0) {
|
|
476
|
-
updateProgress2(
|
|
483
|
+
updateProgress2(`${tab_a()}Scan remote: `, scanned, 0, rel);
|
|
477
484
|
}
|
|
478
485
|
}
|
|
479
486
|
}
|
|
@@ -482,8 +489,8 @@ async function walkRemote(sftp, remoteRoot) {
|
|
|
482
489
|
await recurse(remoteRoot);
|
|
483
490
|
|
|
484
491
|
if (scanned > 0) {
|
|
485
|
-
updateProgress2(
|
|
486
|
-
process.stdout.write(
|
|
492
|
+
updateProgress2(`${tab_a()}Scan remote: `, scanned, 0, "fertig");
|
|
493
|
+
process.stdout.write(cr_a());
|
|
487
494
|
progressActive = false;
|
|
488
495
|
}
|
|
489
496
|
|
|
@@ -609,18 +616,18 @@ function describeSftpError(err) {
|
|
|
609
616
|
async function main() {
|
|
610
617
|
const start = Date.now();
|
|
611
618
|
|
|
612
|
-
log(
|
|
619
|
+
log(`${cr_b()}${hr2()}`);
|
|
613
620
|
log(
|
|
614
621
|
pc.bold(
|
|
615
622
|
`🔐 SFTP Push-Synchronisation: sftp-push-sync v${pkg.version} [logLevel=${LOG_LEVEL}]`
|
|
616
623
|
)
|
|
617
624
|
);
|
|
618
625
|
log(`${tab_a()}Connection: ${pc.cyan(TARGET)}`);
|
|
619
|
-
log(
|
|
620
|
-
log(`${tab_a()}Host:
|
|
621
|
-
log(`${tab_a()}Local:
|
|
626
|
+
log(`${tab_a()}Worker: ${CONNECTION.workers}`);
|
|
627
|
+
log(`${tab_a()}Host: ${pc.green(CONNECTION.host)}:${pc.green(CONNECTION.port)}`);
|
|
628
|
+
log(`${tab_a()}Local: ${pc.green(CONNECTION.localRoot)}`);
|
|
622
629
|
log(`${tab_a()}Remote: ${pc.green(CONNECTION.remoteRoot)}`);
|
|
623
|
-
if (DRY_RUN) log(pc.yellow(
|
|
630
|
+
if (DRY_RUN) log(pc.yellow(`${tab_a()}Mode: DRY-RUN (no changes)`));
|
|
624
631
|
if (RUN_UPLOAD_LIST || RUN_DOWNLOAD_LIST) {
|
|
625
632
|
log(
|
|
626
633
|
pc.blue(
|
|
@@ -630,7 +637,7 @@ async function main() {
|
|
|
630
637
|
)
|
|
631
638
|
);
|
|
632
639
|
}
|
|
633
|
-
log(`${hr1()}
|
|
640
|
+
log(`${hr1()}${cr_a()}`);
|
|
634
641
|
|
|
635
642
|
const sftp = new SftpClient();
|
|
636
643
|
let connected = false;
|
|
@@ -658,7 +665,7 @@ async function main() {
|
|
|
658
665
|
process.exit(1);
|
|
659
666
|
}
|
|
660
667
|
|
|
661
|
-
log(pc.bold(pc.cyan("📥 Phase 1: Scan local files …")));
|
|
668
|
+
log(cr_a() + pc.bold(pc.cyan("📥 Phase 1: Scan local files …")));
|
|
662
669
|
const local = await walkLocal(CONNECTION.localRoot);
|
|
663
670
|
log(`${tab_a()}→ ${local.size} local files`);
|
|
664
671
|
|
|
@@ -671,14 +678,14 @@ async function main() {
|
|
|
671
678
|
log("");
|
|
672
679
|
}
|
|
673
680
|
|
|
674
|
-
log(pc.bold(pc.cyan("📤 Phase 2: Scan remote files …")));
|
|
681
|
+
log(cr_a() + pc.bold(pc.cyan("📤 Phase 2: Scan remote files …")));
|
|
675
682
|
const remote = await walkRemote(sftp, CONNECTION.remoteRoot);
|
|
676
|
-
log(`${tab_a()}→ ${remote.size} remote files
|
|
683
|
+
log(`${tab_a()}→ ${remote.size} remote files${cr_a()}`);
|
|
677
684
|
|
|
678
685
|
const localKeys = new Set(local.keys());
|
|
679
686
|
const remoteKeys = new Set(remote.keys());
|
|
680
687
|
|
|
681
|
-
log(pc.bold(pc.cyan("🔎 Phase 3: Compare & decide …")));
|
|
688
|
+
log(cr_a() + pc.bold(pc.cyan("🔎 Phase 3: Compare & decide …")));
|
|
682
689
|
const totalToCheck = localKeys.size;
|
|
683
690
|
let checkedCount = 0;
|
|
684
691
|
|
|
@@ -761,8 +768,8 @@ async function main() {
|
|
|
761
768
|
|
|
762
769
|
if (IS_VERBOSE) {
|
|
763
770
|
vlog(`${tab_a()}${CHA} Hash different (binary): ${rel}`);
|
|
764
|
-
vlog(`${
|
|
765
|
-
vlog(`${
|
|
771
|
+
vlog(`${tab_b()}local: ${localHash}`);
|
|
772
|
+
vlog(`${tab_b()}remote: ${remoteHash}`);
|
|
766
773
|
}
|
|
767
774
|
|
|
768
775
|
toUpdate.push({ rel, local: l, remote: r, remotePath });
|
|
@@ -772,8 +779,13 @@ async function main() {
|
|
|
772
779
|
}
|
|
773
780
|
}
|
|
774
781
|
|
|
782
|
+
// Wenn Phase 3 nichts gefunden hat, explizit sagen
|
|
783
|
+
if (toAdd.length === 0 && toUpdate.length === 0) {
|
|
784
|
+
log(`${tab_a()}No differences found. Everything is up to date.`);
|
|
785
|
+
}
|
|
786
|
+
|
|
775
787
|
log(
|
|
776
|
-
|
|
788
|
+
cr_a() + pc.bold(pc.cyan("🧹 Phase 4: Removing orphaned remote files …"))
|
|
777
789
|
);
|
|
778
790
|
for (const rel of remoteKeys) {
|
|
779
791
|
if (!localKeys.has(rel)) {
|
|
@@ -785,12 +797,17 @@ async function main() {
|
|
|
785
797
|
}
|
|
786
798
|
}
|
|
787
799
|
|
|
800
|
+
// Auch für Phase 4 eine „nix zu tun“-Meldung
|
|
801
|
+
if (toDelete.length === 0) {
|
|
802
|
+
log(`${tab_a()}No orphaned remote files found.`);
|
|
803
|
+
}
|
|
804
|
+
|
|
788
805
|
// -------------------------------------------------------------------
|
|
789
806
|
// Phase 5: Execute changes (parallel, worker-based)
|
|
790
807
|
// -------------------------------------------------------------------
|
|
791
808
|
|
|
792
809
|
if (!DRY_RUN) {
|
|
793
|
-
log(
|
|
810
|
+
log(cr_a() + pc.bold(pc.cyan("🚚 Phase 5: Apply changes …")));
|
|
794
811
|
|
|
795
812
|
// Upload new files
|
|
796
813
|
await runTasks(
|
|
@@ -844,7 +861,7 @@ async function main() {
|
|
|
844
861
|
} else {
|
|
845
862
|
log(
|
|
846
863
|
pc.yellow(
|
|
847
|
-
|
|
864
|
+
`${cr_a()}💡 DRY-RUN: Connection tested, no files transferred or deleted.`
|
|
848
865
|
)
|
|
849
866
|
);
|
|
850
867
|
}
|
|
@@ -855,7 +872,7 @@ async function main() {
|
|
|
855
872
|
|
|
856
873
|
if (RUN_UPLOAD_LIST && UPLOAD_LIST.length > 0) {
|
|
857
874
|
log(
|
|
858
|
-
|
|
875
|
+
cr_a() +
|
|
859
876
|
pc.bold(pc.cyan("⬆️ Extra Phase: Upload-List (explicit files) …"))
|
|
860
877
|
);
|
|
861
878
|
|
|
@@ -890,7 +907,7 @@ async function main() {
|
|
|
890
907
|
|
|
891
908
|
if (RUN_DOWNLOAD_LIST && DOWNLOAD_LIST.length > 0) {
|
|
892
909
|
log(
|
|
893
|
-
|
|
910
|
+
cr_a() +
|
|
894
911
|
pc.bold(pc.cyan("⬇️ Extra Phase: Download-List (explicit files) …"))
|
|
895
912
|
);
|
|
896
913
|
|
|
@@ -924,7 +941,8 @@ async function main() {
|
|
|
924
941
|
await saveCache(true);
|
|
925
942
|
|
|
926
943
|
// Summary
|
|
927
|
-
log(
|
|
944
|
+
log(hr1());
|
|
945
|
+
log(cr_a() + pc.bold(pc.cyan("📊 Summary:")));
|
|
928
946
|
log(`${tab_a()}Duration: ${pc.green(duration + " s")}`);
|
|
929
947
|
log(`${tab_a()}${ADD} Added : ${toAdd.length}`);
|
|
930
948
|
log(`${tab_a()}${CHA} Changed: ${toUpdate.length}`);
|
|
@@ -935,7 +953,7 @@ async function main() {
|
|
|
935
953
|
);
|
|
936
954
|
}
|
|
937
955
|
if (toAdd.length || toUpdate.length || toDelete.length) {
|
|
938
|
-
log(
|
|
956
|
+
log(`${cr_a()}📄 Changes:`);
|
|
939
957
|
[...toAdd.map((t) => t.rel)]
|
|
940
958
|
.sort()
|
|
941
959
|
.forEach((f) => console.log(`${tab_a()}${ADD} ${f}`));
|
|
@@ -946,10 +964,10 @@ async function main() {
|
|
|
946
964
|
.sort()
|
|
947
965
|
.forEach((f) => console.log(`${tab_a()}${DEL} ${f}`));
|
|
948
966
|
} else {
|
|
949
|
-
log(
|
|
967
|
+
log(`${cr_a()}No changes.`);
|
|
950
968
|
}
|
|
951
969
|
|
|
952
|
-
log(
|
|
970
|
+
log(cr_a() + pc.bold(pc.green("✅ Sync complete.")));
|
|
953
971
|
} catch (err) {
|
|
954
972
|
const hint = describeSftpError(err);
|
|
955
973
|
elog(pc.red("❌ Synchronisation error:"), err.message || err);
|
|
@@ -979,7 +997,7 @@ async function main() {
|
|
|
979
997
|
);
|
|
980
998
|
}
|
|
981
999
|
}
|
|
982
|
-
log(`${hr2()}
|
|
1000
|
+
log(`${hr2()}${cr_b()}`);
|
|
983
1001
|
}
|
|
984
1002
|
|
|
985
|
-
main();
|
|
1003
|
+
main();
|