Skip to content

Commit ccaeaaf

Browse files
committed
feat(node): implement Script file execution node (Story 3.3)
Implements Story 3.3: Script file execution node as .so plugin Features: - Execute script files or inline script content - Multi-language support: bash, sh, python3, python, node, ruby - Automatic interpreter detection and validation - Temporary file management with auto-cleanup - Parameters: script_path, script_content (mutually exclusive) - Full support for args, env, workdir, timeout Core capabilities: - Inline scripts via script_content parameter - External scripts via script_path parameter - Automatic temp file creation/cleanup for inline scripts - Interpreter validation before execution - Proper file extension handling (.sh, .py, .js, .rb) Security: - Environment variable sanitization - Safe temp file handling with unique names - Cleanup guaranteed via defer Code quality: - 86.2% test coverage (26 unit tests, 4 integration tests) - All tests passing with race detector - Clean architecture with helper methods - Comprehensive error handling Documentation: - Complete README with 7 examples - Comparison table with Shell node - Multi-language YAML examples - FAQ and troubleshooting guide Plugin compilation: script.so (4.9MB) Status: Ready for production use Difference from Shell node: - Shell: Single-line commands (sh -c) - Script: Multi-line scripts, multiple interpreters Story: docs/sprint-artifacts/3-3-script-file-execution-node.md
1 parent e095c62 commit ccaeaaf

10 files changed

Lines changed: 2572 additions & 0 deletions

File tree

docs/sprint-artifacts/3-3-script-file-execution-node.md

Lines changed: 873 additions & 0 deletions
Large diffs are not rendered by default.

