aw-spec-cli 0.1.22 → 0.1.23
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/assets/.appweaver/orchestrator/orchestrator.py +47 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +5 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +3 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +21 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +21 -3
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +13 -3
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +9 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +3 -3
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +5 -123
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +784 -0
- package/assets/foundations/java/Hexagonal/pack-foundation.sh +357 -0
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +399 -0
- package/package.json +1 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# unpack-foundation.sh — Java foundation unpacker for AW-Spec
|
|
4
|
+
#
|
|
5
|
+
# Unpacks an AW-Spec Java foundation pack into a target application,
|
|
6
|
+
# replacing the Java package sentinel with the target application package.
|
|
7
|
+
#
|
|
8
|
+
# Java foundation contract:
|
|
9
|
+
# Sentinel: io.appweaver
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
# ./unpack-foundation.sh \
|
|
13
|
+
# --pack-file ./Hexagonal.pack.txt \
|
|
14
|
+
# --target-app-name FlightBooking \
|
|
15
|
+
# --out-dir backend
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# ----------------------------------------------------------- defaults --------------
|
|
21
|
+
|
|
22
|
+
PACK_FILE=''
|
|
23
|
+
TARGET_APP_NAME=''
|
|
24
|
+
OUT_DIR='src'
|
|
25
|
+
SENTINEL=''
|
|
26
|
+
FORCE=0
|
|
27
|
+
DRYRUN=0
|
|
28
|
+
|
|
29
|
+
# ----------------------------------------------------------- usage ----------------
|
|
30
|
+
|
|
31
|
+
usage() {
|
|
32
|
+
cat <<'EOF'
|
|
33
|
+
Usage:
|
|
34
|
+
unpack-foundation.sh \
|
|
35
|
+
--pack-file <pack-file> \
|
|
36
|
+
--target-app-name <app-name> \
|
|
37
|
+
--out-dir <output-dir> \
|
|
38
|
+
[--sentinel <sentinel>] \
|
|
39
|
+
[--force] \
|
|
40
|
+
[--dry-run]
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--pack-file Foundation pack file.
|
|
44
|
+
--target-app-name Target application name.
|
|
45
|
+
--out-dir Output directory.
|
|
46
|
+
--sentinel Optional sentinel override.
|
|
47
|
+
--force Allow existing destination files to be overwritten.
|
|
48
|
+
--dry-run Show what would be written without changing files.
|
|
49
|
+
-h, --help Show this help.
|
|
50
|
+
EOF
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ----------------------------------------------------------- error -----------------
|
|
54
|
+
|
|
55
|
+
error() {
|
|
56
|
+
echo "Error: $*" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# ----------------------------------------------------------- arguments ------------
|
|
61
|
+
|
|
62
|
+
while [ "$#" -gt 0 ]; do
|
|
63
|
+
case "$1" in
|
|
64
|
+
--pack-file)
|
|
65
|
+
[ "$#" -ge 2 ] || error "--pack-file requires a value"
|
|
66
|
+
PACK_FILE="$2"
|
|
67
|
+
shift 2
|
|
68
|
+
;;
|
|
69
|
+
|
|
70
|
+
--target-app-name)
|
|
71
|
+
[ "$#" -ge 2 ] || error "--target-app-name requires a value"
|
|
72
|
+
TARGET_APP_NAME="$2"
|
|
73
|
+
shift 2
|
|
74
|
+
;;
|
|
75
|
+
|
|
76
|
+
--out-dir)
|
|
77
|
+
[ "$#" -ge 2 ] || error "--out-dir requires a value"
|
|
78
|
+
OUT_DIR="$2"
|
|
79
|
+
shift 2
|
|
80
|
+
;;
|
|
81
|
+
|
|
82
|
+
--sentinel)
|
|
83
|
+
[ "$#" -ge 2 ] || error "--sentinel requires a value"
|
|
84
|
+
SENTINEL="$2"
|
|
85
|
+
shift 2
|
|
86
|
+
;;
|
|
87
|
+
|
|
88
|
+
--force)
|
|
89
|
+
FORCE=1
|
|
90
|
+
shift
|
|
91
|
+
;;
|
|
92
|
+
|
|
93
|
+
--dry-run)
|
|
94
|
+
DRYRUN=1
|
|
95
|
+
shift
|
|
96
|
+
;;
|
|
97
|
+
|
|
98
|
+
-h|--help)
|
|
99
|
+
usage
|
|
100
|
+
exit 0
|
|
101
|
+
;;
|
|
102
|
+
|
|
103
|
+
*)
|
|
104
|
+
error "Unknown argument: $1"
|
|
105
|
+
;;
|
|
106
|
+
esac
|
|
107
|
+
done
|
|
108
|
+
|
|
109
|
+
# ----------------------------------------------------------- validate args ----------
|
|
110
|
+
|
|
111
|
+
[ -n "$PACK_FILE" ] || error "--pack-file is required"
|
|
112
|
+
[ -n "$TARGET_APP_NAME" ] || error "--target-app-name is required"
|
|
113
|
+
|
|
114
|
+
[ -f "$PACK_FILE" ] || error "Pack file not found: $PACK_FILE"
|
|
115
|
+
|
|
116
|
+
case "$TARGET_APP_NAME" in
|
|
117
|
+
*[!A-Za-z0-9._-]*)
|
|
118
|
+
error "Invalid application name '$TARGET_APP_NAME'. Allowed characters: A-Z a-z 0-9 . _ -"
|
|
119
|
+
;;
|
|
120
|
+
esac
|
|
121
|
+
|
|
122
|
+
# ----------------------------------------------------------- read pack metadata ----
|
|
123
|
+
|
|
124
|
+
PACK_SENTINEL=''
|
|
125
|
+
|
|
126
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
127
|
+
line="${line%$'\r'}"
|
|
128
|
+
|
|
129
|
+
case "$line" in
|
|
130
|
+
'# sentinel: '*)
|
|
131
|
+
PACK_SENTINEL="${line#'# sentinel: '}"
|
|
132
|
+
break
|
|
133
|
+
;;
|
|
134
|
+
esac
|
|
135
|
+
done < "$PACK_FILE"
|
|
136
|
+
|
|
137
|
+
if [ -n "$SENTINEL" ]; then
|
|
138
|
+
:
|
|
139
|
+
elif [ -n "$PACK_SENTINEL" ]; then
|
|
140
|
+
SENTINEL="$PACK_SENTINEL"
|
|
141
|
+
else
|
|
142
|
+
error "Could not determine sentinel from pack. Use --sentinel."
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
[ -n "$SENTINEL" ] || error "Sentinel cannot be empty"
|
|
146
|
+
|
|
147
|
+
# ----------------------------------------------------------- Java replacement -------
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# Java package rules:
|
|
151
|
+
#
|
|
152
|
+
# io.appweaver
|
|
153
|
+
# ->
|
|
154
|
+
# <lowercase application name>
|
|
155
|
+
#
|
|
156
|
+
# io/appweaver
|
|
157
|
+
# ->
|
|
158
|
+
# <lowercase application name>
|
|
159
|
+
#
|
|
160
|
+
# The application directory itself keeps the original user-provided
|
|
161
|
+
# application name/casing.
|
|
162
|
+
#
|
|
163
|
+
|
|
164
|
+
TARGET_PACKAGE="$(printf '%s' "$TARGET_APP_NAME" | tr '[:upper:]' '[:lower:]')"
|
|
165
|
+
|
|
166
|
+
S_DOT="$SENTINEL"
|
|
167
|
+
T_DOT="$TARGET_PACKAGE"
|
|
168
|
+
|
|
169
|
+
S_PATH="$(printf '%s' "$SENTINEL" | tr '.' '/')"
|
|
170
|
+
T_PATH="$TARGET_PACKAGE"
|
|
171
|
+
|
|
172
|
+
FROM=()
|
|
173
|
+
TO=()
|
|
174
|
+
|
|
175
|
+
add_form() {
|
|
176
|
+
FROM+=("$1")
|
|
177
|
+
TO+=("$2")
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
add_form "$S_DOT" "$T_DOT"
|
|
181
|
+
add_form "$S_PATH" "$T_PATH"
|
|
182
|
+
|
|
183
|
+
# ----------------------------------------------------------- replacement ------------
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# Apply replacements from longest source string to shortest.
|
|
187
|
+
# This avoids partial replacement problems when one replacement
|
|
188
|
+
# string is a prefix of another.
|
|
189
|
+
#
|
|
190
|
+
|
|
191
|
+
apply_repl() {
|
|
192
|
+
local input="$1"
|
|
193
|
+
local i
|
|
194
|
+
local from
|
|
195
|
+
local to
|
|
196
|
+
|
|
197
|
+
REPL_OUT="$input"
|
|
198
|
+
|
|
199
|
+
for i in "${!FROM[@]}"; do
|
|
200
|
+
from="${FROM[$i]}"
|
|
201
|
+
to="${TO[$i]}"
|
|
202
|
+
|
|
203
|
+
REPL_OUT="${REPL_OUT//$from/$to}"
|
|
204
|
+
done
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# ----------------------------------------------------------- output root ------------
|
|
208
|
+
|
|
209
|
+
if [ "$OUT_DIR" = "." ]; then
|
|
210
|
+
OUT_ROOT="$TARGET_APP_NAME"
|
|
211
|
+
else
|
|
212
|
+
OUT_ROOT="$OUT_DIR/$TARGET_APP_NAME"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# ----------------------------------------------------------- parse pack blocks -------
|
|
216
|
+
|
|
217
|
+
rels=()
|
|
218
|
+
encs=()
|
|
219
|
+
contents=()
|
|
220
|
+
dir_rels=()
|
|
221
|
+
|
|
222
|
+
in_block=0
|
|
223
|
+
cur_rel=''
|
|
224
|
+
cur_enc=''
|
|
225
|
+
cur_content=''
|
|
226
|
+
first_line=1
|
|
227
|
+
|
|
228
|
+
file_re='^<<<FILE path="([^"]+)"( encoding="([^"]+)")?>>>[[:space:]]*$'
|
|
229
|
+
dir_re='^<<<DIR path="([^"]+)">>>[[:space:]]*$'
|
|
230
|
+
|
|
231
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
232
|
+
line="${line%$'\r'}"
|
|
233
|
+
|
|
234
|
+
if [ "$in_block" -eq 0 ]; then
|
|
235
|
+
|
|
236
|
+
if [[ "$line" =~ $file_re ]]; then
|
|
237
|
+
cur_rel="${BASH_REMATCH[1]}"
|
|
238
|
+
cur_enc="${BASH_REMATCH[3]:-}"
|
|
239
|
+
in_block=1
|
|
240
|
+
first_line=1
|
|
241
|
+
cur_content=''
|
|
242
|
+
|
|
243
|
+
elif [[ "$line" =~ $dir_re ]]; then
|
|
244
|
+
dir_rels+=("${BASH_REMATCH[1]}")
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
else
|
|
248
|
+
|
|
249
|
+
if [ "$line" = '<<<ENDFILE>>>' ]; then
|
|
250
|
+
rels+=("$cur_rel")
|
|
251
|
+
encs+=("$cur_enc")
|
|
252
|
+
contents+=("$cur_content")
|
|
253
|
+
|
|
254
|
+
in_block=0
|
|
255
|
+
|
|
256
|
+
elif [ "$first_line" -eq 1 ]; then
|
|
257
|
+
cur_content="$line"
|
|
258
|
+
first_line=0
|
|
259
|
+
|
|
260
|
+
else
|
|
261
|
+
cur_content="$cur_content"$'\n'"$line"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
done < "$PACK_FILE"
|
|
267
|
+
|
|
268
|
+
[ "${#rels[@]}" -gt 0 ] || {
|
|
269
|
+
echo "Error: No <<<FILE>>> blocks found in pack: $PACK_FILE" >&2
|
|
270
|
+
exit 1
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# ----------------------------------------------------------- resolve directories ----
|
|
274
|
+
|
|
275
|
+
is_empty_java_package_parent() {
|
|
276
|
+
local rel="$1"
|
|
277
|
+
|
|
278
|
+
case "$rel" in
|
|
279
|
+
src/main/java/io|src/test/java/io)
|
|
280
|
+
return 0
|
|
281
|
+
;;
|
|
282
|
+
*)
|
|
283
|
+
return 1
|
|
284
|
+
;;
|
|
285
|
+
esac
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
dir_dests=()
|
|
289
|
+
|
|
290
|
+
for rel in "${dir_rels[@]}"; do
|
|
291
|
+
if is_empty_java_package_parent "$rel"; then
|
|
292
|
+
continue
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
apply_repl "$rel"
|
|
296
|
+
dir_dests+=("$OUT_ROOT/$REPL_OUT")
|
|
297
|
+
done
|
|
298
|
+
|
|
299
|
+
# ----------------------------------------------------------- resolve files ----------
|
|
300
|
+
|
|
301
|
+
dests=()
|
|
302
|
+
|
|
303
|
+
for rel in "${rels[@]}"; do
|
|
304
|
+
apply_repl "$rel"
|
|
305
|
+
dests+=("$OUT_ROOT/$REPL_OUT")
|
|
306
|
+
done
|
|
307
|
+
|
|
308
|
+
# ----------------------------------------------------------- conflict guard ----------
|
|
309
|
+
|
|
310
|
+
if [ "$FORCE" -eq 0 ] && [ "$DRYRUN" -eq 0 ]; then
|
|
311
|
+
|
|
312
|
+
for dest in "${dests[@]}"; do
|
|
313
|
+
if [ -e "$dest" ]; then
|
|
314
|
+
error "Destination already exists: $dest (use --force to overwrite)"
|
|
315
|
+
fi
|
|
316
|
+
done
|
|
317
|
+
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
# ----------------------------------------------------------- base64 decoder ----------
|
|
321
|
+
|
|
322
|
+
decode_base64() {
|
|
323
|
+
if base64 --help 2>&1 | grep -q -- '--decode'; then
|
|
324
|
+
base64 --decode
|
|
325
|
+
else
|
|
326
|
+
base64 -D
|
|
327
|
+
fi
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
# ----------------------------------------------------------- create directories -------
|
|
331
|
+
|
|
332
|
+
directories_created=0
|
|
333
|
+
|
|
334
|
+
if [ "$DRYRUN" -eq 1 ]; then
|
|
335
|
+
|
|
336
|
+
for dest in "${dir_dests[@]}"; do
|
|
337
|
+
echo "WOULD CREATE DIRECTORY $dest"
|
|
338
|
+
done
|
|
339
|
+
|
|
340
|
+
else
|
|
341
|
+
|
|
342
|
+
for dest in "${dir_dests[@]}"; do
|
|
343
|
+
mkdir -p "$dest"
|
|
344
|
+
directories_created=$((directories_created + 1))
|
|
345
|
+
done
|
|
346
|
+
|
|
347
|
+
fi
|
|
348
|
+
|
|
349
|
+
# ----------------------------------------------------------- write files -------------
|
|
350
|
+
|
|
351
|
+
written=0
|
|
352
|
+
|
|
353
|
+
if [ "$DRYRUN" -eq 1 ]; then
|
|
354
|
+
|
|
355
|
+
for i in "${!rels[@]}"; do
|
|
356
|
+
echo "WOULD WRITE ${dests[$i]}"
|
|
357
|
+
done
|
|
358
|
+
|
|
359
|
+
else
|
|
360
|
+
|
|
361
|
+
for i in "${!rels[@]}"; do
|
|
362
|
+
|
|
363
|
+
dest="${dests[$i]}"
|
|
364
|
+
enc="${encs[$i]}"
|
|
365
|
+
content="${contents[$i]}"
|
|
366
|
+
|
|
367
|
+
mkdir -p "$(dirname "$dest")"
|
|
368
|
+
|
|
369
|
+
if [ "$enc" = "base64" ]; then
|
|
370
|
+
printf '%s' "$content" | decode_base64 > "$dest"
|
|
371
|
+
else
|
|
372
|
+
apply_repl "$content"
|
|
373
|
+
printf '%s\n' "$REPL_OUT" > "$dest"
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
written=$((written + 1))
|
|
377
|
+
|
|
378
|
+
done
|
|
379
|
+
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# ----------------------------------------------------------- summary -----------------
|
|
383
|
+
|
|
384
|
+
echo
|
|
385
|
+
|
|
386
|
+
if [ "$DRYRUN" -eq 1 ]; then
|
|
387
|
+
|
|
388
|
+
echo "DryRun: ${#rels[@]} file(s) and ${#dir_rels[@]} directorie(s) would be written to:"
|
|
389
|
+
echo " $OUT_ROOT"
|
|
390
|
+
|
|
391
|
+
else
|
|
392
|
+
|
|
393
|
+
echo "Unpacked $written file(s) and $directories_created directorie(s) to:"
|
|
394
|
+
echo " $OUT_ROOT"
|
|
395
|
+
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
echo " Sentinel: $SENTINEL"
|
|
399
|
+
echo " Package: $TARGET_PACKAGE"
|
package/package.json
CHANGED