producs 1.0.1 → 3.0.0
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 +245 -0
- package/README.pd +352 -0
- package/cli.js +103 -81
- package/examples/checks.pd +126 -0
- package/examples/demo.pd +113 -0
- package/examples/deploy.pd +43 -0
- package/index.js +1079 -149
- package/package.json +29 -11
package/cli.js
CHANGED
|
@@ -1,92 +1,114 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
// ProDucs CLI — node cli.js <file.pd> [flags]
|
|
6
|
+
// producs <file.pd> [flags] (when installed globally)
|
|
7
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
4
10
|
const path = require("path");
|
|
5
|
-
const
|
|
6
|
-
const { ProDucs } = require("./producs.js");
|
|
7
|
-
|
|
8
|
-
const PAGES_DIR = "/usr/share/producs";
|
|
9
|
-
const program = new Command();
|
|
10
|
-
|
|
11
|
-
// ───────────────────────────────────────────────
|
|
12
|
-
// Utility functions
|
|
13
|
-
// ───────────────────────────────────────────────
|
|
14
|
-
function listPages(showDesc = false) {
|
|
15
|
-
if (!fs.existsSync(PAGES_DIR)) {
|
|
16
|
-
console.log(chalk.red(`No pages directory found at ${PAGES_DIR}`));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
11
|
+
const { ProDucs, ProDucsError } = require("./index.js");
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
13
|
+
// ─── ANSI (minimal, used before env is up) ────────────────────────────────────
|
|
14
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
15
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
16
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
17
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
desc = " — " + firstLine.trim();
|
|
34
|
-
} catch {}
|
|
35
|
-
}
|
|
36
|
-
console.log(" • " + chalk.green(name) + chalk.gray(desc));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
19
|
+
const HELP = `
|
|
20
|
+
${bold(cyan("ProDucs"))} — Professional Documentation Shell DSL ${dim("v3.0.0")}
|
|
21
|
+
${dim("─".repeat(60))}
|
|
22
|
+
${bold("Usage:")}
|
|
23
|
+
producs <file.pd> [flags]
|
|
24
|
+
producs --demo
|
|
25
|
+
producs --help
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
27
|
+
${bold("Flags:")}
|
|
28
|
+
${bold("--debug")} Verbose debug output to stderr
|
|
29
|
+
${bold("--strict")} Halt on any shell command failure
|
|
30
|
+
${bold("--no-color")} Disable ANSI colors
|
|
31
|
+
${bold("--ast")} Dump AST as JSON and exit (no execution)
|
|
32
|
+
${bold("--demo")} Run the built-in feature demo
|
|
33
|
+
${bold("--help")} Show this message
|
|
34
|
+
${bold("--version")} Print version
|
|
35
|
+
|
|
36
|
+
${bold("Syntax Quick-Ref:")}
|
|
37
|
+
${cyan("#$Cmd['arg1','arg2']")} inline command
|
|
38
|
+
${cyan("#$Cmd1 | #$Cmd2 | #$Cmd3")} pipe chain (left→right)
|
|
39
|
+
${cyan("#$Block['title']")} open a block
|
|
40
|
+
${cyan("#$End_Block")} close a block
|
|
41
|
+
${cyan("// comment")} line comment
|
|
42
|
+
${cyan("${varname}")} variable interpolation
|
|
43
|
+
|
|
44
|
+
${bold("Branching (replaces if/else):")}
|
|
45
|
+
${cyan("#$Check_eq['expected']")} sets failed=0 (pass) or failed=1 (fail)
|
|
46
|
+
${cyan("#$Branch")} runs children only when failed=0
|
|
47
|
+
${cyan(" #$Print['it passed']")}
|
|
48
|
+
${cyan("#$End_Branch")}
|
|
49
|
+
${cyan("#$BranchElse")} runs children only when failed=1
|
|
50
|
+
${cyan(" #$Print['it failed']")}
|
|
51
|
+
${cyan("#$End_BranchElse")}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
${bold("All Check_* commands:")}
|
|
54
|
+
Check_eq Check_neq Check_contains Check_not_contains
|
|
55
|
+
Check_starts Check_ends Check_matches Check_empty
|
|
56
|
+
Check_nonempty Check_gt Check_gte Check_lt
|
|
57
|
+
Check_lte Check_num_eq Check_num_neq Check_between
|
|
58
|
+
Check_failed Check_passed Check_var_eq Check_var_neq
|
|
59
|
+
Check_failed_error Check_passed_error
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
${bold("File extension:")} .pd (ProDucs script)
|
|
62
|
+
${dim("─".repeat(60))}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const DEMO = require("fs").readFileSync(
|
|
66
|
+
require("path").join(__dirname, "examples", "demo.pd"), "utf8"
|
|
67
|
+
);
|
|
68
|
+
// ─── Parse argv ───────────────────────────────────────────────────────────────
|
|
69
|
+
const argv = process.argv.slice(2);
|
|
70
|
+
const flags = new Set(argv.filter(a => a.startsWith("--")));
|
|
71
|
+
const files = argv.filter(a => !a.startsWith("--"));
|
|
72
|
+
|
|
73
|
+
if (flags.has("--version")) { process.stdout.write("ProDucs v3.0.0\n"); process.exit(0); }
|
|
74
|
+
if (flags.has("--help") || process.argv.length === 2) { process.stdout.write(HELP); process.exit(0); }
|
|
75
|
+
|
|
76
|
+
const opts = {
|
|
77
|
+
debug: flags.has("--debug"),
|
|
78
|
+
strict: flags.has("--strict"),
|
|
79
|
+
noColor: flags.has("--no-color"),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const prod = new ProDucs(opts);
|
|
83
|
+
|
|
84
|
+
// ─── Demo ─────────────────────────────────────────────────────────────────────
|
|
85
|
+
if (flags.has("--demo")) {
|
|
86
|
+
try { prod.run(DEMO); }
|
|
87
|
+
catch (e) {
|
|
88
|
+
process.stderr.write(red(`\n${e.name||"Error"}: ${e.message}\n`));
|
|
89
|
+
if (opts.debug && e.stack) process.stderr.write(dim(e.stack)+"\n");
|
|
62
90
|
process.exit(1);
|
|
63
91
|
}
|
|
92
|
+
process.exit(0);
|
|
64
93
|
}
|
|
65
94
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
.
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.argument("[page]", "Run a page directly (shortcut for 'run <page>')")
|
|
87
|
-
.action((page) => {
|
|
88
|
-
if (page) runPage(page);
|
|
89
|
-
else listPages(true);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
program.parse(process.argv);
|
|
95
|
+
// ─── File execution ───────────────────────────────────────────────────────────
|
|
96
|
+
const filePath = path.resolve(files[0]);
|
|
97
|
+
let src;
|
|
98
|
+
try { src = fs.readFileSync(filePath, "utf8"); }
|
|
99
|
+
catch (e) {
|
|
100
|
+
process.stderr.write(red(`Cannot read: ${filePath}\n`));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (flags.has("--ast")) {
|
|
105
|
+
console.log(JSON.stringify(prod.astify(src), null, 2));
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try { prod.run(src); }
|
|
110
|
+
catch (e) {
|
|
111
|
+
process.stderr.write(red(`\n${e.name||"Error"}: ${e.message}\n`));
|
|
112
|
+
if (opts.debug && e.stack) process.stderr.write(dim(e.stack)+"\n");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// checks.pd — showcase all Check_* commands
|
|
2
|
+
|
|
3
|
+
#$Header['ProDucs Check Commands Demo']
|
|
4
|
+
|
|
5
|
+
// ── String Checks ─────────────────────────────────────────────────
|
|
6
|
+
#$Section['String Checks on VALUE']
|
|
7
|
+
|
|
8
|
+
#$Set['val','Hello World']
|
|
9
|
+
|
|
10
|
+
#$Set['val','${val}'] | #$Check_eq['Hello World']
|
|
11
|
+
#$Branch
|
|
12
|
+
#$Color['green','Check_eq: PASS']
|
|
13
|
+
#$End_Branch
|
|
14
|
+
|
|
15
|
+
#$Set['val','${val}'] | #$Check_contains['World']
|
|
16
|
+
#$Branch
|
|
17
|
+
#$Color['green','Check_contains: PASS']
|
|
18
|
+
#$End_Branch
|
|
19
|
+
|
|
20
|
+
#$Set['val','${val}'] | #$Check_starts['Hello']
|
|
21
|
+
#$Branch
|
|
22
|
+
#$Color['green','Check_starts: PASS']
|
|
23
|
+
#$End_Branch
|
|
24
|
+
|
|
25
|
+
#$Set['val','${val}'] | #$Check_ends['World']
|
|
26
|
+
#$Branch
|
|
27
|
+
#$Color['green','Check_ends: PASS']
|
|
28
|
+
#$End_Branch
|
|
29
|
+
|
|
30
|
+
#$Set['val','${val}'] | #$Check_matches['^Hello']
|
|
31
|
+
#$Branch
|
|
32
|
+
#$Color['green','Check_matches: PASS']
|
|
33
|
+
#$End_Branch
|
|
34
|
+
|
|
35
|
+
#$Set['empty','']
|
|
36
|
+
#$Set['empty','${empty}'] | #$Check_empty
|
|
37
|
+
#$Branch
|
|
38
|
+
#$Color['green','Check_empty: PASS']
|
|
39
|
+
#$End_Branch
|
|
40
|
+
|
|
41
|
+
#$Set['val','${val}'] | #$Check_nonempty
|
|
42
|
+
#$Branch
|
|
43
|
+
#$Color['green','Check_nonempty: PASS']
|
|
44
|
+
#$End_Branch
|
|
45
|
+
|
|
46
|
+
#$End_Section
|
|
47
|
+
|
|
48
|
+
// ── Numeric Checks ────────────────────────────────────────────────
|
|
49
|
+
#$Section['Numeric Checks']
|
|
50
|
+
|
|
51
|
+
#$Set['score','85']
|
|
52
|
+
|
|
53
|
+
#$Check_gt['score','80']
|
|
54
|
+
#$Branch
|
|
55
|
+
#$Color['green','Check_gt score > 80: PASS']
|
|
56
|
+
#$End_Branch
|
|
57
|
+
|
|
58
|
+
#$Check_gte['score','85']
|
|
59
|
+
#$Branch
|
|
60
|
+
#$Color['green','Check_gte score >= 85: PASS']
|
|
61
|
+
#$End_Branch
|
|
62
|
+
|
|
63
|
+
#$Check_lt['score','90']
|
|
64
|
+
#$Branch
|
|
65
|
+
#$Color['green','Check_lt score < 90: PASS']
|
|
66
|
+
#$End_Branch
|
|
67
|
+
|
|
68
|
+
#$Check_lte['score','85']
|
|
69
|
+
#$Branch
|
|
70
|
+
#$Color['green','Check_lte score <= 85: PASS']
|
|
71
|
+
#$End_Branch
|
|
72
|
+
|
|
73
|
+
#$Check_num_eq['score','85']
|
|
74
|
+
#$Branch
|
|
75
|
+
#$Color['green','Check_num_eq score == 85: PASS']
|
|
76
|
+
#$End_Branch
|
|
77
|
+
|
|
78
|
+
#$Check_between['score','80','90']
|
|
79
|
+
#$Branch
|
|
80
|
+
#$Color['green','Check_between score in [80,90]: PASS']
|
|
81
|
+
#$End_Branch
|
|
82
|
+
|
|
83
|
+
#$End_Section
|
|
84
|
+
|
|
85
|
+
// ── Variable Comparison ───────────────────────────────────────────
|
|
86
|
+
#$Section['Variable Comparison']
|
|
87
|
+
|
|
88
|
+
#$Set['a','hello']
|
|
89
|
+
#$Set['b','hello']
|
|
90
|
+
#$Set['c','world']
|
|
91
|
+
|
|
92
|
+
#$Check_var_eq['a','b']
|
|
93
|
+
#$Branch
|
|
94
|
+
#$Color['green','Check_var_eq a===b: PASS']
|
|
95
|
+
#$End_Branch
|
|
96
|
+
|
|
97
|
+
#$Check_var_neq['a','c']
|
|
98
|
+
#$Branch
|
|
99
|
+
#$Color['green','Check_var_neq a!==c: PASS']
|
|
100
|
+
#$End_Branch
|
|
101
|
+
|
|
102
|
+
#$End_Section
|
|
103
|
+
|
|
104
|
+
// ── Result / State Checks ─────────────────────────────────────────
|
|
105
|
+
#$Section['Result State Checks']
|
|
106
|
+
|
|
107
|
+
// Force a fail state
|
|
108
|
+
#$Set['x','5'] | #$Check_gt['10']
|
|
109
|
+
|
|
110
|
+
#$Check_failed
|
|
111
|
+
#$Branch
|
|
112
|
+
#$Color['green','Check_failed: PASS (previous check did fail)']
|
|
113
|
+
#$End_Branch
|
|
114
|
+
|
|
115
|
+
// Now pass
|
|
116
|
+
#$Set['x','15'] | #$Check_gt['10']
|
|
117
|
+
|
|
118
|
+
#$Check_passed
|
|
119
|
+
#$Branch
|
|
120
|
+
#$Color['green','Check_passed: PASS (previous check passed)']
|
|
121
|
+
#$End_Branch
|
|
122
|
+
|
|
123
|
+
#$End_Section
|
|
124
|
+
|
|
125
|
+
#$HR
|
|
126
|
+
#$Status['All checks demonstrated']
|
package/examples/demo.pd
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#$Header['ProDucs v3.0 Demo']
|
|
2
|
+
|
|
3
|
+
// ── Variables & Math ──────────────────────────────────────────────
|
|
4
|
+
#$Section['Variables & Math']
|
|
5
|
+
#$Set['x','40']
|
|
6
|
+
#$Set['y','2']
|
|
7
|
+
#$Math['x * y'] | #$Set['product']
|
|
8
|
+
#$Print['${x} * ${y} = ${product}']
|
|
9
|
+
#$Inc['x','5']
|
|
10
|
+
#$Print['x after Inc: ${x}']
|
|
11
|
+
#$End_Section
|
|
12
|
+
|
|
13
|
+
// ── Pipe chain ────────────────────────────────────────────────────
|
|
14
|
+
#$Section['Pipe Chain']
|
|
15
|
+
#$Set['msg',' hello world ']
|
|
16
|
+
#$Set['msg','${msg}'] | #$Trim | #$Upper | #$Set['msg']
|
|
17
|
+
#$Print['Processed: ${msg}']
|
|
18
|
+
#$Len | #$Set['len']
|
|
19
|
+
#$Print['Length: ${len}']
|
|
20
|
+
#$End_Section
|
|
21
|
+
|
|
22
|
+
// ── Branch on string check ────────────────────────────────────────
|
|
23
|
+
#$Section['Branch: String Check']
|
|
24
|
+
#$Set['role','admin']
|
|
25
|
+
#$Set['role','${role}'] | #$Check_eq['admin']
|
|
26
|
+
#$Branch
|
|
27
|
+
#$Color['green','✔ Role is admin']
|
|
28
|
+
#$End_Branch
|
|
29
|
+
#$BranchElse
|
|
30
|
+
#$Color['red','✖ Role is not admin']
|
|
31
|
+
#$End_BranchElse
|
|
32
|
+
#$End_Section
|
|
33
|
+
|
|
34
|
+
// ── Branch on numeric check ───────────────────────────────────────
|
|
35
|
+
#$Section['Branch: Numeric Check']
|
|
36
|
+
#$Set['score','87']
|
|
37
|
+
#$Check_gte['score','80']
|
|
38
|
+
#$Branch
|
|
39
|
+
#$Color['green','✔ Score ${score} >= 80: Pass']
|
|
40
|
+
#$End_Branch
|
|
41
|
+
#$BranchElse
|
|
42
|
+
#$Color['red','✖ Score ${score} < 80: Fail']
|
|
43
|
+
#$End_BranchElse
|
|
44
|
+
|
|
45
|
+
#$Check_between['score','90','100']
|
|
46
|
+
#$Branch
|
|
47
|
+
#$Print['Score is A-grade']
|
|
48
|
+
#$End_Branch
|
|
49
|
+
#$BranchElse
|
|
50
|
+
#$Print['Score is not A-grade (${score} not in 90-100)']
|
|
51
|
+
#$End_BranchElse
|
|
52
|
+
#$End_Section
|
|
53
|
+
|
|
54
|
+
// ── Repeat ────────────────────────────────────────────────────────
|
|
55
|
+
#$Section['Repeat']
|
|
56
|
+
#$Repeat['3']
|
|
57
|
+
#$Print['Iteration ${_i}']
|
|
58
|
+
#$End_Repeat
|
|
59
|
+
#$End_Section
|
|
60
|
+
|
|
61
|
+
// ── ForEach ───────────────────────────────────────────────────────
|
|
62
|
+
#$Section['ForEach']
|
|
63
|
+
#$ForEach['lang','Nova','C','Zig']
|
|
64
|
+
#$Print['Language: ${lang}']
|
|
65
|
+
#$End_ForEach
|
|
66
|
+
#$End_Section
|
|
67
|
+
|
|
68
|
+
// ── Goto & Label ─────────────────────────────────────────────────
|
|
69
|
+
#$Section['Goto & Label']
|
|
70
|
+
#$Set['skip','yes']
|
|
71
|
+
#$Set['skip','${skip}'] | #$Check_eq['yes']
|
|
72
|
+
#$Branch
|
|
73
|
+
#$Goto['after_block']
|
|
74
|
+
#$End_Branch
|
|
75
|
+
#$Print['THIS MUST NOT APPEAR']
|
|
76
|
+
#$Label['after_block']
|
|
77
|
+
#$Print['✔ Jumped here via Goto']
|
|
78
|
+
#$End_Section
|
|
79
|
+
|
|
80
|
+
// ── Try / Catch ───────────────────────────────────────────────────
|
|
81
|
+
#$Section['Try / Catch']
|
|
82
|
+
#$Try
|
|
83
|
+
#$Assert['1===2','intentional failure']
|
|
84
|
+
#$Catch
|
|
85
|
+
#$Print['Caught: ${_error}']
|
|
86
|
+
#$End_Catch
|
|
87
|
+
#$End_Try
|
|
88
|
+
#$End_Section
|
|
89
|
+
|
|
90
|
+
// ── Wait ──────────────────────────────────────────────────────────
|
|
91
|
+
#$Section['Wait']
|
|
92
|
+
#$Spinner['Waiting 300ms...']
|
|
93
|
+
#$Wait['300']
|
|
94
|
+
#$Color['green','Done.']
|
|
95
|
+
#$End_Section
|
|
96
|
+
|
|
97
|
+
// ── Table ─────────────────────────────────────────────────────────
|
|
98
|
+
#$Section['Table']
|
|
99
|
+
#$Table
|
|
100
|
+
#$Row['Command','Category','Description']
|
|
101
|
+
#$Row['Check_gt','Numeric','Greater-than check']
|
|
102
|
+
#$Row['Check_between','Numeric','Range check']
|
|
103
|
+
#$Row['Branch','Control','Run if check passed']
|
|
104
|
+
#$Row['BranchElse','Control','Run if check failed']
|
|
105
|
+
#$Row['Goto','Control','Jump to label']
|
|
106
|
+
#$Row['Wait','Timing','Blocking sleep (ms)']
|
|
107
|
+
#$Row['Shell_capture','Shell','Capture stdout']
|
|
108
|
+
#$End_Table
|
|
109
|
+
#$End_Section
|
|
110
|
+
|
|
111
|
+
#$HR['=']
|
|
112
|
+
#$Status['Demo complete']
|
|
113
|
+
#$Dump_log
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// deploy.pd — deployment checklist example
|
|
2
|
+
|
|
3
|
+
#$Header['Deploy Checklist']
|
|
4
|
+
|
|
5
|
+
#$Section['Pre-flight Checks']
|
|
6
|
+
#$Require_cmd['git']
|
|
7
|
+
#$Require_cmd['node']
|
|
8
|
+
#$Color['green','✔ Required commands found']
|
|
9
|
+
|
|
10
|
+
#$Timestamp['iso'] | #$Set['deploy_time']
|
|
11
|
+
#$Print['Deploy started: ${deploy_time}']
|
|
12
|
+
#$End_Section
|
|
13
|
+
|
|
14
|
+
#$Section['Branch Check']
|
|
15
|
+
#$Shell_capture['git branch --show-current'] | #$Set['branch']
|
|
16
|
+
#$Print['Current branch: ${branch}']
|
|
17
|
+
|
|
18
|
+
#$Set['branch','${branch}'] | #$Check_eq['main']
|
|
19
|
+
#$Branch
|
|
20
|
+
#$Color['green','✔ On main branch']
|
|
21
|
+
#$End_Branch
|
|
22
|
+
#$BranchElse
|
|
23
|
+
#$Warn['Not on main branch — deploying ${branch}']
|
|
24
|
+
#$Confirm['Continue anyway?','n']
|
|
25
|
+
#$Check_passed_error['Aborted by user']
|
|
26
|
+
#$End_BranchElse
|
|
27
|
+
#$End_Section
|
|
28
|
+
|
|
29
|
+
#$Section['Build Steps']
|
|
30
|
+
#$Step['Install dependencies']
|
|
31
|
+
#$Shell_run['npm ci']
|
|
32
|
+
#$Status['npm ci']
|
|
33
|
+
#$End_Step
|
|
34
|
+
|
|
35
|
+
#$Step['Run tests']
|
|
36
|
+
#$Shell_run['npm test']
|
|
37
|
+
#$Status['npm test']
|
|
38
|
+
#$End_Step
|
|
39
|
+
#$End_Section
|
|
40
|
+
|
|
41
|
+
#$HR['═']
|
|
42
|
+
#$Status['Deploy complete']
|
|
43
|
+
#$Dump_log
|