docs/sprint-artifacts/sprint-status.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ development_status:
7373
epic-3: in-progress
7474
3-1-node-interface-design: done # ✅代码审查完成,所有问题已修复(Makefile语法,File List更新,Task 5完成),AC1-AC3 100%达成,覆盖率92.2%,Echo示例编译&测试通过,性能达标(<1ms),完成日期2025-12-30
7575
3-2-shell-command-execution-node: done # ✅代码审查通过,安全问题已修复(args转义,env清理),复杂度优化,文档增强,测试95.2%覆盖率,2025-12-30
76+
3-3-script-file-execution-node: done # ✅实现完成,AC1-AC3达成,单元测试86.2%覆盖率,集成测试4场景全通过,插件编译4.9MB,多语言支持,完成日期2025-12-30
7677
3-3-script-file-execution-node: ready-for-dev
7778
3-4-sleep-delay-node: ready-for-dev
7879
3-5-http-request-node: ready-for-dev
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
name: Script Node Examples
2+
3+
jobs:
4+
bash-scripts:
5+
name: Bash Script Examples
6+
runs-on: linux-amd64
7+
steps:
8+
# Inline bash script
9+
- name: Bash inline script
10+
uses: exec/script@v1
11+
with:
12+
script_content: |
13+
#!/bin/bash
14+
echo "Current user: $(whoami)"
15+
echo "Current directory: $(pwd)"
16+
echo "System: $(uname -a)"
17+
interpreter: bash
18+
19+
# Bash script with arguments
20+
- name: Bash script with args
21+
uses: exec/script@v1
22+
with:
23+
script_content: |
24+
#!/bin/bash
25+
echo "First arg: $1"
26+
echo "Second arg: $2"
27+
echo "All args: $@"
28+
interpreter: bash
29+
args: ["production", "v1.2.3"]
30+
31+
# Bash script with environment variables
32+
- name: Bash with env vars
33+
uses: exec/script@v1
34+
with:
35+
script_content: |
36+
#!/bin/bash
37+
echo "Database: $DB_HOST:$DB_PORT"
38+
echo "API Key: $API_KEY"
39+
interpreter: bash
40+
env:
41+
DB_HOST: "db.example.com"
42+
DB_PORT: "5432"
43+
API_KEY: "secret-key-123"
44+
45+
python-scripts:
46+
name: Python Script Examples
47+
runs-on: linux-amd64
48+
steps:
49+
# Simple Python script
50+
- name: Python inline script
51+
uses: exec/script@v1
52+
with:
53+
script_content: |
54+
#!/usr/bin/env python3
55+
import sys
56+
import os
57+
58+
print(f"Python version: {sys.version}")
59+
print(f"Platform: {sys.platform}")
60+
print(f"Current directory: {os.getcwd()}")
61+
interpreter: python3
62+
63+
# Python with arguments and env
64+
- name: Python data processing
65+
uses: exec/script@v1
66+
with:
67+
script_content: |
68+
#!/usr/bin/env python3
69+
import sys
70+
import os
71+
import json
72+
73+
if len(sys.argv) < 3:
74+
print("Usage: script <input> <output>")
75+
sys.exit(1)
76+
77+
input_file = sys.argv[1]
78+
output_file = sys.argv[2]
79+
env_name = os.getenv('ENVIRONMENT', 'development')
80+
81+
print(f"Processing: {input_file} -> {output_file}")
82+
print(f"Environment: {env_name}")
83+
84+
# Simulate data processing
85+
data = {"input": input_file, "output": output_file, "env": env_name}
86+
print(f"Result: {json.dumps(data, indent=2)}")
87+
interpreter: python3
88+
args: ["input.csv", "output.json"]
89+
env:
90+
ENVIRONMENT: "production"
91+
92+
script-files:
93+
name: Script File Examples
94+
runs-on: linux-amd64
95+
steps:
96+
# Execute existing script file
97+
- name: Run deployment script
98+
uses: exec/script@v1
99+
with:
100+
script_path: /app/scripts/deploy.sh
101+
interpreter: bash
102+
args: ["production", "--verbose"]
103+
timeout: 300
104+
105+
# Run Python script file
106+
- name: Run Python analysis
107+
uses: exec/script@v1
108+
with:
109+
script_path: /app/scripts/analyze.py
110+
interpreter: python3
111+
workdir: /app/data
112+
env:
113+
DATA_PATH: "/app/data/input"
114+
OUTPUT_PATH: "/app/data/output"
115+
116+
multi-language:
117+
name: Multi-Language Examples
118+
runs-on: linux-amd64
119+
steps:
120+
# Node.js script
121+
- name: Node.js script
122+
uses: exec/script@v1
123+
with:
124+
script_content: |
125+
const os = require('os');
126+
const fs = require('fs');
127+
128+
console.log('Node.js version:', process.version);
129+
console.log('Platform:', process.platform);
130+
console.log('Hostname:', os.hostname());
131+
console.log('CPUs:', os.cpus().length);
132+
interpreter: node
133+
134+
# Ruby script
135+
- name: Ruby script
136+
uses: exec/script@v1
137+
with:
138+
script_content: |
139+
#!/usr/bin/env ruby
140+
141+
puts "Ruby version: #{RUBY_VERSION}"
142+
puts "Platform: #{RUBY_PLATFORM}"
143+
144+
# Process arguments
145+
if ARGV.length > 0
146+
puts "Arguments: #{ARGV.join(', ')}"
147+
end
148+
interpreter: ruby
149+
args: ["arg1", "arg2"]
150+
151+
error-handling:
152+
name: Error Handling Examples
153+
runs-on: linux-amd64
154+
steps:
155+
# Script with timeout
156+
- name: Long-running script with timeout
157+
uses: exec/script@v1
158+
with:
159+
script_content: |
160+
#!/bin/bash
161+
echo "Starting long task..."
162+
sleep 100
163+
echo "Task completed"
164+
interpreter: bash
165+
timeout: 5
166+
continue-on-error: true
167+
168+
# Script that might fail
169+
- name: Conditional script
170+
uses: exec/script@v1
171+
with:
172+
script_content: |
173+
#!/bin/bash
174+
if [ ! -f /tmp/config.txt ]; then
175+
echo "Warning: Config file not found" >&2
176+
echo "Creating default config..."
177+
echo "default=true" > /tmp/config.txt
178+
fi
179+
cat /tmp/config.txt
180+
interpreter: bash
181+
182+
# Cleanup script (always runs)
183+
- name: Cleanup
184+
uses: exec/script@v1
185+
with:
186+
script_content: |
187+
#!/bin/bash
188+
echo "Cleaning up temporary files..."
189+
rm -f /tmp/config.txt
190+
echo "Cleanup complete"
191+
interpreter: bash
192+
193+
complex-workflows:
194+
name: Complex Workflow Examples
195+
runs-on: linux-amd64
196+
steps:
197+
# Multi-step deployment
198+
- name: Pre-deployment checks
199+
uses: exec/script@v1
200+
with:
201+
script_content: |
202+
#!/bin/bash
203+
set -e
204+
205+
echo "Checking prerequisites..."
206+
command -v docker >/dev/null 2>&1 || { echo "Docker not installed"; exit 1; }
207+
command -v git >/dev/null 2>&1 || { echo "Git not installed"; exit 1; }
208+
209+
echo "Checking disk space..."
210+
available=$(df / | tail -1 | awk '{print $4}')
211+
if [ "$available" -lt 1000000 ]; then
212+
echo "Insufficient disk space"
213+
exit 1
214+
fi
215+
216+
echo "All checks passed!"
217+
interpreter: bash
218+
219+
- name: Build application
220+
uses: exec/script@v1
221+
with:
222+
script_content: |
223+
#!/bin/bash
224+
set -e
225+
226+
echo "Building application..."
227+
npm install
228+
npm run build
229+
230+
echo "Build complete!"
231+
ls -lh dist/
232+
interpreter: bash
233+
workdir: /app/frontend
234+
timeout: 600
235+
236+
- name: Run tests
237+
uses: exec/script@v1
238+
with:
239+
script_content: |
240+
#!/usr/bin/env python3
241+
import subprocess
242+
import sys
243+
244+
print("Running test suite...")
245+
246+
# Run unit tests
247+
result = subprocess.run(['pytest', 'tests/unit'], capture_output=True)
248+
if result.returncode != 0:
249+
print("Unit tests failed!")
250+
sys.exit(1)
251+
252+
# Run integration tests
253+
result = subprocess.run(['pytest', 'tests/integration'], capture_output=True)
254+
if result.returncode != 0:
255+
print("Integration tests failed!")
256+
sys.exit(1)
257+
258+
print("All tests passed!")
259+
interpreter: python3
260+
workdir: /app/backend
261+
env:
262+
PYTHONPATH: "/app/backend/src"
263+
TEST_ENV: "ci"

