plain-forge 1.0.16 → 1.0.18
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/forge/rules/definitions.md +0 -2
- package/forge/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +14 -3
- package/forge/rules/exported-concepts.md +0 -2
- package/forge/rules/func-specs.md +0 -2
- package/forge/rules/impl-reqs.md +0 -2
- package/forge/rules/import-modules.md +0 -2
- package/forge/rules/integration-embedded-testing.md +9 -9
- package/forge/rules/integration-embedded.md +4 -6
- package/forge/rules/integration-standalone.md +0 -2
- package/forge/rules/integrations.md +0 -2
- package/forge/rules/line-length.md +0 -2
- package/forge/rules/linked-resources.md +0 -2
- package/forge/rules/required-concepts.md +0 -2
- package/forge/rules/requires-modules.md +0 -2
- package/forge/rules/test-reqs.md +0 -2
- package/forge/skills/implement-conformance-testing-script/SKILL.md +56 -43
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1 +34 -21
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1 +16 -5
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +22 -11
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1 +13 -2
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +16 -6
- package/forge/skills/implement-prepare-environment-script/SKILL.md +23 -18
- package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_java.sh +8 -4
- package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_python.sh +8 -4
- package/forge/skills/implement-unit-testing-script/SKILL.md +14 -11
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_flutter.ps1 +6 -1
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_golang.ps1 +6 -1
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_java.sh +6 -3
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.ps1 +6 -1
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.sh +8 -4
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_react.ps1 +6 -2
- package/forge/skills/init-config-file/SKILL.md +5 -5
- package/forge/skills/load-plain-reference/SKILL.md +97 -42
- package/forge/skills/plain-healthcheck/SKILL.md +1 -1
- package/forge/skills/run-codeplain/SKILL.md +12 -3
- package/package.json +1 -1
package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1
CHANGED
|
@@ -69,6 +69,17 @@ function Cleanup {
|
|
|
69
69
|
if ($script:build_output -and (Test-Path $script:build_output)) {
|
|
70
70
|
Remove-Item $script:build_output -Force -ErrorAction SilentlyContinue
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
# Step out of any temp subfolder we may still be in before deleting it
|
|
74
|
+
Set-Location -Path ([System.IO.Path]::GetTempPath()) -ErrorAction SilentlyContinue
|
|
75
|
+
|
|
76
|
+
# Remove temporary build subfolders if they exist
|
|
77
|
+
if ($script:NODE_SUBFOLDER -and (Test-Path $script:NODE_SUBFOLDER)) {
|
|
78
|
+
Remove-Item -Path $script:NODE_SUBFOLDER -Recurse -Force -ErrorAction SilentlyContinue
|
|
79
|
+
}
|
|
80
|
+
if ($script:NODE_CONFORMANCE_TESTS_SUBFOLDER -and (Test-Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER)) {
|
|
81
|
+
Remove-Item -Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER -Recurse -Force -ErrorAction SilentlyContinue
|
|
82
|
+
}
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
# Check for and kill any existing Node server from previous runs
|
|
@@ -98,20 +109,21 @@ if ($args[2] -eq "-v" -or $args[2] -eq "--verbose") {
|
|
|
98
109
|
$current_dir = Get-Location
|
|
99
110
|
|
|
100
111
|
try {
|
|
101
|
-
# Define the path to the subfolder
|
|
102
|
-
$
|
|
112
|
+
# Define the path to the subfolder. It lives in the system temp directory;
|
|
113
|
+
# $BuildFolder may be an absolute path, so only its leaf name is used.
|
|
114
|
+
$script:NODE_SUBFOLDER = Join-Path ([System.IO.Path]::GetTempPath()) "node_$(Split-Path $BuildFolder -Leaf)"
|
|
103
115
|
|
|
104
116
|
# Running React application
|
|
105
|
-
Write-Host "### Step 1: Starting the React application in folder $NODE_SUBFOLDER..."
|
|
117
|
+
Write-Host "### Step 1: Starting the React application in folder $script:NODE_SUBFOLDER..."
|
|
106
118
|
|
|
107
119
|
if ($env:VERBOSE -eq "1") {
|
|
108
|
-
Write-Host "Preparing Node subfolder: $NODE_SUBFOLDER"
|
|
120
|
+
Write-Host "Preparing Node subfolder: $script:NODE_SUBFOLDER"
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
# Check if the node subfolder exists
|
|
112
|
-
if (Test-Path $NODE_SUBFOLDER) {
|
|
124
|
+
if (Test-Path $script:NODE_SUBFOLDER) {
|
|
113
125
|
# Delete all files and folders except "node_modules", "plain_modules", and "package-lock.json"
|
|
114
|
-
Get-ChildItem -Path $NODE_SUBFOLDER -Force |
|
|
126
|
+
Get-ChildItem -Path $script:NODE_SUBFOLDER -Force |
|
|
115
127
|
Where-Object {
|
|
116
128
|
$_.Name -ne "node_modules" -and
|
|
117
129
|
$_.Name -ne "plain_modules" -and
|
|
@@ -126,18 +138,18 @@ try {
|
|
|
126
138
|
Write-Host "Subfolder does not exist. Creating it..."
|
|
127
139
|
}
|
|
128
140
|
|
|
129
|
-
New-Item -ItemType Directory -Path $NODE_SUBFOLDER -Force | Out-Null
|
|
141
|
+
New-Item -ItemType Directory -Path $script:NODE_SUBFOLDER -Force | Out-Null
|
|
130
142
|
}
|
|
131
143
|
|
|
132
|
-
Copy-Item -Path "$BuildFolder/*" -Destination $NODE_SUBFOLDER -Recurse -Force
|
|
144
|
+
Copy-Item -Path "$BuildFolder/*" -Destination $script:NODE_SUBFOLDER -Recurse -Force
|
|
133
145
|
|
|
134
146
|
# Move to the subfolder
|
|
135
|
-
if (-not (Test-Path $NODE_SUBFOLDER)) {
|
|
136
|
-
Write-Host "Error: Node build folder '$NODE_SUBFOLDER' does not exist."
|
|
147
|
+
if (-not (Test-Path $script:NODE_SUBFOLDER)) {
|
|
148
|
+
Write-Host "Error: Node build folder '$script:NODE_SUBFOLDER' does not exist."
|
|
137
149
|
exit $UNRECOVERABLE_ERROR_EXIT_CODE
|
|
138
150
|
}
|
|
139
151
|
|
|
140
|
-
Push-Location $NODE_SUBFOLDER
|
|
152
|
+
Push-Location $script:NODE_SUBFOLDER
|
|
141
153
|
|
|
142
154
|
# Temporarily allow stderr output without throwing (npm may write warnings to stderr)
|
|
143
155
|
# ForEach-Object converts ErrorRecord objects (from stderr) to plain strings to avoid verbose error formatting
|
|
@@ -245,17 +257,18 @@ try {
|
|
|
245
257
|
# Move back to the original directory
|
|
246
258
|
Set-Location $current_dir
|
|
247
259
|
|
|
248
|
-
# Define the path to the conformance tests subfolder
|
|
249
|
-
|
|
260
|
+
# Define the path to the conformance tests subfolder. It lives in the system
|
|
261
|
+
# temp directory; $ConformanceTestsFolder may be absolute, so only its leaf name is used.
|
|
262
|
+
$script:NODE_CONFORMANCE_TESTS_SUBFOLDER = Join-Path ([System.IO.Path]::GetTempPath()) "node_$(Split-Path $ConformanceTestsFolder -Leaf)"
|
|
250
263
|
|
|
251
264
|
if ($env:VERBOSE -eq "1") {
|
|
252
|
-
Write-Host "Preparing conformance tests Node subfolder: $NODE_CONFORMANCE_TESTS_SUBFOLDER"
|
|
265
|
+
Write-Host "Preparing conformance tests Node subfolder: $script:NODE_CONFORMANCE_TESTS_SUBFOLDER"
|
|
253
266
|
}
|
|
254
267
|
|
|
255
268
|
# Check if the conformance tests node subfolder exists
|
|
256
|
-
if (Test-Path $NODE_CONFORMANCE_TESTS_SUBFOLDER) {
|
|
269
|
+
if (Test-Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER) {
|
|
257
270
|
# Delete all files and folders except "node_modules", "plain_modules", and "package-lock.json"
|
|
258
|
-
Get-ChildItem -Path $NODE_CONFORMANCE_TESTS_SUBFOLDER -Force |
|
|
271
|
+
Get-ChildItem -Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER -Force |
|
|
259
272
|
Where-Object {
|
|
260
273
|
$_.Name -ne "node_modules" -and
|
|
261
274
|
$_.Name -ne "plain_modules" -and
|
|
@@ -270,18 +283,18 @@ try {
|
|
|
270
283
|
Write-Host "Subfolder does not exist. Creating it..."
|
|
271
284
|
}
|
|
272
285
|
|
|
273
|
-
New-Item -ItemType Directory -Path $NODE_CONFORMANCE_TESTS_SUBFOLDER -Force | Out-Null
|
|
286
|
+
New-Item -ItemType Directory -Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER -Force | Out-Null
|
|
274
287
|
}
|
|
275
288
|
|
|
276
|
-
Copy-Item -Path "$ConformanceTestsFolder/*" -Destination $NODE_CONFORMANCE_TESTS_SUBFOLDER -Recurse -Force
|
|
289
|
+
Copy-Item -Path "$ConformanceTestsFolder/*" -Destination $script:NODE_CONFORMANCE_TESTS_SUBFOLDER -Recurse -Force
|
|
277
290
|
|
|
278
291
|
# Move to the subfolder with Cypress tests
|
|
279
|
-
if (-not (Test-Path $NODE_CONFORMANCE_TESTS_SUBFOLDER)) {
|
|
280
|
-
Write-Host "Error: conformance tests Node folder '$NODE_CONFORMANCE_TESTS_SUBFOLDER' does not exist."
|
|
292
|
+
if (-not (Test-Path $script:NODE_CONFORMANCE_TESTS_SUBFOLDER)) {
|
|
293
|
+
Write-Host "Error: conformance tests Node folder '$script:NODE_CONFORMANCE_TESTS_SUBFOLDER' does not exist."
|
|
281
294
|
exit $UNRECOVERABLE_ERROR_EXIT_CODE
|
|
282
295
|
}
|
|
283
296
|
|
|
284
|
-
Push-Location $NODE_CONFORMANCE_TESTS_SUBFOLDER
|
|
297
|
+
Push-Location $script:NODE_CONFORMANCE_TESTS_SUBFOLDER
|
|
285
298
|
|
|
286
299
|
# Temporarily allow stderr output without throwing (npm may write warnings to stderr)
|
|
287
300
|
# ForEach-Object converts ErrorRecord objects (from stderr) to plain strings to avoid verbose error formatting
|
package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1
CHANGED
|
@@ -23,7 +23,15 @@ $ConformanceTestsFolder = $args[1]
|
|
|
23
23
|
|
|
24
24
|
$current_dir = Get-Location
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# Resolve conformance tests folder to an absolute path so it can be used
|
|
27
|
+
# from the build subfolder (where we'll Push-Location to next).
|
|
28
|
+
if (-not [System.IO.Path]::IsPathRooted($ConformanceTestsFolder)) {
|
|
29
|
+
$ConformanceTestsFolder = Join-Path $current_dir $ConformanceTestsFolder
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Working folder lives in the system temp directory. $BuildFolder may be an
|
|
33
|
+
# absolute path, so only its leaf name is used to build the working folder name.
|
|
34
|
+
$GO_BUILD_SUBFOLDER = Join-Path ([System.IO.Path]::GetTempPath()) "go_$(Split-Path $BuildFolder -Leaf)"
|
|
27
35
|
|
|
28
36
|
if ($env:VERBOSE -eq "1") {
|
|
29
37
|
Write-Host "Preparing Go build subfolder: $GO_BUILD_SUBFOLDER"
|
|
@@ -60,9 +68,9 @@ try {
|
|
|
60
68
|
go get
|
|
61
69
|
|
|
62
70
|
# Move to conformance tests folder
|
|
63
|
-
Set-Location
|
|
71
|
+
Set-Location $ConformanceTestsFolder
|
|
64
72
|
if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
|
|
65
|
-
Write-Host "Error: Conformance tests folder '$
|
|
73
|
+
Write-Host "Error: Conformance tests folder '$ConformanceTestsFolder' does not exist."
|
|
66
74
|
exit $UNRECOVERABLE_ERROR_EXIT_CODE
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -75,7 +83,7 @@ try {
|
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
# Move back to build directory
|
|
78
|
-
Set-Location
|
|
86
|
+
Set-Location $GO_BUILD_SUBFOLDER
|
|
79
87
|
|
|
80
88
|
# Execute Go lang conformance tests
|
|
81
89
|
Write-Host "Running Golang conformance tests...`n"
|
|
@@ -83,7 +91,7 @@ try {
|
|
|
83
91
|
# Temporarily allow stderr output without throwing (Go may write to stderr)
|
|
84
92
|
# ForEach-Object converts ErrorRecord objects (from stderr) to plain strings to avoid verbose error formatting
|
|
85
93
|
$ErrorActionPreference = 'Continue'
|
|
86
|
-
$output = go run
|
|
94
|
+
$output = go run (Join-Path $ConformanceTestsFolder "conformance_tests.go") 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Out-String
|
|
87
95
|
$exit_code = $LASTEXITCODE
|
|
88
96
|
$ErrorActionPreference = 'Stop'
|
|
89
97
|
|
|
@@ -97,4 +105,7 @@ try {
|
|
|
97
105
|
exit $exit_code
|
|
98
106
|
} finally {
|
|
99
107
|
Pop-Location
|
|
108
|
+
if (Test-Path $GO_BUILD_SUBFOLDER) {
|
|
109
|
+
Remove-Item -Path $GO_BUILD_SUBFOLDER -Recurse -Force -ErrorAction SilentlyContinue
|
|
110
|
+
}
|
|
100
111
|
}
|
package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh
CHANGED
|
@@ -19,18 +19,31 @@ fi
|
|
|
19
19
|
current_dir=$(pwd)
|
|
20
20
|
printf "Current directory: $current_dir\n"
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# Resolve the conformance tests folder to an absolute path so it can be used
|
|
23
|
+
# after changing into the working folder ($2 may be relative to the invocation
|
|
24
|
+
# directory on older renderers; newer renderers pass it as an absolute path).
|
|
25
|
+
case "$2" in
|
|
26
|
+
/*) CONFORMANCE_TESTS_SOURCE="$2" ;;
|
|
27
|
+
*) CONFORMANCE_TESTS_SOURCE="$current_dir/$2" ;;
|
|
28
|
+
esac
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
tree "$CONFORMANCE_TESTS_SOURCE"
|
|
31
|
+
|
|
32
|
+
# Working folders live in the system temp directory. $1 and $2 may be absolute
|
|
33
|
+
# paths, so only their basenames are used to build the working folder names.
|
|
34
|
+
JAVA_BUILD_SUBFOLDER="/tmp/java_$(basename "$1")"
|
|
35
|
+
CONFORMANCE_TESTS_FOLDER="/tmp/java_conformance_$(basename "$2")"
|
|
36
|
+
|
|
37
|
+
trap 'rm -rf "$JAVA_BUILD_SUBFOLDER" "$CONFORMANCE_TESTS_FOLDER"' EXIT
|
|
25
38
|
|
|
26
39
|
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
27
40
|
printf "Preparing Java build subfolder: $JAVA_BUILD_SUBFOLDER\n"
|
|
28
41
|
fi
|
|
29
42
|
|
|
30
|
-
rm -rf $JAVA_BUILD_SUBFOLDER
|
|
31
|
-
mkdir -p $JAVA_BUILD_SUBFOLDER
|
|
43
|
+
rm -rf "$JAVA_BUILD_SUBFOLDER"
|
|
44
|
+
mkdir -p "$JAVA_BUILD_SUBFOLDER"
|
|
32
45
|
|
|
33
|
-
cp -R $1/* $JAVA_BUILD_SUBFOLDER
|
|
46
|
+
cp -R "$1"/* "$JAVA_BUILD_SUBFOLDER"
|
|
34
47
|
printf "Copied from $1 to $JAVA_BUILD_SUBFOLDER...\n"
|
|
35
48
|
|
|
36
49
|
# Move to the subfolder
|
|
@@ -54,17 +67,15 @@ if [ $exit_code -ne 0 ]; then
|
|
|
54
67
|
exit $exit_code
|
|
55
68
|
fi
|
|
56
69
|
|
|
57
|
-
CONFORMANCE_TESTS_FOLDER=.tmp/java_conformance
|
|
58
|
-
|
|
59
70
|
cd "$current_dir" 2>/dev/null
|
|
60
71
|
printf "Moved to $current_dir...\n"
|
|
61
72
|
printf "Preparing Java conformance tests subfolder: $CONFORMANCE_TESTS_FOLDER\n"
|
|
62
73
|
|
|
63
|
-
rm -rf $CONFORMANCE_TESTS_FOLDER
|
|
64
|
-
mkdir -p $CONFORMANCE_TESTS_FOLDER
|
|
74
|
+
rm -rf "$CONFORMANCE_TESTS_FOLDER"
|
|
75
|
+
mkdir -p "$CONFORMANCE_TESTS_FOLDER"
|
|
65
76
|
|
|
66
|
-
cp -R $
|
|
67
|
-
printf "Copied from $
|
|
77
|
+
cp -R "$CONFORMANCE_TESTS_SOURCE"/* "$CONFORMANCE_TESTS_FOLDER"
|
|
78
|
+
printf "Copied from $CONFORMANCE_TESTS_SOURCE to $CONFORMANCE_TESTS_FOLDER...\n"
|
|
68
79
|
|
|
69
80
|
# Move to the subfolder
|
|
70
81
|
cd "$CONFORMANCE_TESTS_FOLDER" 2>/dev/null
|
package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1
CHANGED
|
@@ -33,7 +33,15 @@ if (Get-Command python3 -ErrorAction SilentlyContinue) {
|
|
|
33
33
|
|
|
34
34
|
$current_dir = Get-Location
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
# Resolve conformance tests folder to an absolute path so it can be used
|
|
37
|
+
# from the build subfolder (where we'll Push-Location to next).
|
|
38
|
+
if (-not [System.IO.Path]::IsPathRooted($ConformanceTestsFolder)) {
|
|
39
|
+
$ConformanceTestsFolder = Join-Path $current_dir $ConformanceTestsFolder
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Working folder lives in the system temp directory. $BuildFolder may be an
|
|
43
|
+
# absolute path, so only its leaf name is used to build the working folder name.
|
|
44
|
+
$PYTHON_BUILD_SUBFOLDER = Join-Path ([System.IO.Path]::GetTempPath()) "python_$(Split-Path $BuildFolder -Leaf)"
|
|
37
45
|
|
|
38
46
|
if ($env:VERBOSE -eq "1") {
|
|
39
47
|
Write-Host "Preparing Python build subfolder: $PYTHON_BUILD_SUBFOLDER"
|
|
@@ -72,7 +80,7 @@ try {
|
|
|
72
80
|
# Temporarily allow stderr output without throwing (Python unittest writes progress to stderr)
|
|
73
81
|
# ForEach-Object converts ErrorRecord objects (from stderr) to plain strings to avoid verbose error formatting
|
|
74
82
|
$ErrorActionPreference = 'Continue'
|
|
75
|
-
$output = & $PYTHON_CMD -m unittest discover -b -s
|
|
83
|
+
$output = & $PYTHON_CMD -m unittest discover -b -s $ConformanceTestsFolder 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Out-String
|
|
76
84
|
$exit_code = $LASTEXITCODE
|
|
77
85
|
$ErrorActionPreference = 'Stop'
|
|
78
86
|
|
|
@@ -89,4 +97,7 @@ try {
|
|
|
89
97
|
exit $exit_code
|
|
90
98
|
} finally {
|
|
91
99
|
Pop-Location
|
|
100
|
+
if (Test-Path $PYTHON_BUILD_SUBFOLDER) {
|
|
101
|
+
Remove-Item -Path $PYTHON_BUILD_SUBFOLDER -Recurse -Force -ErrorAction SilentlyContinue
|
|
102
|
+
}
|
|
92
103
|
}
|
package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh
CHANGED
|
@@ -26,18 +26,28 @@ else
|
|
|
26
26
|
exit $UNRECOVERABLE_ERROR_EXIT_CODE
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# Resolve the conformance tests folder to an absolute path so it can be used
|
|
30
|
+
# after changing into the working folder ($2 may be relative to the invocation
|
|
31
|
+
# directory on older renderers; newer renderers pass it as an absolute path).
|
|
32
|
+
case "$2" in
|
|
33
|
+
/*) CONFORMANCE_TESTS_FOLDER="$2" ;;
|
|
34
|
+
*) CONFORMANCE_TESTS_FOLDER="$(pwd)/$2" ;;
|
|
35
|
+
esac
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
# Working folder lives in the system temp directory. $1 may be an absolute
|
|
38
|
+
# path, so only its basename is used to build the working folder name.
|
|
39
|
+
PYTHON_BUILD_SUBFOLDER="/tmp/python_$(basename "$1")"
|
|
40
|
+
|
|
41
|
+
trap 'rm -rf "$PYTHON_BUILD_SUBFOLDER"' EXIT
|
|
32
42
|
|
|
33
43
|
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
34
44
|
printf "Preparing Python build subfolder: $PYTHON_BUILD_SUBFOLDER\n"
|
|
35
45
|
fi
|
|
36
46
|
|
|
37
|
-
rm -rf $PYTHON_BUILD_SUBFOLDER
|
|
38
|
-
mkdir -p $PYTHON_BUILD_SUBFOLDER
|
|
47
|
+
rm -rf "$PYTHON_BUILD_SUBFOLDER"
|
|
48
|
+
mkdir -p "$PYTHON_BUILD_SUBFOLDER"
|
|
39
49
|
|
|
40
|
-
cp -R $1/* $PYTHON_BUILD_SUBFOLDER
|
|
50
|
+
cp -R "$1"/* "$PYTHON_BUILD_SUBFOLDER"
|
|
41
51
|
|
|
42
52
|
# Move to the subfolder
|
|
43
53
|
cd "$PYTHON_BUILD_SUBFOLDER" 2>/dev/null
|
|
@@ -84,7 +94,7 @@ printf "Requirements setup completed in %.2f seconds\n\n" "$duration"
|
|
|
84
94
|
# Execute all Python conformance tests in the build folder
|
|
85
95
|
printf "Running Python conformance tests...\n\n"
|
|
86
96
|
|
|
87
|
-
output=$($PYTHON_CMD -m unittest discover -b -s "$
|
|
97
|
+
output=$($PYTHON_CMD -m unittest discover -b -s "$CONFORMANCE_TESTS_FOLDER" 2>&1)
|
|
88
98
|
exit_code=$?
|
|
89
99
|
|
|
90
100
|
# Echo the original output
|
|
@@ -33,7 +33,7 @@ Without a prepare script, every one of those N invocations does the full depende
|
|
|
33
33
|
|
|
34
34
|
`prepare_environment_<lang>` exists to **amortize that cost to one** install per render:
|
|
35
35
|
|
|
36
|
-
- Prepare runs **once**, installs everything, populates the project-local isolation location inside
|
|
36
|
+
- Prepare runs **once**, installs everything, populates the project-local isolation location inside the working folder `<temp>/<lang>_<basename>/` (`./.venv`, `./node_modules`, `./.m2`, `./.gocache`, `./.cargo`, `./.pub-cache`). Throughout this skill, `<temp>/<lang>_<basename>` means: the system temp directory (`/tmp` in Bash, `[System.IO.Path]::GetTempPath()` in PowerShell), a short language identifier, and the **basename** of the build-folder argument (`$(basename "$1")` / `Split-Path -Leaf`) — never the raw argument, which arrives as an absolute path from current renderers.
|
|
37
37
|
- The conformance script then runs **N times**, each invocation in its [activate-only variant](../implement-conformance-testing-script/SKILL.md#variant-decision-install-inline-vs-activate-only), attaching to the already-populated working folder and skipping the install step entirely.
|
|
38
38
|
- Net effect: install cost goes from `N × install-cost` to `1 × install-cost + N × cheap-attach-cost`.
|
|
39
39
|
|
|
@@ -45,8 +45,8 @@ This is the **whole reason** the prepare-then-conformance split exists. If a pro
|
|
|
45
45
|
|
|
46
46
|
`prepare_environment_<lang>` exists **solely** to set up the working folder that `run_conformance_tests_<lang>` then attaches to (via the activate-only variant of the conformance script). It has **no relationship** to [`run_unittests_<lang>`](../implement-unit-testing-script/SKILL.md):
|
|
47
47
|
|
|
48
|
-
- The unit-test runner is **fully self-contained**. It does its own staging into its **own**
|
|
49
|
-
- The unit-test runner **never reads from** the working folder `prepare_environment` populates. The two scripts use independent working folders even when they happen to share a
|
|
48
|
+
- The unit-test runner is **fully self-contained**. It does its own staging into its **own** `<temp>/<lang>_<basename>/` working folder, and it installs its own dependencies inline (`pip install -r requirements.txt`, `npm ci`, `mvn`, `cargo fetch`, ...) every run — and it removes that folder when it exits.
|
|
49
|
+
- The unit-test runner **never reads from** the working folder `prepare_environment` populates. The two scripts use independent working folders even when they happen to share a `<temp>/<lang>_<basename>/` naming convention — each script wipes and rebuilds its own copy.
|
|
50
50
|
- The unit-test runner **does not require** `prepare_environment` to have run first. Users and CI systems routinely run unit tests as a smoke check without ever invoking conformance, and that must keep working.
|
|
51
51
|
- There is **no activate-only variant** of the unit-test runner. [`implement-unit-testing-script`](../implement-unit-testing-script/SKILL.md) emits a self-contained script every time — the two-variant pattern is exclusive to the conformance runner.
|
|
52
52
|
|
|
@@ -86,13 +86,13 @@ The same pattern applies to both shell flavors. Only the syntax changes.
|
|
|
86
86
|
Every prepare-environment script must implement these steps **in this order**:
|
|
87
87
|
|
|
88
88
|
1. **Toolchain check.** Verify that the required language runtime / build tool (and the required version, if any) is installed. If not, print an error and exit with code `69`.
|
|
89
|
-
2. **Argument validation.** Require **one** positional argument: `<build_folder>`. If missing, print usage and exit with code `69`.
|
|
90
|
-
3. **Working directory setup.** Define a working folder at
|
|
89
|
+
2. **Argument validation.** Require **one** positional argument: `<build_folder>`. If missing, print usage and exit with code `69`. The renderer passes this argument as an **absolute path** (older renderer versions passed a relative one) — the script must work with both, which is why the working-folder name in step 3 is built from the argument's *basename*, never from the raw argument.
|
|
90
|
+
3. **Working directory setup.** Define a working folder at `<temp>/<lang>_<basename>` — **identical** to the path the conformance script will use (`/tmp/<lang>_$(basename "$1")` in Bash, `Join-Path ([System.IO.Path]::GetTempPath()) "<lang>_$(Split-Path $BuildFolder -Leaf)"` in PowerShell). Wipe it (`rm -rf` / `Remove-Item -Recurse -Force`) and recreate it. This folder — and **only** this folder — is where every subsequent write must land. **Do not register a cleanup trap / `finally` deletion** — unlike its two siblings, prepare deliberately leaves the working folder populated on exit; that folder *is* the deliverable the conformance script attaches to.
|
|
91
91
|
4. **Copy the build.** Recursively copy everything from `<build_folder>` (`$1`) into the working folder. After this step the source folder (`$1`) is treated as **read-only** for the rest of the script.
|
|
92
|
-
5. **Enter the working directory.** `cd` / `Set-Location` into
|
|
93
|
-
6. **Install dependencies / pre-build artifacts into an isolated environment inside
|
|
92
|
+
5. **Enter the working directory.** `cd` / `Set-Location` into `<temp>/<lang>_<basename>`. If that fails, exit with code `69`. All remaining steps run from inside the working folder; they must never write back to `$1`.
|
|
93
|
+
6. **Install dependencies / pre-build artifacts into an isolated environment inside `<temp>/<lang>_<basename>`.** Set up a per-working-folder dependency location (a Python venv at `./.venv`, a local `./node_modules`, a project-scoped Maven repo at `./.m2`, etc.) and install/resolve all dependencies into it. Where the language requires building before tests can run (Java, Rust, Go), also produce the build artifact and place it where the conformance script can find it — **inside the working folder**, never inside `$1` and never in the user's home directory. **Never** install into the source build folder (`$1`), the user's global cache (`~/.m2`, system-wide `pip`, `~/.cargo`, `~/.npm`, ...), or anywhere outside `<temp>/<lang>_<basename>`. If any sub-step fails, exit with code `69` (do **not** propagate Maven/pip/npm exit codes — a half-prepared environment is itself an unrecoverable error). See [Dependency isolation](#dependency-isolation) for per-language specifics.
|
|
94
94
|
|
|
95
|
-
That's it. There is no step 7. Once dependencies are installed and (where applicable) the build artifact is in the local repo, this script's job is done.
|
|
95
|
+
That's it. There is no step 7. Once dependencies are installed and (where applicable) the build artifact is in the local repo, this script's job is done — and the populated working folder is intentionally left in place for the conformance runner.
|
|
96
96
|
|
|
97
97
|
### The build folder is read-only — hard rule
|
|
98
98
|
|
|
@@ -100,14 +100,14 @@ The source build folder passed in as `$1` is **input only**. Prepare reads from
|
|
|
100
100
|
|
|
101
101
|
- install dependencies into it (no `pip install` inside `$1`, no `npm install` inside `$1`, no `mvn install` writing into `$1`, no Cargo build artifacts ending up under `$1`),
|
|
102
102
|
- write a virtualenv / `node_modules` / `.m2` / `.gocache` / `.cargo` / `.pub-cache` directory inside it,
|
|
103
|
-
- pre-build into it (every compile output — `target/`, `build/`, `dist/`, native binaries, generated sources — lives inside
|
|
103
|
+
- pre-build into it (every compile output — `target/`, `build/`, `dist/`, native binaries, generated sources — lives inside `<temp>/<lang>_<basename>`),
|
|
104
104
|
- create logs, caches, or temp files inside it.
|
|
105
105
|
|
|
106
|
-
The build folder is shared with the renderer (`plain_modules/...` by default) and with the conformance script, which staging-checks
|
|
106
|
+
The build folder is shared with the renderer (`plain_modules/...` by default) and with the conformance script, which staging-checks the working folder via `if [ ! -d "/tmp/<lang>_$(basename "$1")" ]` and expects `$1` itself to look the same as it did right after rendering. Writing into `$1` corrupts the renderer's view of "what was generated", churns git status if the project commits `$1`, and (if the conformance script ever does an `rm -rf $1` during its own setup) silently destroys work prepare did.
|
|
107
107
|
|
|
108
|
-
The whole point of staging
|
|
108
|
+
The whole point of staging into `<temp>/<lang>_<basename>` is so the source build folder stays a clean, reproducible artifact of the render and no build debris lands inside the user's project. Every dependency, every compiled class, every binary, every cache must land inside the working folder — because that is exactly what the conformance script's activate-only variant attaches to.
|
|
109
109
|
|
|
110
|
-
If you find yourself about to issue any command whose `cwd` is `$1`, or whose target path starts with `$1/`, **stop**. Either move the operation into
|
|
110
|
+
If you find yourself about to issue any command whose `cwd` is `$1`, or whose target path starts with `$1/`, **stop**. Either move the operation into `<temp>/<lang>_<basename>`, or you're doing something the script must not do.
|
|
111
111
|
|
|
112
112
|
## Coordination contract
|
|
113
113
|
|
|
@@ -117,7 +117,8 @@ The two scripts must agree on:
|
|
|
117
117
|
|
|
118
118
|
| What | Why it matters |
|
|
119
119
|
|---|---|
|
|
120
|
-
| **Working folder path** (
|
|
120
|
+
| **Working folder path** (`<temp>/<lang>_<basename>`) | The conformance script `cd`s into this folder. If prepare uses a different name, the conformance script sees an empty / freshly-staged folder and re-does all the work. Both scripts must derive it the same way: system temp dir + `<lang>_` + basename of `$1`. |
|
|
121
|
+
| **Working folder lifecycle** | Prepare wipes-and-recreates the folder at the start and **leaves it populated on exit**. The activate-only conformance script attaches to it and **never deletes it** — not on success, not on failure. Only prepare (on its next run) recreates it. A cleanup trap in either script destroys the amortization this pair exists for. |
|
|
121
122
|
| **Dependency isolation location** (`./.venv`, `./.m2`, `./node_modules`, `./.gocache`, `./.cargo`, …) — relative to the working folder | If prepare populates `~/.m2` and conformance reads from `./.m2`, the warm cache is invisible. Always use the **project-local** path inside the working folder, in both scripts. |
|
|
122
123
|
| **Build artifact location** (Java/Rust/Go) | The conformance test project depends on the build's artifact. It must be findable at the exact coordinates the conformance script expects (e.g. installed into the same project-local `./.m2` for Java). |
|
|
123
124
|
| **Toolchain version** | If prepare runs under Java 21 and conformance runs under Java 17, classfile incompatibilities will surface at test time, not prepare time. Both scripts should perform the same toolchain check. |
|
|
@@ -131,7 +132,8 @@ Shared across both shell flavors:
|
|
|
131
132
|
- **Exit codes:**
|
|
132
133
|
- `69` — unrecoverable: missing argument, missing toolchain, can't enter working folder, dependency install / build failed. Treat **all** failures as unrecoverable here — there is no "soft" failure mode for prepare.
|
|
133
134
|
- `0` — success.
|
|
134
|
-
- **Working folder naming:**
|
|
135
|
+
- **Working folder naming:** `<temp>/<lang>_<basename>` — the system temp directory (`/tmp` in Bash, `[System.IO.Path]::GetTempPath()` in PowerShell) plus a short identifier for the language (`java`, `python`, `node`, `go`, `rust`, ...) and the **basename** of the first (and only) argument (`$(basename "$1")` / `Split-Path -Leaf`). Never embed the raw argument in the path — it may be absolute, and concatenating it produces broken doubled paths. All dependency installs, build outputs, caches, and pre-built artifacts live inside this folder. Nothing the script does should touch `$1` after step 4.
|
|
136
|
+
- **No cleanup on exit.** Prepare leaves the working folder populated — that folder is its deliverable. Do not add a `trap … EXIT` / `finally` deletion; the folder is reclaimed the next time prepare wipes-and-recreates it (and the OS eventually reclaims the temp dir).
|
|
135
137
|
- **Logging — be as verbose as possible.** The script is the only thing the operator sees between a `codeplain` render and a green/red conformance result; when prepare fails, the only forensic evidence anyone has is its stdout/stderr. Treat the script like a production runbook, not a quiet helper. Concretely:
|
|
136
138
|
- **Announce every step before doing it**, with the exact value of every variable involved — the resolved source folder, the working folder, the language, the toolchain version detected, the isolation root, the dependency-manifest path, the install / pre-build command about to run. "Installing dependencies" alone is useless; "Installing dependencies from `./requirements.txt` into venv `./.venv` (Python 3.11.6 at `/usr/local/bin/python3`)" is triage-ready.
|
|
137
139
|
- **Echo every non-trivial command before executing it.** In Bash, use `set -x` for the body of the script (or `echo "+ <cmd>"` immediately before each call); in PowerShell, set `$VerbosePreference = 'Continue'` and use `Write-Verbose` / `Write-Host` to print each command line with its arguments.
|
|
@@ -159,7 +161,7 @@ The dependency environment must live **inside** `$WORKING_FOLDER` so the conform
|
|
|
159
161
|
|
|
160
162
|
Notes:
|
|
161
163
|
|
|
162
|
-
- **Every path in the install / pre-build command is relative to
|
|
164
|
+
- **Every path in the install / pre-build command is relative to `<temp>/<lang>_<basename>`.** That's why the script `cd`s into the working folder in step 5 — from that point on, `./.venv`, `./node_modules`, `./.m2`, `./.gocache`, `./.cargo`, `./.pub-cache`, and any compile output (`target/`, `build/`, `dist/`, native binaries) all resolve under `<temp>/<lang>_<basename>`, never under `$1` and never under the user's home directory.
|
|
163
165
|
- **Java/Rust/Go must compile, not just download.** The conformance script will time-out / re-compile from scratch if you only resolve metadata. Use `mvn install`, `cargo build --tests`, `go build ./...` (not just `dependency:resolve` / `cargo fetch` / `go mod download`).
|
|
164
166
|
- **Python and Node.js only need to install** — they're interpreted/JIT-compiled at test time, so `pip install` / `npm ci` is sufficient.
|
|
165
167
|
- **Always pass the isolation flag/env var.** `mvn` without `-Dmaven.repo.local`, `cargo` without `CARGO_HOME`, etc., write to the user's home directory instead of `$WORKING_FOLDER`. The conformance script will look in the wrong place and the warming was wasted — and the user's home dir gets polluted.
|
|
@@ -207,9 +209,10 @@ Adding a `prepare_environment_<lang>` script changes the contract for the corres
|
|
|
207
209
|
|
|
208
210
|
| Step in conformance | If prepare exists, you must... |
|
|
209
211
|
|---|---|
|
|
210
|
-
| Staging block (`rm -rf
|
|
212
|
+
| Staging block (`rm -rf` of the working folder + `mkdir -p` + `cp -R $1/*` into it) | **Remove entirely.** Replace with a guard: `WORKING_FOLDER="/tmp/<lang>_$(basename "$1")"; if [ ! -d "$WORKING_FOLDER" ]; then echo "Error: build folder missing — run prepare_environment_<lang>.sh first."; exit 69; fi` |
|
|
211
213
|
| Dependency install / pre-build (`pip install`, `mvn install -DskipTests`, `npm ci`, `cargo build --tests`, etc.) | **Remove entirely.** Replace with a guard that the isolation location exists (`.venv/bin/activate` for Python, `.m2/` for Java, `node_modules/` for Node, etc.) and exit `69` if missing. |
|
|
212
214
|
| Activation step (Python `source .venv/bin/activate`, Java `-Dmaven.repo.local=$(pwd)/.m2`) | **Keep.** Without it the test command can't see the prepared deps. |
|
|
215
|
+
| Working-folder cleanup on exit (`trap 'rm -rf "$WORKING_FOLDER"' EXIT` / `Remove-Item` in `finally`) | **Remove entirely.** The activate-only variant never deletes the working folder — prepare owns its lifecycle. Leaving the trap in wipes the warmed environment after the first conformance run, so every later run fails the "missing prepared environment" guard. |
|
|
213
216
|
| Test execution + "no tests discovered" guard + exit-code propagation | **Keep unchanged.** |
|
|
214
217
|
|
|
215
218
|
4. **Verify the conformance script's exit codes still follow [`implement-conformance-testing-script`](../implement-conformance-testing-script/SKILL.md)** — the new "missing prepared environment" guard should exit `69` (unrecoverable invocation error), the no-tests guard should still exit `1`, and the test command's exit code should still propagate.
|
|
@@ -224,14 +227,16 @@ Adding a `prepare_environment_<lang>` script changes the contract for the corres
|
|
|
224
227
|
## Anti-Patterns
|
|
225
228
|
|
|
226
229
|
- **(Hard mistake) Don't pre-warm the unit-test runner from this script.** `prepare_environment_<lang>` is for the **conformance** script only. The unit-test runner ([`implement-unit-testing-script`](../implement-unit-testing-script/SKILL.md)) is always fully self-contained — it stages, installs, and runs in one shot, every invocation, regardless of whether a prepare script exists. Do not add a unit-test dependency-install step to `prepare_environment` "to save time"; the unit-test runner will not read what you produce, and the coupling breaks the activate-only contract between prepare and conformance. See [`prepare_environment` is conformance-only — NOT for unit tests](#prepare_environment-is-conformance-only--not-for-unit-tests) above.
|
|
227
|
-
- **(Hard mistake) Don't install into, build into, or otherwise write to the source build folder (`$1`).** The build folder passed as `$1` is read-only input after step 4. Every install, cache, build artifact (`target/`, `build/`, `dist/`, native binaries, generated sources), log, and temp file must land in
|
|
230
|
+
- **(Hard mistake) Don't install into, build into, or otherwise write to the source build folder (`$1`).** The build folder passed as `$1` is read-only input after step 4. Every install, cache, build artifact (`target/`, `build/`, `dist/`, native binaries, generated sources), log, and temp file must land in `<temp>/<lang>_<basename>`. This includes never running `pip install`, `npm install`, `mvn install`, `cargo build`, or `go build` with `$1` as their `cwd` or target; never letting a venv / `node_modules` / `.m2` / `.gocache` / `.cargo` / `.pub-cache` directory appear inside `$1`; and never producing a pre-built artifact at any path under `$1/`. The whole point of staging into the system temp directory is so the build folder remains a clean artifact of the render — writing to it corrupts the renderer's view, churns git status, and can be silently destroyed if the conformance script ever re-stages `$1` on its own.
|
|
231
|
+
- **(Hard mistake) Don't build any path by concatenating the raw build-folder argument.** `$1` arrives as an absolute path from current renderers. Patterns like `WORKING_FOLDER=.tmp/$1` or `WORKING_FOLDER=<lang>_$1` silently produce doubled paths (`/project//abs/path/...`) — the classic symptom is a "folder does not exist" or `ImportError: Start directory is not importable` error pointing at a path that contains the project root twice. Always take `$(basename "$1")` / `Split-Path -Leaf`.
|
|
232
|
+
- **(Hard mistake) Don't add a cleanup trap that deletes the working folder on exit.** The populated working folder is this script's deliverable — the activate-only conformance runner attaches to it N times per render. Deleting it on exit turns every conformance run into a "missing prepared environment" failure. (The unit-test and install-inline conformance scripts *do* clean up after themselves; prepare and activate-only conformance never do.)
|
|
228
233
|
- **Don't write to the user's global dependency cache** (`~/.m2`, system-wide `pip`, `~/.cargo`, `~/.npm`, etc.). The conformance script reads from the project-local cache; a global write is invisible to it and pollutes the user's home dir.
|
|
229
234
|
- **Don't use a different working folder name than the conformance script.** They must match exactly. If you change one, change the other.
|
|
230
235
|
- **Don't run tests** — not unit tests, not conformance tests, not smoke tests. Prepare's contract is "set up the environment"; running tests belongs to its siblings.
|
|
231
236
|
- **Don't propagate non-`69` exit codes from `mvn` / `pip` / `npm`.** A failed install means the environment isn't usable. Treat every failure as `exit 69` so the orchestrator can tell "prepare failed" apart from "tests failed".
|
|
232
237
|
- **Don't skip the toolchain check**, even when "everyone has it installed" — exit code `69` is what the calling system relies on to detect a missing runtime, and prepare is usually the *first* script to run, so it's the cheapest place to surface a missing JDK / Python / Node.
|
|
233
238
|
- **Don't print the "moved to ..." line before the `cd` success check.** The reference script does this and it lies on failure. Put the log *after* the guard, or print "attempting to enter ..." instead.
|
|
234
|
-
- **Don't reuse the source folder in place.** Always copy into
|
|
239
|
+
- **Don't reuse the source folder in place.** Always copy into `<temp>/<lang>_<basename>` first; the conformance script relies on this isolation.
|
|
235
240
|
- **Don't change the exit-code contract between Bash and PowerShell variants.** The `.sh` and `.ps1` for the same language must use identical exit codes for identical failure modes.
|
|
236
241
|
- **Don't write a cross-shell hybrid** (e.g. a `.sh` that detects PowerShell, or vice versa). Ship one script per shell, named with the appropriate extension.
|
|
237
242
|
- **Don't forget to time the install.** Without the duration log, there's no way to tell whether prepare is actually saving wall-clock time vs. doing the same work the conformance script would have done inline.
|
package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_java.sh
CHANGED
|
@@ -16,16 +16,20 @@ if [ -z "$1" ]; then
|
|
|
16
16
|
exit 1
|
|
17
17
|
fi
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Working folder lives in the system temp directory. $1 may be an absolute
|
|
20
|
+
# path, so only its basename is used to build the working folder name.
|
|
21
|
+
# No cleanup trap here on purpose: this script deliberately leaves the working
|
|
22
|
+
# folder populated so the conformance runner can attach to it on every invocation.
|
|
23
|
+
JAVA_BUILD_SUBFOLDER="/tmp/java_$(basename "$1")"
|
|
20
24
|
|
|
21
25
|
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
22
26
|
printf "Copying generated code to main project folder: $JAVA_BUILD_SUBFOLDER\n"
|
|
23
27
|
fi
|
|
24
28
|
|
|
25
|
-
rm -rf $JAVA_BUILD_SUBFOLDER
|
|
26
|
-
mkdir -p $JAVA_BUILD_SUBFOLDER
|
|
29
|
+
rm -rf "$JAVA_BUILD_SUBFOLDER"
|
|
30
|
+
mkdir -p "$JAVA_BUILD_SUBFOLDER"
|
|
27
31
|
|
|
28
|
-
cp -R $1/* $JAVA_BUILD_SUBFOLDER
|
|
32
|
+
cp -R "$1"/* "$JAVA_BUILD_SUBFOLDER"
|
|
29
33
|
printf "Copied from $1 to $JAVA_BUILD_SUBFOLDER...\n"
|
|
30
34
|
|
|
31
35
|
# Move to the subfolder
|
package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_python.sh
CHANGED
|
@@ -27,16 +27,20 @@ fi
|
|
|
27
27
|
|
|
28
28
|
current_dir=$(pwd)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
# Working folder lives in the system temp directory. $1 may be an absolute
|
|
31
|
+
# path, so only its basename is used to build the working folder name.
|
|
32
|
+
# No cleanup trap here on purpose: this script deliberately leaves the working
|
|
33
|
+
# folder populated so the conformance runner can attach to it on every invocation.
|
|
34
|
+
PYTHON_BUILD_SUBFOLDER="/tmp/python_$(basename "$1")"
|
|
31
35
|
|
|
32
36
|
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
33
37
|
printf "Preparing Python build subfolder: $PYTHON_BUILD_SUBFOLDER\n"
|
|
34
38
|
fi
|
|
35
39
|
|
|
36
|
-
rm -rf $PYTHON_BUILD_SUBFOLDER
|
|
37
|
-
mkdir -p $PYTHON_BUILD_SUBFOLDER
|
|
40
|
+
rm -rf "$PYTHON_BUILD_SUBFOLDER"
|
|
41
|
+
mkdir -p "$PYTHON_BUILD_SUBFOLDER"
|
|
38
42
|
|
|
39
|
-
cp -R $1/* $PYTHON_BUILD_SUBFOLDER
|
|
43
|
+
cp -R "$1"/* "$PYTHON_BUILD_SUBFOLDER"
|
|
40
44
|
|
|
41
45
|
# Move to the subfolder
|
|
42
46
|
cd "$PYTHON_BUILD_SUBFOLDER" 2>/dev/null
|
|
@@ -31,11 +31,11 @@ The same seven-step pattern applies to both. Only the syntax changes.
|
|
|
31
31
|
Every testing script must implement these steps **in this order**:
|
|
32
32
|
|
|
33
33
|
1. **Toolchain check.** Verify that the required language runtime / build tool (and the required version, if any) is installed. If not, print an error and exit with code `69`.
|
|
34
|
-
2. **Argument validation.** Require exactly one positional argument: the source build folder
|
|
35
|
-
3. **Working directory setup.** Define a working folder
|
|
34
|
+
2. **Argument validation.** Require exactly one positional argument: the source build folder. If missing, print usage and exit with code `1`. The renderer passes this argument as an **absolute path** (older renderer versions passed a relative one) — the script must work with both, which is why the working-folder name in step 3 is built from the argument's *basename*, never from the raw argument.
|
|
35
|
+
3. **Working directory setup.** Define a working folder in the **system temp directory**: `/tmp/<lang>_$(basename "$1")` in Bash, `Join-Path ([System.IO.Path]::GetTempPath()) "<lang>_$(Split-Path $BuildFolder -Leaf)"` in PowerShell (written as `<temp>/<lang>_<basename>` below). If it exists, wipe its contents; otherwise create it. Register cleanup so the folder is removed when the script exits — `trap 'rm -rf "$WORKING_FOLDER"' EXIT` in Bash, a `Remove-Item -Recurse -Force` in the `finally` block in PowerShell. This folder — and **only** this folder — is where every subsequent write must land.
|
|
36
36
|
4. **Copy the build.** Recursively copy everything from the source folder into the working folder. After this step the source folder (`$1`) is treated as **read-only** for the rest of the script.
|
|
37
|
-
5. **Enter the working directory.** `cd` / `Set-Location` into
|
|
38
|
-
6. **Install dependencies into an isolated environment inside
|
|
37
|
+
5. **Enter the working directory.** `cd` / `Set-Location` into `<temp>/<lang>_<basename>`. If that fails, exit with code `2`. All remaining steps run from inside the working folder; they must never write back to the source build folder.
|
|
38
|
+
6. **Install dependencies into an isolated environment inside `<temp>/<lang>_<basename>`.** Set up a per-working-folder dependency location (a Python venv at `./.venv`, a local `./node_modules`, a project-scoped Maven repo at `./.m2`, etc.) and install/resolve all dependencies into it. **Never** install into the source build folder, the user's global cache (`~/.m2`, system-wide `pip`, `~/.cargo`, `~/.npm`, ...), or anywhere outside `<temp>/<lang>_<basename>`. If the install command fails, propagate its exit code immediately and **do not** proceed to step 7. See [Dependency isolation](#dependency-isolation) for per-language specifics.
|
|
39
39
|
7. **Run the tests.** Invoke the language's standard test command (e.g. `mvn test`, `pytest`, `npm test`, `go test ./...`, `cargo test`), pointed at the same isolated environment from step 6. The script's final exit code is whatever the test command returns.
|
|
40
40
|
|
|
41
41
|
### The build folder is read-only — hard rule
|
|
@@ -44,12 +44,12 @@ The source build folder passed in as `$1` is **input only**. The script must nev
|
|
|
44
44
|
|
|
45
45
|
- install dependencies into it (no `pip install` inside `$1`, no `npm install` inside `$1`, no `mvn install` writing into `$1`, no Cargo build artifacts ending up under `$1`),
|
|
46
46
|
- write a virtualenv / `node_modules` / `.m2` / `.gocache` / `.cargo` directory inside it,
|
|
47
|
-
- run the test command from inside it (every test command runs from inside
|
|
47
|
+
- run the test command from inside it (every test command runs from inside `<temp>/<lang>_<basename>` after the `cd` in step 5),
|
|
48
48
|
- create logs, caches, build outputs, or temp files inside it.
|
|
49
49
|
|
|
50
|
-
The build folder is shared with the renderer (`plain_modules/...` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. Every write must go into
|
|
50
|
+
The build folder is shared with the renderer (`plain_modules/...` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. Every write must go into `<temp>/<lang>_<basename>` — the whole point of staging into the system temp directory is so the source build folder stays a clean, reproducible artifact of the render and no build debris is left inside the user's project.
|
|
51
51
|
|
|
52
|
-
If you find yourself about to issue any command whose `cwd` is the source folder, or whose target path starts with `$1/`, **stop**. Either move the operation into
|
|
52
|
+
If you find yourself about to issue any command whose `cwd` is the source folder, or whose target path starts with `$1/`, **stop**. Either move the operation into `<temp>/<lang>_<basename>`, or you're doing something the script must not do.
|
|
53
53
|
|
|
54
54
|
## Conventions
|
|
55
55
|
|
|
@@ -60,7 +60,8 @@ Shared across both shell flavors:
|
|
|
60
60
|
- `2` — filesystem problem (couldn't enter the working folder).
|
|
61
61
|
- `69` — required toolchain / runtime is not installed.
|
|
62
62
|
- Any other non-zero code — propagated from the underlying test command.
|
|
63
|
-
- **Working folder naming:**
|
|
63
|
+
- **Working folder naming:** `<temp>/<lang>_<basename>` — the system temp directory (`/tmp` in Bash, `[System.IO.Path]::GetTempPath()` in PowerShell) plus a short identifier for the language (`java`, `python`, `node`, `go`, `rust`, ...) and the **basename** of the build-folder argument (`$(basename "$1")` / `Split-Path $BuildFolder -Leaf`). Never embed the raw argument in the path — it may be absolute, and concatenating it produces broken doubled paths. All dependency installs, build outputs, caches, and the test run itself live inside this folder. Nothing the script does should touch the source build folder after step 4.
|
|
64
|
+
- **Cleanup on exit:** the working folder is temporary — remove it when the script exits, on success and failure alike (`trap 'rm -rf "$WORKING_FOLDER"' EXIT` in Bash; `Remove-Item -Recurse -Force` in the PowerShell `finally` block, after `Pop-Location`). No leftover build folders may accumulate in the temp directory or the user's project.
|
|
64
65
|
- **Logging — be as verbose as possible.** The script is the only thing the operator sees between a `codeplain` render and a green/red test result; when it fails, the only forensic evidence anyone has is its stdout/stderr. Treat the script like a production runbook, not a quiet helper. Concretely:
|
|
65
66
|
- **Announce every step before doing it**, with the exact value of every variable involved — the resolved source folder, the working folder, the language, the toolchain version detected, the isolation root, the dependency-manifest path, the test command about to run. "Installing dependencies" alone is useless; "Installing dependencies from `./requirements.txt` into venv `./.venv` (Python 3.11.6 at `/usr/local/bin/python3`)" is triage-ready.
|
|
66
67
|
- **Echo every non-trivial command before executing it.** In Bash, use `set -x` for the body of the script (or `echo "+ <cmd>"` immediately before each call); in PowerShell, set `$VerbosePreference = 'Continue'` and use `Write-Verbose` / `Write-Host` to print each command line with its arguments.
|
|
@@ -86,7 +87,7 @@ The dependency environment must live **inside** `$WORKING_FOLDER` so the test ru
|
|
|
86
87
|
|
|
87
88
|
Notes:
|
|
88
89
|
|
|
89
|
-
- **Every path in the install command and test command is relative to
|
|
90
|
+
- **Every path in the install command and test command is relative to `<temp>/<lang>_<basename>`.** That's why the script `cd`s into the working folder in step 5 — from that point on, `./.venv`, `./node_modules`, `./.m2`, etc. all resolve under `<temp>/<lang>_<basename>`, never under the source build folder.
|
|
90
91
|
- **Always pass the isolation flag/env var to both the install command and the test command** — they must agree on where deps live, otherwise the test command will silently fall back to the global cache **or** (worse) the source build folder.
|
|
91
92
|
- **Python is the only ecosystem where the venv is mandatory** to satisfy "into a virtual environment" literally. The others use language-native equivalents that achieve the same isolation.
|
|
92
93
|
- **Pre-warming is optional for Java/Go/Rust** — their test commands will fetch deps on demand. Doing it as a separate step makes failures easier to diagnose and gives a clean "install failed vs test failed" signal.
|
|
@@ -121,9 +122,11 @@ Notes:
|
|
|
121
122
|
|
|
122
123
|
## Anti-Patterns
|
|
123
124
|
|
|
124
|
-
- **(Hard mistake) Don't install into, build into, or otherwise write to the source build folder.** The build folder passed as `$1` is read-only input. Every install, cache, build artifact, log, and temp file must land in
|
|
125
|
+
- **(Hard mistake) Don't install into, build into, or otherwise write to the source build folder.** The build folder passed as `$1` is read-only input. Every install, cache, build artifact, log, and temp file must land in `<temp>/<lang>_<basename>`. This includes never running `pip install`, `npm install`, `mvn install`, or `cargo build` with the source folder as their `cwd` or target, never letting a venv / `node_modules` / `.m2` / `.gocache` / `.cargo` directory appear inside the source folder, and never running the test command from inside it. The whole point of staging the build into the system temp directory is so the source folder remains a clean, reproducible artifact of the render — writing to it corrupts the renderer's view and breaks subsequent renders.
|
|
126
|
+
- **(Hard mistake) Don't build any path by concatenating the raw build-folder argument.** `$1` arrives as an absolute path from current renderers. Patterns like `WORKING_FOLDER=.tmp/$1`, `WORKING_FOLDER=<lang>_$1`, or `"$current_dir/$1"` silently produce doubled paths (`/project//abs/path/...`) — the classic symptom is an `ImportError: Start directory is not importable` or "folder does not exist" pointing at a path that contains the project root twice. Always take `$(basename "$1")` / `Split-Path -Leaf`.
|
|
125
127
|
- Don't skip the toolchain check, even when "everyone has it installed" — exit code `69` is what the calling system relies on to detect a missing runtime.
|
|
126
|
-
- Don't reuse the source folder in place. Always copy into
|
|
128
|
+
- Don't reuse the source folder in place. Always copy into `<temp>/<lang>_<basename>` first; the renderer relies on this isolation.
|
|
129
|
+
- Don't leave the working folder behind. Register the cleanup (`trap … EXIT` / `finally`) before the first write so even an early failure removes it.
|
|
127
130
|
- Don't change the exit-code contract. Other parts of the system branch on `1`, `2`, and `69` specifically — and these codes must be identical between the Bash and PowerShell variants.
|
|
128
131
|
- Don't write a cross-shell hybrid (e.g. a `.sh` that detects PowerShell, or vice versa). Ship one script per shell, named with the appropriate extension.
|
|
129
132
|
- Don't install dependencies into the user's global location (`~/.m2`, system-wide `pip`, `~/.cargo`, etc.). Always isolate inside `$WORKING_FOLDER` so concurrent runs and other projects can't interfere.
|
|
@@ -16,7 +16,9 @@ if (-not (Get-Command flutter -ErrorAction SilentlyContinue)) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
$SOURCE_FOLDER = $args[0]
|
|
19
|
-
$
|
|
19
|
+
# Working folder lives in the system temp directory. $SOURCE_FOLDER may be an
|
|
20
|
+
# absolute path, so only its leaf name is used to build the working folder name.
|
|
21
|
+
$BUILD_SUBFOLDER = Join-Path ([System.IO.Path]::GetTempPath()) "flutter_$(Split-Path $SOURCE_FOLDER -Leaf)"
|
|
20
22
|
|
|
21
23
|
Write-Host "Current directory: $(Get-Location)"
|
|
22
24
|
Write-Host "Source folder: $SOURCE_FOLDER"
|
|
@@ -79,4 +81,7 @@ try {
|
|
|
79
81
|
Remove-Item -Path "flutter_test_stdout.txt" -ErrorAction SilentlyContinue
|
|
80
82
|
Remove-Item -Path "flutter_test_stderr.txt" -ErrorAction SilentlyContinue
|
|
81
83
|
Pop-Location
|
|
84
|
+
if (Test-Path $BUILD_SUBFOLDER) {
|
|
85
|
+
Remove-Item -Path $BUILD_SUBFOLDER -Recurse -Force -ErrorAction SilentlyContinue
|
|
86
|
+
}
|
|
82
87
|
}
|