Skip to content

Commit a6695d4

Browse files
authored
Replace dorny/paths-filter with native git diff (#96)
* Replace dorny/paths-filter with native git diff Use native git commands to detect path changes instead of the dorny/paths-filter action, which is not ProdSec approved. The replacement script handles both pull_request and push events, and includes handling for initial pushes where there is no previous commit to compare against. * Extract integration tests to standalone script Move the integration tests that require Falcon API credentials from main.yml to a standalone test-integration.sh script. This allows: - CI to run without secrets (unit tests only) - Developers to run integration tests locally with their own credentials - The script prompts for credentials if not set as environment variables The blog post can now reference the standalone script instead of the workflow file for demonstrating integration test automation. * Add venv setup and simplify integration test script - Auto-create and activate virtual environment - Install dependencies automatically - Remove FALCON_BASE_URL (FalconPy defaults to us-1) - Document prerequisites: deployed app and API scopes
1 parent 70a588c commit a6695d4

2 files changed

Lines changed: 177 additions & 112 deletions

File tree

.github/workflows/main.yml

Lines changed: 22 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,29 @@ jobs:
1414
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
1515
with:
1616
fetch-depth: 0
17-
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
17+
- name: Check for path changes
1818
id: changes
19-
with:
20-
filters: |
21-
functions:
22-
- 'functions/**'
23-
ui:
24-
- 'ui/**'
19+
run: |
20+
if [ "${{ github.event_name }}" == "pull_request" ]; then
21+
# For PRs, compare PR head against base
22+
git fetch origin ${{ github.event.pull_request.base.ref }}
23+
CHANGED_FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD)
24+
else
25+
# For push events, compare against default branch
26+
CHANGED_FILES=$(git diff --name-only origin/${{ github.event.repository.default_branch }}...HEAD)
27+
fi
28+
29+
if echo "$CHANGED_FILES" | grep -q '^functions/'; then
30+
echo "functions=true" >> $GITHUB_OUTPUT
31+
else
32+
echo "functions=false" >> $GITHUB_OUTPUT
33+
fi
34+
35+
if echo "$CHANGED_FILES" | grep -q '^ui/'; then
36+
echo "ui=true" >> $GITHUB_OUTPUT
37+
else
38+
echo "ui=false" >> $GITHUB_OUTPUT
39+
fi
2540
2641
test-functions:
2742
needs: check-changes
@@ -44,9 +59,6 @@ jobs:
4459
- name: Install Python dependencies
4560
run: pip install -r requirements.txt
4661
working-directory: ${{ matrix.function }}
47-
- name: Install HTTPie for API testing
48-
run: pip install httpie
49-
if: matrix.function == 'functions/csv-import'
5062
- name: Run tests if test_main.py exists
5163
run: |
5264
if [ -f "test_main.py" ]; then
@@ -56,108 +68,6 @@ jobs:
5668
echo "No test_main.py found, skipping tests"
5769
fi
5870
working-directory: ${{ matrix.function }}
59-
- name: Run Python function and verify output
60-
env:
61-
FALCON_CLIENT_ID: ${{ secrets.FALCON_CLIENT_ID }}
62-
FALCON_CLIENT_SECRET: ${{ secrets.FALCON_CLIENT_SECRET }}
63-
FALCON_BASE_URL: ${{ secrets.FALCON_BASE_URL }}
64-
APP_ID: ${{ secrets.APP_ID }}
65-
run: |
66-
# Start the function
67-
python main.py > output.log 2>&1 &
68-
PID=$!
69-
timeout=30
70-
elapsed=0
71-
while [ $elapsed -lt $timeout ]; do
72-
if grep -q "running at port 8081" output.log; then
73-
echo "✅ Application started successfully"
74-
break
75-
fi
76-
sleep 1
77-
elapsed=$((elapsed+1))
78-
done
79-
80-
if [ $elapsed -ge $timeout ]; then
81-
echo "❌ Application failed to start within $timeout seconds"
82-
cat output.log
83-
kill $PID 2>/dev/null || true
84-
exit 1
85-
fi
86-
87-
TEST_FAILURES=0
88-
89-
# Run CSV import tests
90-
if [[ "${{ matrix.function }}" == "functions/csv-import" ]]; then
91-
echo "Running CSV import tests..."
92-
sleep 2
93-
94-
# Create sample CSV if needed
95-
if [ ! -f "security_events.csv" ]; then
96-
echo "timestamp,event_type,severity,description,source_ip,destination_ip,user" > security_events.csv
97-
echo "2025-07-11T14:14:08Z,login_failure,medium,Failed login from IP 192.168.1.100,192.168.1.100,192.168.1.1,test.user" >> security_events.csv
98-
echo "2025-07-11T14:15:22Z,malware_detected,high,Malware detected on workstation,192.168.1.101,192.168.1.1,admin.user" >> security_events.csv
99-
fi
100-
101-
# Test 1: Import CSV with file path
102-
echo "=== Test 1: Import file path ==="
103-
set +e
104-
http --ignore-stdin POST :8081 method=POST url=/import-csv "body[csv_file_path]=security_events.csv" > import_file_output.log 2>&1
105-
IMPORT_FILE_EXIT_CODE=$?
106-
set -e
107-
108-
if [ $IMPORT_FILE_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_file_output.log; then
109-
echo "✅ Import file path test passed"
110-
else
111-
echo "❌ Import file path test failed"
112-
cat import_file_output.log
113-
TEST_FAILURES=$((TEST_FAILURES + 1))
114-
fi
115-
116-
# Test 2: Import CSV with inline data
117-
echo "=== Test 2: Import inline data ==="
118-
CSV_DATA=$(cat security_events.csv)
119-
set +e
120-
http --ignore-stdin POST :8081 method=POST url=/import-csv "body[csv_data]=$CSV_DATA" > import_data_output.log 2>&1
121-
IMPORT_DATA_EXIT_CODE=$?
122-
set -e
123-
124-
if [ $IMPORT_DATA_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_data_output.log; then
125-
echo "✅ Import inline data test passed"
126-
else
127-
echo "❌ Import inline data test failed"
128-
cat import_data_output.log
129-
TEST_FAILURES=$((TEST_FAILURES + 1))
130-
fi
131-
132-
# Test 3: Execute import.py script
133-
if [ -f "import.py" ]; then
134-
echo "=== Test 3: Import script ==="
135-
set +e
136-
python import.py > import_script_output.log 2>&1
137-
IMPORT_SCRIPT_EXIT_CODE=$?
138-
set -e
139-
140-
if [ $IMPORT_SCRIPT_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_script_output.log; then
141-
echo "✅ Import script test passed"
142-
else
143-
echo "❌ Import script test failed"
144-
cat import_script_output.log
145-
TEST_FAILURES=$((TEST_FAILURES + 1))
146-
fi
147-
fi
148-
fi
149-
150-
# Cleanup
151-
kill $PID 2>/dev/null || true
152-
153-
# Final result
154-
if [ $TEST_FAILURES -gt 0 ]; then
155-
echo "❌ $TEST_FAILURES test(s) failed"
156-
exit 1
157-
else
158-
echo "✅ All tests passed"
159-
fi
160-
working-directory: ${{ matrix.function }}
16171

16272
test-ui:
16373
needs: check-changes
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/bin/bash
2+
#
3+
# Integration test script for the CSV import function.
4+
# This script starts the function server and tests the import endpoints.
5+
#
6+
# Prerequisites:
7+
# - Python 3.x
8+
# - Collections Toolkit app deployed (install from Foundry > Templates, or run `foundry apps deploy`)
9+
#
10+
# Usage:
11+
# ./test-integration.sh
12+
#
13+
# Environment variables (will prompt if not set):
14+
# FALCON_CLIENT_ID - API client ID (requires Custom Storage Read and Write scopes)
15+
# FALCON_CLIENT_SECRET - API client secret
16+
# APP_ID - Your Foundry app ID
17+
18+
set -e
19+
20+
# Detect Python command
21+
if command -v python3 &> /dev/null; then
22+
PYTHON_CMD="python3"
23+
elif command -v python &> /dev/null; then
24+
PYTHON_CMD="python"
25+
else
26+
echo "Error: Python not found. Please install Python 3.x"
27+
exit 1
28+
fi
29+
30+
# Create and activate virtual environment
31+
if [ ! -d ".venv" ]; then
32+
echo "Creating virtual environment..."
33+
$PYTHON_CMD -m venv .venv
34+
fi
35+
36+
echo "Activating virtual environment..."
37+
source .venv/bin/activate
38+
39+
# Install dependencies
40+
echo "Installing dependencies..."
41+
pip install -q --upgrade pip -r requirements.txt
42+
pip install -q httpie
43+
44+
# Prompt for credentials if not set
45+
if [ -z "$FALCON_CLIENT_ID" ]; then
46+
read -p "Enter FALCON_CLIENT_ID: " FALCON_CLIENT_ID
47+
export FALCON_CLIENT_ID
48+
fi
49+
50+
if [ -z "$FALCON_CLIENT_SECRET" ]; then
51+
read -s -p "Enter FALCON_CLIENT_SECRET: " FALCON_CLIENT_SECRET
52+
echo
53+
export FALCON_CLIENT_SECRET
54+
fi
55+
56+
if [ -z "$APP_ID" ]; then
57+
read -p "Enter APP_ID: " APP_ID
58+
export APP_ID
59+
fi
60+
61+
echo "Starting integration tests..."
62+
63+
# Start the function
64+
$PYTHON_CMD main.py > output.log 2>&1 &
65+
PID=$!
66+
67+
# Wait for server to start
68+
timeout=30
69+
elapsed=0
70+
while [ $elapsed -lt $timeout ]; do
71+
if grep -q "running at port 8081" output.log; then
72+
echo "Application started successfully"
73+
break
74+
fi
75+
sleep 1
76+
elapsed=$((elapsed+1))
77+
done
78+
79+
if [ $elapsed -ge $timeout ]; then
80+
echo "Application failed to start within $timeout seconds"
81+
cat output.log
82+
kill $PID 2>/dev/null || true
83+
exit 1
84+
fi
85+
86+
TEST_FAILURES=0
87+
88+
echo "Running CSV import tests..."
89+
sleep 2
90+
91+
# Create sample CSV if needed
92+
if [ ! -f "security_events.csv" ]; then
93+
echo "timestamp,event_type,severity,description,source_ip,destination_ip,user" > security_events.csv
94+
echo "2025-07-11T14:14:08Z,login_failure,medium,Failed login from IP 192.168.1.100,192.168.1.100,192.168.1.1,test.user" >> security_events.csv
95+
echo "2025-07-11T14:15:22Z,malware_detected,high,Malware detected on workstation,192.168.1.101,192.168.1.1,admin.user" >> security_events.csv
96+
fi
97+
98+
# Test 1: Import CSV with file path
99+
echo "=== Test 1: Import file path ==="
100+
set +e
101+
http --ignore-stdin POST :8081 method=POST url=/import-csv "body[csv_file_path]=security_events.csv" > import_file_output.log 2>&1
102+
IMPORT_FILE_EXIT_CODE=$?
103+
set -e
104+
105+
if [ $IMPORT_FILE_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_file_output.log; then
106+
echo "Import file path test passed"
107+
else
108+
echo "Import file path test failed"
109+
cat import_file_output.log
110+
TEST_FAILURES=$((TEST_FAILURES + 1))
111+
fi
112+
113+
# Test 2: Import CSV with inline data
114+
echo "=== Test 2: Import inline data ==="
115+
CSV_DATA=$(cat security_events.csv)
116+
set +e
117+
http --ignore-stdin POST :8081 method=POST url=/import-csv "body[csv_data]=$CSV_DATA" > import_data_output.log 2>&1
118+
IMPORT_DATA_EXIT_CODE=$?
119+
set -e
120+
121+
if [ $IMPORT_DATA_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_data_output.log; then
122+
echo "Import inline data test passed"
123+
else
124+
echo "Import inline data test failed"
125+
cat import_data_output.log
126+
TEST_FAILURES=$((TEST_FAILURES + 1))
127+
fi
128+
129+
# Test 3: Execute import.py script
130+
if [ -f "import.py" ]; then
131+
echo "=== Test 3: Import script ==="
132+
set +e
133+
$PYTHON_CMD import.py > import_script_output.log 2>&1
134+
IMPORT_SCRIPT_EXIT_CODE=$?
135+
set -e
136+
137+
if [ $IMPORT_SCRIPT_EXIT_CODE -eq 0 ] && grep -q '"success": true' import_script_output.log; then
138+
echo "Import script test passed"
139+
else
140+
echo "Import script test failed"
141+
cat import_script_output.log
142+
TEST_FAILURES=$((TEST_FAILURES + 1))
143+
fi
144+
fi
145+
146+
# Cleanup
147+
kill $PID 2>/dev/null || true
148+
149+
# Final result
150+
if [ $TEST_FAILURES -gt 0 ]; then
151+
echo "$TEST_FAILURES test(s) failed"
152+
exit 1
153+
else
154+
echo "All tests passed"
155+
fi

0 commit comments

Comments
 (0)