plugins/exec/script/Makefile

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.PHONY: build test clean install check
2+
3+
PLUGIN_NAME = script.so
4+
GO_VERSION_MIN = 1.22
5+
6+
check:
7+
@echo "Checking Go version..."
8+
@go version | grep -q "go1.2[2-9]\|go1.[3-9][0-9]" || (echo "Error: Go $(GO_VERSION_MIN)+ required" && exit 1)
9+
@echo "Checking CGO..."
10+
@if [ -z "$${CGO_ENABLED}" ] || [ "$${CGO_ENABLED}" != "1" ]; then \
11+
echo "Warning: CGO_ENABLED should be set to 1"; \
12+
echo "Run: export CGO_ENABLED=1"; \
13+
fi
14+
@echo "✓ Environment checks passed"
15+
16+
build: check
17+
@echo "Building plugin..."
18+
CGO_ENABLED=1 go build -buildmode=plugin -o $(PLUGIN_NAME) main.go
19+
@echo "✓ Plugin built: $(PLUGIN_NAME)"
20+
21+
test:
22+
@echo "Running tests..."
23+
go test -v -race -coverprofile=coverage.out
24+
@echo "\n=== Coverage Report ==="
25+
@go tool cover -func=coverage.out | tail -1
26+
@echo "✓ Tests passed"
27+
28+
integration-test: build
29+
@echo "Running integration tests..."
30+
go test -v -tags=integration -run TestScriptPlugin
31+
@echo "✓ Integration tests passed"
32+
33+
clean:
34+
rm -f $(PLUGIN_NAME) coverage.out
35+
@echo "✓ Cleaned"
36+
37+
install: build
38+
@echo "Installing plugin..."
39+
@mkdir -p /opt/waterflow/plugins
40+
cp $(PLUGIN_NAME) /opt/waterflow/plugins/
41+
@echo "✓ Installed to /opt/waterflow/plugins/$(PLUGIN_NAME)"
42+
43+
.DEFAULT_GOAL := build

0 commit comments

Comments
 (0)