Skip to content

Commit e02ec24

Browse files
committed
Add CI workflows for testing and version checks, enhance README and TESTING documentation
1 parent ae45e1a commit e02ec24

File tree

7 files changed

+719
-0
lines changed

7 files changed

+719
-0
lines changed

.github/workflows/check-version.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
paths:
88
- 'sync-ssh-keys.sh'
99
- 'users.conf'
10+
- '.github/workflows/check-version.yml'
1011

1112
jobs:
1213
check-version:

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
# Run all individual checks in parallel
13+
lint:
14+
uses: ./.github/workflows/lint.yml
15+
16+
version-check:
17+
uses: ./.github/workflows/check-version.yml
18+
if: github.event_name == 'pull_request'
19+
20+
test:
21+
uses: ./.github/workflows/test.yml
22+
23+
# Final status check
24+
ci-success:
25+
runs-on: ubuntu-latest
26+
needs: [lint, test]
27+
if: always()
28+
steps:
29+
- name: Check all jobs status
30+
run: |
31+
if [[ "${{ needs.lint.result }}" != "success" ]]; then
32+
echo "Lint job failed"
33+
exit 1
34+
fi
35+
36+
if [[ "${{ needs.test.result }}" != "success" ]]; then
37+
echo "Test job failed"
38+
exit 1
39+
fi
40+
41+
if [[ "${{ github.event_name }}" == "pull_request" && "${{ needs.version-check.result }}" != "success" ]]; then
42+
echo "Version check job failed"
43+
exit 1
44+
fi
45+
46+
echo "All CI checks passed!"

.github/workflows/lint.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ on:
66
- main
77
paths:
88
- 'sync-ssh-keys.sh'
9+
- 'users.conf'
10+
- '.github/workflows/lint.yml'
11+
push:
12+
branches:
13+
- main
14+
paths:
15+
- 'sync-ssh-keys.sh'
16+
- 'users.conf'
917
- '.github/workflows/lint.yml'
1018

1119
jobs:

.github/workflows/test.yml

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
name: Test SSH Key Sync
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- 'sync-ssh-keys.sh'
9+
- 'users.conf'
10+
- '.github/workflows/test.yml'
11+
push:
12+
branches:
13+
- main
14+
paths:
15+
- 'sync-ssh-keys.sh'
16+
- 'users.conf'
17+
- '.github/workflows/test.yml'
18+
19+
jobs:
20+
test:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v4
25+
26+
- name: Make script executable
27+
run: chmod +x sync-ssh-keys.sh
28+
29+
- name: Create test users
30+
run: |
31+
sudo useradd -m testuser1 || true
32+
sudo useradd -m testuser2 || true
33+
sudo useradd -m testuser3 || true
34+
35+
- name: Setup test environment
36+
run: |
37+
# Create a temporary test configuration
38+
cat > test-users.conf << 'EOF'
39+
#!/bin/bash
40+
# Test configuration for GitHub Actions
41+
42+
# No GitHub token needed for public key tests
43+
CONF_GITHUB_TOKEN=""
44+
45+
# Test user key mapping with public GitHub users
46+
declare -A USER_KEYS=(
47+
# Test with a known GitHub user that has public keys
48+
["testuser1"]="ghuser:octocat"
49+
# Test with raw public key (using GitHub's public key endpoint)
50+
["testuser2"]="raw:https://github.com/octocat.keys"
51+
)
52+
EOF
53+
54+
- name: Test configuration validation
55+
run: |
56+
# Test with missing config file
57+
mv users.conf users.conf.backup
58+
! ./sync-ssh-keys.sh 2>&1 | grep -q "Configuration file 'users.conf' not found"
59+
mv users.conf.backup users.conf
60+
echo "✓ Configuration file validation works"
61+
62+
- name: Test configuration loading
63+
run: |
64+
# Use test configuration
65+
cp test-users.conf users.conf
66+
67+
# Test basic functionality (dry run equivalent)
68+
timeout 30s ./sync-ssh-keys.sh || {
69+
echo "Script execution completed or timed out as expected"
70+
}
71+
echo "✓ Configuration loading test completed"
72+
73+
- name: Test invalid method handling
74+
run: |
75+
# Create config with invalid method
76+
cat > test-invalid.conf << 'EOF'
77+
#!/bin/bash
78+
declare -A USER_KEYS=(
79+
["testuser1"]="invalid:test"
80+
)
81+
EOF
82+
83+
cp test-invalid.conf users.conf
84+
85+
# Should fail with unsupported method error
86+
! ./sync-ssh-keys.sh 2>&1 | grep -q "Unsupported method 'invalid'"
87+
echo "✓ Invalid method handling works"
88+
89+
- name: Test script syntax and functions
90+
run: |
91+
# Source the script to test function definitions
92+
bash -n sync-ssh-keys.sh
93+
echo "✓ Script syntax is valid"
94+
95+
# Test individual functions by sourcing (without execution)
96+
(
97+
source sync-ssh-keys.sh 2>/dev/null || true
98+
echo "✓ Script can be sourced"
99+
)
100+
101+
- name: Test with empty user array
102+
run: |
103+
# Create config with empty user array
104+
cat > test-empty.conf << 'EOF'
105+
#!/bin/bash
106+
declare -A USER_KEYS=()
107+
EOF
108+
109+
cp test-empty.conf users.conf
110+
111+
# Should exit cleanly with warning
112+
./sync-ssh-keys.sh 2>&1 | grep -q "No users defined in USER_KEYS array"
113+
echo "✓ Empty user array handling works"
114+
115+
- name: Test GitHub user key fetching (mock)
116+
run: |
117+
# Test the curl command format for GitHub user keys
118+
curl -fsSL "https://github.com/octocat.keys" | head -5
119+
echo "✓ GitHub user key endpoint is accessible"
120+
121+
- name: Test script version extraction
122+
run: |
123+
# Verify version can be extracted
124+
VERSION=$(awk -F'"' '/SCRIPT_VERSION/ {print $2; exit}' sync-ssh-keys.sh)
125+
if [[ -n "$VERSION" ]]; then
126+
echo "✓ Script version found: $VERSION"
127+
else
128+
echo "✗ Script version not found"
129+
exit 1
130+
fi
131+
132+
- name: Test self-update function (dry run)
133+
run: |
134+
# Test that self-update function exists and can be parsed
135+
grep -q "self_update()" sync-ssh-keys.sh
136+
grep -q "get_latest_release_url" sync-ssh-keys.sh
137+
grep -q "download_latest_script" sync-ssh-keys.sh
138+
echo "✓ Self-update functions are present"
139+
140+
- name: Test error handling
141+
run: |
142+
# Create config that will trigger various error conditions
143+
cat > test-errors.conf << 'EOF'
144+
#!/bin/bash
145+
declare -A USER_KEYS=(
146+
["nonexistentuser"]="ghuser:nonexistentuser12345"
147+
["testuser1"]="raw:https://invalid-url-that-does-not-exist.example.com/keys"
148+
)
149+
EOF
150+
151+
cp test-errors.conf users.conf
152+
153+
# Should handle errors gracefully
154+
! ./sync-ssh-keys.sh
155+
echo "✓ Error handling test completed"
156+
157+
- name: Test log message formatting
158+
run: |
159+
# Verify log messages are properly formatted with timestamps
160+
cp test-users.conf users.conf
161+
timeout 10s ./sync-ssh-keys.sh 2>&1 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}:' || true
162+
echo "✓ Log message format test completed"
163+
164+
- name: Cleanup test environment
165+
run: |
166+
# Restore original configuration
167+
git checkout users.conf 2>/dev/null || true
168+
169+
# Remove test files
170+
rm -f test-*.conf
171+
172+
# Remove test users (if they exist)
173+
sudo userdel -f testuser1 2>/dev/null || true
174+
sudo userdel -f testuser2 2>/dev/null || true
175+
sudo userdel -f testuser3 2>/dev/null || true
176+
177+
echo "✓ Cleanup completed"
178+
179+
integration-test:
180+
runs-on: ubuntu-latest
181+
needs: test
182+
if: github.event_name == 'pull_request'
183+
steps:
184+
- name: Checkout code
185+
uses: actions/checkout@v4
186+
187+
- name: Make script executable
188+
run: chmod +x sync-ssh-keys.sh
189+
190+
- name: Create integration test user
191+
run: |
192+
sudo useradd -m integrationuser
193+
194+
- name: Setup integration test configuration
195+
run: |
196+
cat > users.conf << 'EOF'
197+
#!/bin/bash
198+
# Integration test configuration
199+
declare -A USER_KEYS=(
200+
# Use a real GitHub user with known public keys for integration testing
201+
["integrationuser"]="ghuser:octocat"
202+
)
203+
EOF
204+
205+
- name: Run integration test
206+
run: |
207+
# Run the script and verify it completes successfully
208+
./sync-ssh-keys.sh
209+
210+
# Verify the authorized_keys file was created
211+
if [[ -f /home/integrationuser/.ssh/authorized_keys ]]; then
212+
echo "✓ authorized_keys file created successfully"
213+
echo "File contents:"
214+
head -2 /home/integrationuser/.ssh/authorized_keys
215+
echo "File permissions:"
216+
ls -la /home/integrationuser/.ssh/authorized_keys
217+
else
218+
echo "✗ authorized_keys file was not created"
219+
exit 1
220+
fi
221+
222+
- name: Verify file permissions
223+
run: |
224+
# Check SSH directory permissions
225+
PERMS=$(stat -c "%a" /home/integrationuser/.ssh)
226+
if [[ "$PERMS" == "700" ]]; then
227+
echo "✓ SSH directory permissions are correct (700)"
228+
else
229+
echo "✗ SSH directory permissions are incorrect: $PERMS"
230+
exit 1
231+
fi
232+
233+
# Check authorized_keys file permissions
234+
PERMS=$(stat -c "%a" /home/integrationuser/.ssh/authorized_keys)
235+
if [[ "$PERMS" == "600" ]]; then
236+
echo "✓ authorized_keys file permissions are correct (600)"
237+
else
238+
echo "✗ authorized_keys file permissions are incorrect: $PERMS"
239+
exit 1
240+
fi
241+
242+
- name: Cleanup integration test
243+
run: |
244+
sudo userdel -rf integrationuser 2>/dev/null || true
245+
git checkout users.conf

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SSH Key Sync
22

