@wpmoo/toolkit 0.9.24 → 0.9.26
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/dist/audit-log.js +78 -0
- package/dist/cli.js +11 -1
- package/dist/cockpit/daily-prompts.js +15 -1
- package/dist/daily-actions.js +162 -76
- package/dist/databases.js +81 -0
- package/dist/environment-policy.js +219 -0
- package/dist/help.js +2 -2
- package/dist/migrations.js +112 -0
- package/dist/safe-reset.js +244 -17
- package/dist/scaffold.js +2 -1
- package/dist/service-runtime-status.js +65 -3
- package/dist/templates.js +169 -6
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
+
const odooHttpReadyUrl = 'http://127.0.0.1:8069';
|
|
3
|
+
const odooHttpProbeTimeoutMs = 1_000;
|
|
2
4
|
function run(command, args, options) {
|
|
3
5
|
return new Promise((resolve, reject) => {
|
|
4
6
|
execFile(command, args, { cwd: options.cwd }, (error, stdout) => {
|
|
@@ -10,11 +12,36 @@ function run(command, args, options) {
|
|
|
10
12
|
});
|
|
11
13
|
});
|
|
12
14
|
}
|
|
15
|
+
async function fetchOdooHttpReady() {
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
const timeout = setTimeout(() => controller.abort(), odooHttpProbeTimeoutMs);
|
|
18
|
+
try {
|
|
19
|
+
return await fetch(odooHttpReadyUrl, { signal: controller.signal });
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
clearTimeout(timeout);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
13
25
|
export function renderServiceRuntimeStatusLine(status) {
|
|
14
|
-
if (status.kind === 'running'
|
|
26
|
+
if (status.kind === 'running' ||
|
|
27
|
+
status.kind === 'services-running' ||
|
|
28
|
+
status.kind === 'odoo-not-ready' ||
|
|
29
|
+
status.kind === 'fully-ready' ||
|
|
30
|
+
status.kind === 'db-ready') {
|
|
31
|
+
if (status.kind === 'db-ready') {
|
|
32
|
+
return 'Status: ● DB ready';
|
|
33
|
+
}
|
|
34
|
+
if (status.kind === 'odoo-not-ready') {
|
|
35
|
+
return 'Status: ● Odoo not ready';
|
|
36
|
+
}
|
|
37
|
+
if (status.kind === 'fully-ready') {
|
|
38
|
+
return 'Status: ● Fully ready';
|
|
39
|
+
}
|
|
15
40
|
return 'Status: ● Services running';
|
|
16
|
-
|
|
41
|
+
}
|
|
42
|
+
if (status.kind === 'docker-not-running') {
|
|
17
43
|
return 'Status: ● Docker not running';
|
|
44
|
+
}
|
|
18
45
|
return 'Status: ● Services stopped';
|
|
19
46
|
}
|
|
20
47
|
export async function getServiceRuntimeStatus(target, environmentStatus, runner = run) {
|
|
@@ -44,5 +71,40 @@ export async function getServiceRuntimeStatus(target, environmentStatus, runner
|
|
|
44
71
|
catch {
|
|
45
72
|
return { kind: 'stopped' };
|
|
46
73
|
}
|
|
47
|
-
|
|
74
|
+
if (!result.stdout.trim()) {
|
|
75
|
+
return { kind: 'stopped' };
|
|
76
|
+
}
|
|
77
|
+
const runningServices = result.stdout
|
|
78
|
+
.trim()
|
|
79
|
+
.split('\n')
|
|
80
|
+
.map((service) => service.trim())
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
if (!runningServices.includes('db')) {
|
|
83
|
+
return { kind: 'services-running' };
|
|
84
|
+
}
|
|
85
|
+
const dbProbeArgs = [
|
|
86
|
+
'compose',
|
|
87
|
+
...environmentStatus.composeFiles.flatMap((file) => ['-f', file]),
|
|
88
|
+
'exec',
|
|
89
|
+
'-T',
|
|
90
|
+
'db',
|
|
91
|
+
'pg_isready',
|
|
92
|
+
'-U',
|
|
93
|
+
'odoo',
|
|
94
|
+
'-d',
|
|
95
|
+
'postgres',
|
|
96
|
+
];
|
|
97
|
+
try {
|
|
98
|
+
await runner('docker', dbProbeArgs, { cwd: target });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return { kind: 'services-running' };
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetchOdooHttpReady();
|
|
105
|
+
return response.ok ? { kind: 'fully-ready' } : { kind: 'odoo-not-ready' };
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return { kind: 'db-ready' };
|
|
109
|
+
}
|
|
48
110
|
}
|
package/dist/templates.js
CHANGED
|
@@ -226,8 +226,8 @@ exposes them through \`/mnt/wpmoo-addons\`.
|
|
|
226
226
|
\`./moo\` routes day-to-day service and module workflows to local scripts in
|
|
227
227
|
\`./scripts/\` (for example \`start\`, \`logs\`, \`update\`, \`test\`, \`snapshot\`).
|
|
228
228
|
\`./moo status\` runs local offline metadata checks without needing network access.
|
|
229
|
-
\`./moo doctor\`
|
|
230
|
-
\`npx --yes ${fallbackPackageSpec()} doctor\`.
|
|
229
|
+
\`./moo doctor\` runs local checks first and uses the package fallback only for
|
|
230
|
+
advanced usage (for example \`--help\`) via \`npx --yes ${fallbackPackageSpec()} doctor\`.
|
|
231
231
|
|
|
232
232
|
### Start And Inspect Services
|
|
233
233
|
|
|
@@ -449,7 +449,7 @@ usage() {
|
|
|
449
449
|
case "$1" in
|
|
450
450
|
"start") echo "Usage: ./moo start" ;;
|
|
451
451
|
"stop") echo "Usage: ./moo stop" ;;
|
|
452
|
-
"logs") echo "Usage: ./moo logs [service]" ;;
|
|
452
|
+
"logs") echo "Usage: ./moo logs [service] [tail-lines]" ;;
|
|
453
453
|
"restart") echo "Usage: ./moo restart" ;;
|
|
454
454
|
"shell") echo "Usage: ./moo shell" ;;
|
|
455
455
|
"psql") echo "Usage: ./moo psql [db]" ;;
|
|
@@ -601,6 +601,62 @@ allow_stage_lifecycle() {
|
|
|
601
601
|
[[ "$value" == "1" ]]
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
allow_no_recent_snapshot() {
|
|
605
|
+
local value="\${WPMOO_ALLOW_NO_RECENT_SNAPSHOT:-$(env_file_value WPMOO_ALLOW_NO_RECENT_SNAPSHOT)}"
|
|
606
|
+
[[ "$value" == "1" ]]
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
allow_migrations() {
|
|
610
|
+
local value="\${WPMOO_ALLOW_MIGRATIONS:-$(env_file_value WPMOO_ALLOW_MIGRATIONS)}"
|
|
611
|
+
[[ "$value" == "1" ]]
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
has_recent_snapshot() {
|
|
615
|
+
local dir
|
|
616
|
+
for dir in backups backup snapshots; do
|
|
617
|
+
[[ -d "$dir" ]] || continue
|
|
618
|
+
if find "$dir" -type f \\( -name "*.dump" -o -name "*.sql" -o -name "*.sql.gz" -o -name "*.zip" -o -name "*.tar" -o -name "*.tar.gz" \\) -mtime -1 -print -quit 2>/dev/null | grep -q .; then
|
|
619
|
+
return 0
|
|
620
|
+
fi
|
|
621
|
+
done
|
|
622
|
+
return 1
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
require_recent_snapshot_or_override() {
|
|
626
|
+
local command="$1"
|
|
627
|
+
local env_name
|
|
628
|
+
env_name="$(selected_env)"
|
|
629
|
+
if [[ "$env_name" == "stage" || "$env_name" == "prod" ]]; then
|
|
630
|
+
if ! allow_no_recent_snapshot && ! has_recent_snapshot; then
|
|
631
|
+
echo "Refusing destructive command '$command' in WPMOO_ENV=$env_name without a recent database snapshot. Create a snapshot first or set WPMOO_ALLOW_NO_RECENT_SNAPSHOT=1 to run it intentionally." >&2
|
|
632
|
+
exit 1
|
|
633
|
+
fi
|
|
634
|
+
fi
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
has_migration_risk() {
|
|
638
|
+
local base
|
|
639
|
+
for base in odoo/custom/src/private odoo/custom/src/oca odoo/custom/src/external; do
|
|
640
|
+
[[ -d "$base" ]] || continue
|
|
641
|
+
if find "$base" -type f \\( -path "*/migrations/*/pre-migration.py" -o -path "*/migrations/*/post-migration.py" -o -path "*/migrations/*/end-migration.py" -o -path "*/migration/*/pre-migration.py" -o -path "*/migration/*/post-migration.py" -o -path "*/migration/*/end-migration.py" -o -path "*/scripts/migrate.py" -o -path "*/scripts/migration.py" \\) -print -quit 2>/dev/null | grep -q .; then
|
|
642
|
+
return 0
|
|
643
|
+
fi
|
|
644
|
+
done
|
|
645
|
+
return 1
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
require_migrations_allowed() {
|
|
649
|
+
local command="$1"
|
|
650
|
+
local env_name
|
|
651
|
+
env_name="$(selected_env)"
|
|
652
|
+
if [[ "$env_name" == "stage" || "$env_name" == "prod" ]]; then
|
|
653
|
+
if ! allow_migrations && has_migration_risk; then
|
|
654
|
+
echo "Refusing migration-risk command '$command' in WPMOO_ENV=$env_name. Review detected migration scripts or set WPMOO_ALLOW_MIGRATIONS=1 to run it intentionally." >&2
|
|
655
|
+
exit 1
|
|
656
|
+
fi
|
|
657
|
+
fi
|
|
658
|
+
}
|
|
659
|
+
|
|
604
660
|
require_stage_lifecycle_allowed() {
|
|
605
661
|
local command="$1"
|
|
606
662
|
local env_name
|
|
@@ -657,7 +713,14 @@ case "$command" in
|
|
|
657
713
|
fi
|
|
658
714
|
run_package_command "$command" "$@"
|
|
659
715
|
;;
|
|
660
|
-
"
|
|
716
|
+
"doctor")
|
|
717
|
+
shift
|
|
718
|
+
if [[ "$#" -eq 0 && -x ./scripts/doctor.sh ]]; then
|
|
719
|
+
run_script ./scripts/doctor.sh
|
|
720
|
+
fi
|
|
721
|
+
run_package_command "$command" "$@"
|
|
722
|
+
;;
|
|
723
|
+
"create"|"add-repo"|"remove-repo"|"add-module"|"remove-module"|"source"|"reset")
|
|
661
724
|
run_package_command "$@"
|
|
662
725
|
;;
|
|
663
726
|
"start")
|
|
@@ -672,7 +735,17 @@ case "$command" in
|
|
|
672
735
|
;;
|
|
673
736
|
"logs")
|
|
674
737
|
shift
|
|
675
|
-
|
|
738
|
+
if [[ "$#" -gt 2 || "\${1:-}" == -* || "\${2:-}" == -* ]]; then
|
|
739
|
+
fail_usage "$command"
|
|
740
|
+
fi
|
|
741
|
+
service="\${1:-odoo}"
|
|
742
|
+
if [[ "$#" -eq 2 ]]; then
|
|
743
|
+
if [[ ! "$2" =~ ^[1-9][0-9]*$ ]]; then
|
|
744
|
+
echo "Invalid logs tail count: expected a positive integer." >&2
|
|
745
|
+
exit 2
|
|
746
|
+
fi
|
|
747
|
+
run_script ./scripts/logs.sh "$service" "$2"
|
|
748
|
+
fi
|
|
676
749
|
run_script ./scripts/logs.sh "$service"
|
|
677
750
|
;;
|
|
678
751
|
"restart")
|
|
@@ -695,6 +768,7 @@ case "$command" in
|
|
|
695
768
|
require_module_args "$command" "$@"
|
|
696
769
|
require_stage_lifecycle_allowed "$command"
|
|
697
770
|
require_prod_lifecycle_allowed "$command"
|
|
771
|
+
require_migrations_allowed "$command"
|
|
698
772
|
run_script ./scripts/install.sh "$@"
|
|
699
773
|
;;
|
|
700
774
|
"update")
|
|
@@ -702,18 +776,21 @@ case "$command" in
|
|
|
702
776
|
require_module_args "$command" "$@"
|
|
703
777
|
require_stage_lifecycle_allowed "$command"
|
|
704
778
|
require_prod_lifecycle_allowed "$command"
|
|
779
|
+
require_migrations_allowed "$command"
|
|
705
780
|
run_script ./scripts/update.sh "$@"
|
|
706
781
|
;;
|
|
707
782
|
"test")
|
|
708
783
|
shift
|
|
709
784
|
validate_test_args "$@"
|
|
710
785
|
require_prod_lifecycle_allowed "$command"
|
|
786
|
+
require_migrations_allowed "$command"
|
|
711
787
|
run_script ./scripts/test.sh "$@"
|
|
712
788
|
;;
|
|
713
789
|
"resetdb")
|
|
714
790
|
shift
|
|
715
791
|
positional_args "$command" 0 2 "$@"
|
|
716
792
|
require_destructive_allowed "$command"
|
|
793
|
+
require_recent_snapshot_or_override "$command"
|
|
717
794
|
run_script ./scripts/resetdb.sh "$@"
|
|
718
795
|
;;
|
|
719
796
|
"snapshot")
|
|
@@ -732,6 +809,7 @@ case "$command" in
|
|
|
732
809
|
restore_args+=("$@")
|
|
733
810
|
if [[ "\${restore_args[0]:-}" != "--dry-run" ]]; then
|
|
734
811
|
require_destructive_allowed "$command"
|
|
812
|
+
require_recent_snapshot_or_override "$command"
|
|
735
813
|
fi
|
|
736
814
|
run_script ./scripts/restore-snapshot.sh "\${restore_args[@]}"
|
|
737
815
|
;;
|
|
@@ -753,6 +831,91 @@ case "$command" in
|
|
|
753
831
|
esac
|
|
754
832
|
`;
|
|
755
833
|
}
|
|
834
|
+
export function renderDoctorScript() {
|
|
835
|
+
return `#!/usr/bin/env bash
|
|
836
|
+
set -euo pipefail
|
|
837
|
+
|
|
838
|
+
script_dir="$(cd -- "$(dirname -- "\${BASH_SOURCE[0]}")" && pwd)"
|
|
839
|
+
root_dir="$(cd -- "$script_dir/.." && pwd)"
|
|
840
|
+
cd "$root_dir"
|
|
841
|
+
|
|
842
|
+
echo "WPMoo doctor"
|
|
843
|
+
|
|
844
|
+
issues=()
|
|
845
|
+
warnings=()
|
|
846
|
+
|
|
847
|
+
required_files=(
|
|
848
|
+
"moo"
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
required_scripts=(
|
|
852
|
+
"up.sh"
|
|
853
|
+
"down.sh"
|
|
854
|
+
"logs.sh"
|
|
855
|
+
"restart.sh"
|
|
856
|
+
"shell.sh"
|
|
857
|
+
"psql.sh"
|
|
858
|
+
"install.sh"
|
|
859
|
+
"update.sh"
|
|
860
|
+
"test.sh"
|
|
861
|
+
"resetdb.sh"
|
|
862
|
+
"snapshot.sh"
|
|
863
|
+
"restore-snapshot.sh"
|
|
864
|
+
"lint.sh"
|
|
865
|
+
"pot.sh"
|
|
866
|
+
"status.sh"
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
for file in "\${required_files[@]}"; do
|
|
870
|
+
if [[ ! -f "$file" ]]; then
|
|
871
|
+
issues+=("missing required file: $file")
|
|
872
|
+
fi
|
|
873
|
+
done
|
|
874
|
+
|
|
875
|
+
for script in "\${required_scripts[@]}"; do
|
|
876
|
+
script_path="scripts/$script"
|
|
877
|
+
if [[ ! -f "$script_path" ]]; then
|
|
878
|
+
issues+=("missing required script: $script_path")
|
|
879
|
+
continue
|
|
880
|
+
fi
|
|
881
|
+
if [[ ! -x "$script_path" ]]; then
|
|
882
|
+
issues+=("not executable: $script_path")
|
|
883
|
+
fi
|
|
884
|
+
done
|
|
885
|
+
|
|
886
|
+
if [[ ! -d scripts ]]; then
|
|
887
|
+
issues+=("missing scripts directory")
|
|
888
|
+
fi
|
|
889
|
+
|
|
890
|
+
if [[ ! -d odoo/custom/src ]]; then
|
|
891
|
+
warnings+=("odoo/custom/src is missing; add source repositories before running module workflows.")
|
|
892
|
+
fi
|
|
893
|
+
|
|
894
|
+
if [[ ! -f .wpmoo/odoo.json ]]; then
|
|
895
|
+
warnings+=("missing .wpmoo/odoo.json; run ./moo reset to initialize environment metadata.")
|
|
896
|
+
fi
|
|
897
|
+
|
|
898
|
+
if (( \${#issues[@]} > 0 )); then
|
|
899
|
+
echo "Doctor checks found issues."
|
|
900
|
+
for issue in "\${issues[@]}"; do
|
|
901
|
+
echo " - $issue"
|
|
902
|
+
done
|
|
903
|
+
if (( \${#warnings[@]} > 0 )); then
|
|
904
|
+
for warning in "\${warnings[@]}"; do
|
|
905
|
+
echo " - warning: $warning"
|
|
906
|
+
done
|
|
907
|
+
fi
|
|
908
|
+
exit 1
|
|
909
|
+
fi
|
|
910
|
+
|
|
911
|
+
echo "Doctor checks passed."
|
|
912
|
+
if (( \${#warnings[@]} > 0 )); then
|
|
913
|
+
for warning in "\${warnings[@]}"; do
|
|
914
|
+
echo " - warning: $warning"
|
|
915
|
+
done
|
|
916
|
+
fi
|
|
917
|
+
`;
|
|
918
|
+
}
|
|
756
919
|
export function renderStatusScript() {
|
|
757
920
|
return `#!/usr/bin/env bash
|
|
758
921
|
set -euo pipefail
|
|
@@ -1360,7 +1523,7 @@ Useful maintenance commands:
|
|
|
1360
1523
|
Daily script delegation vs package fallback:
|
|
1361
1524
|
- \`./moo start\`, \`logs\`, \`install\`, \`update\`, \`test\`, \`snapshot\`, and related runtime tasks delegate to local \`./scripts/*.sh\`.
|
|
1362
1525
|
- \`./moo status\` runs local offline metadata checks through \`./scripts/status.sh\`.
|
|
1363
|
-
- \`./moo doctor\`
|
|
1526
|
+
- \`./moo doctor\` runs local checks first and uses package fallback for advanced usage, routed via \`npx --yes ${fallbackPackageSpec()} doctor\`.
|
|
1364
1527
|
|
|
1365
1528
|
Only report completion after the relevant update/test/lint command exits cleanly.
|
|
1366
1529
|
`;
|