33
[![Lint Status](https://img.shields.io/github/actions/workflow/status/locus313/ssh-key-sync/lint.yml?style=flat-square&label=lint)](https://github.com/locus313/ssh-key-sync/actions)
4+
[![Test Status](https://img.shields.io/github/actions/workflow/status/locus313/ssh-key-sync/ci.yml?style=flat-square&label=tests)](https://github.com/locus313/ssh-key-sync/actions)
45
[![License](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
56
[![Shell](https://img.shields.io/badge/Shell-Bash-green?style=flat-square&logo=gnu-bash)](https://www.gnu.org/software/bash/)
67
[![Version](https://img.shields.io/badge/Version-0.1.2-orange?style=flat-square)](https://github.com/locus313/ssh-key-sync/releases)
@@ -272,6 +273,33 @@ The script provides detailed logging with timestamps:
272273
2025-08-29 12:00:02: Synchronization complete. Processed: 1, Failed: 0
273274
```
274275

276+
## Testing
277+
278+
The project includes comprehensive testing to ensure reliability:
279+
280+
### Automated Testing
281+
- **GitHub Actions CI**: Runs on all pull requests and pushes
282+
- **Lint Checks**: ShellCheck validation for code quality
283+
- **Unit Tests**: Configuration validation and function testing
284+
- **Integration Tests**: Real environment testing with user creation
285+
286+
### Running Tests Locally
287+
```bash
288+
# Quick validation
289+
./test.sh
290+
291+
# Manual syntax check
292+
bash -n sync-ssh-keys.sh
293+
294+
# With ShellCheck (if installed)
295+
shellcheck sync-ssh-keys.sh
296+
```
297+
298+
### CI Status
299+
[![Test Status](https://img.shields.io/github/actions/workflow/status/locus313/ssh-key-sync/ci.yml?style=flat-square&label=tests)](https://github.com/locus313/ssh-key-sync/actions)
300+
301+
For detailed testing information, see [TESTING.md](TESTING.md).
302+
275303
## Security Considerations
276304

277305
- Store GitHub tokens securely and rotate them regularly

0 commit comments

Comments
 (0)