@sinch/cli 0.4.3 → 0.4.6
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/README.md +1 -1
- package/bin/s +2 -2
- package/bin/sinch +17 -36
- package/bin/sinch.cmd +2 -0
- package/package.json +9 -83
- package/scripts/postinstall.js +142 -0
- package/dist/index.js +0 -722
- package/scripts/README-E2E.md +0 -242
- package/scripts/e2e-test.sh +0 -155
- package/scripts/post-build.js +0 -36
- package/scripts/setup-dev.js +0 -188
- package/scripts/smoke-test.js +0 -90
package/scripts/README-E2E.md
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
# E2E Testing Documentation
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
End-to-end testing infrastructure for the Sinch CLI that tests the complete workflow against the real API.
|
|
6
|
-
|
|
7
|
-
## What is Tested
|
|
8
|
-
|
|
9
|
-
The E2E test (`e2e-test.sh`) performs the following workflow:
|
|
10
|
-
|
|
11
|
-
1. ✅ **CLI Installation** - Verifies CLI is installed and accessible
|
|
12
|
-
2. ✅ **Authentication** - Checks credentials are configured
|
|
13
|
-
3. ✅ **List Functions** - Verifies API connectivity
|
|
14
|
-
4. ✅ **Init Function** - Creates a new function from template
|
|
15
|
-
5. ✅ **Deploy Function** - Deploys function to production
|
|
16
|
-
6. ✅ **Verify Deployment** - Confirms function is running
|
|
17
|
-
7. ✅ **Check Status** - Validates function status endpoint
|
|
18
|
-
8. ✅ **Check Logs** - Validates log streaming endpoint
|
|
19
|
-
9. ✅ **Delete Function** - Cleans up test resources
|
|
20
|
-
|
|
21
|
-
## Running E2E Tests
|
|
22
|
-
|
|
23
|
-
### Option 1: GitLab CI (Automatic)
|
|
24
|
-
|
|
25
|
-
E2E tests run automatically on:
|
|
26
|
-
|
|
27
|
-
- Every push to `main` branch
|
|
28
|
-
- Every merge request
|
|
29
|
-
|
|
30
|
-
**Required GitLab CI Variables:**
|
|
31
|
-
|
|
32
|
-
- `E2E_KEY_ID` (protected)
|
|
33
|
-
- `E2E_KEY_SECRET` (protected, masked)
|
|
34
|
-
- `E2E_APPLICATION_KEY` (protected)
|
|
35
|
-
- `E2E_APPLICATION_SECRET` (protected, masked)
|
|
36
|
-
|
|
37
|
-
### Option 2: Local with Node.js
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
# Build and install CLI
|
|
41
|
-
npm run build
|
|
42
|
-
npm install -g .
|
|
43
|
-
|
|
44
|
-
# Set environment variables
|
|
45
|
-
export SINCH_KEY_ID="your-key-id"
|
|
46
|
-
export SINCH_KEY_SECRET="your-key-secret"
|
|
47
|
-
export SINCH_APPLICATION_KEY="your-app-key"
|
|
48
|
-
export SINCH_APPLICATION_SECRET="your-app-secret"
|
|
49
|
-
|
|
50
|
-
# Run E2E test
|
|
51
|
-
./scripts/e2e-test.sh
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Option 3: Docker (Isolated Environment)
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# Build E2E Docker image
|
|
58
|
-
docker build -f Dockerfile.e2e -t sinch-cli-e2e .
|
|
59
|
-
|
|
60
|
-
# Run E2E tests in Docker
|
|
61
|
-
docker run --rm \
|
|
62
|
-
-e SINCH_KEY_ID="your-key-id" \
|
|
63
|
-
-e SINCH_KEY_SECRET="your-key-secret" \
|
|
64
|
-
-e SINCH_APPLICATION_KEY="your-app-key" \
|
|
65
|
-
-e SINCH_APPLICATION_SECRET="your-app-secret" \
|
|
66
|
-
sinch-cli-e2e
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Docker Benefits:**
|
|
70
|
-
|
|
71
|
-
- Clean, isolated environment every run
|
|
72
|
-
- No local Node.js required
|
|
73
|
-
- Same environment as CI
|
|
74
|
-
- Great for testing before pushing
|
|
75
|
-
|
|
76
|
-
## Test Output
|
|
77
|
-
|
|
78
|
-
The test script provides colored output:
|
|
79
|
-
|
|
80
|
-
- ✅ **Green** - Test passed
|
|
81
|
-
- ❌ **Red** - Test failed
|
|
82
|
-
- ⚠️ **Yellow** - Warning (non-fatal)
|
|
83
|
-
|
|
84
|
-
Example output:
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
=== Sinch CLI E2E Test ===
|
|
88
|
-
|
|
89
|
-
Test 1: CLI installation check
|
|
90
|
-
✅ PASS: CLI installed (version: 0.1.0)
|
|
91
|
-
|
|
92
|
-
Test 2: Authentication check
|
|
93
|
-
✅ PASS: Authentication OK
|
|
94
|
-
|
|
95
|
-
Test 3: List functions
|
|
96
|
-
✅ PASS: List functions OK
|
|
97
|
-
|
|
98
|
-
...
|
|
99
|
-
|
|
100
|
-
=== E2E Test Complete ===
|
|
101
|
-
✅ All tests passed!
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Credentials
|
|
105
|
-
|
|
106
|
-
### CI/CD Environment
|
|
107
|
-
|
|
108
|
-
In GitLab CI, credentials are provided via environment variables configured in GitLab:
|
|
109
|
-
|
|
110
|
-
Settings → CI/CD → Variables
|
|
111
|
-
|
|
112
|
-
### Local Development
|
|
113
|
-
|
|
114
|
-
Two options:
|
|
115
|
-
|
|
116
|
-
1. **Environment Variables** (recommended for E2E):
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
export SINCH_KEY_ID="..."
|
|
120
|
-
export SINCH_KEY_SECRET="..."
|
|
121
|
-
export SINCH_APPLICATION_KEY="..."
|
|
122
|
-
export SINCH_APPLICATION_SECRET="..."
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
2. **OS Keychain** (normal CLI usage):
|
|
126
|
-
```bash
|
|
127
|
-
sinch auth login
|
|
128
|
-
# Credentials stored securely in OS keychain
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Troubleshooting
|
|
132
|
-
|
|
133
|
-
### Test Fails: Authentication not configured
|
|
134
|
-
|
|
135
|
-
**Problem:** Environment variables not set
|
|
136
|
-
|
|
137
|
-
**Solution:**
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
# Check env vars are set
|
|
141
|
-
echo $SINCH_KEY_ID
|
|
142
|
-
echo $SINCH_APPLICATION_KEY
|
|
143
|
-
|
|
144
|
-
# Set them if missing
|
|
145
|
-
export SINCH_KEY_ID="..."
|
|
146
|
-
export SINCH_KEY_SECRET="..."
|
|
147
|
-
export SINCH_APPLICATION_KEY="..."
|
|
148
|
-
export SINCH_APPLICATION_SECRET="..."
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Test Fails: Deploy timeout
|
|
152
|
-
|
|
153
|
-
**Problem:** API is slow or function deployment taking longer than expected
|
|
154
|
-
|
|
155
|
-
**Solution:** The test allows up to 2 minutes for deployment. If it times out:
|
|
156
|
-
|
|
157
|
-
1. Check API status
|
|
158
|
-
2. Check function complexity
|
|
159
|
-
3. Try again (might be temporary)
|
|
160
|
-
|
|
161
|
-
### Test Fails: Function not found after delete
|
|
162
|
-
|
|
163
|
-
**Problem:** This is actually expected - the test verifies deletion worked
|
|
164
|
-
|
|
165
|
-
**Solution:** No action needed, this is correct behavior
|
|
166
|
-
|
|
167
|
-
## Cleanup
|
|
168
|
-
|
|
169
|
-
The E2E test **always cleans up** test functions, even on failure:
|
|
170
|
-
|
|
171
|
-
- Uses a `cleanup()` trap function
|
|
172
|
-
- Runs on EXIT, INT (Ctrl+C), or TERM signals
|
|
173
|
-
- Deletes test function by ID
|
|
174
|
-
- Safe to run multiple times
|
|
175
|
-
|
|
176
|
-
## CI/CD Integration
|
|
177
|
-
|
|
178
|
-
### Pipeline Stages
|
|
179
|
-
|
|
180
|
-
```
|
|
181
|
-
┌─────────┐
|
|
182
|
-
│ Build │ (26s)
|
|
183
|
-
└────┬────┘
|
|
184
|
-
│
|
|
185
|
-
┌────▼────┐
|
|
186
|
-
│ Test:E2E│ (13s) ← New!
|
|
187
|
-
└────┬────┘
|
|
188
|
-
│
|
|
189
|
-
┌────▼────┐
|
|
190
|
-
│ Publish │ (14s)
|
|
191
|
-
└─────────┘
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### When Tests Run
|
|
195
|
-
|
|
196
|
-
- **Main branch:** Every commit
|
|
197
|
-
- **Merge requests:** Every push to MR
|
|
198
|
-
- **Tags:** No (only build + publish)
|
|
199
|
-
|
|
200
|
-
### Failure Handling
|
|
201
|
-
|
|
202
|
-
- `allow_failure: false` - Pipeline **fails** if E2E test fails
|
|
203
|
-
- Prevents broken code from being published
|
|
204
|
-
- Forces fixes before merge
|
|
205
|
-
|
|
206
|
-
## Best Practices
|
|
207
|
-
|
|
208
|
-
1. **Run E2E before pushing:**
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
npm run build
|
|
212
|
-
npm install -g .
|
|
213
|
-
./scripts/e2e-test.sh
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
2. **Use Docker for final verification:**
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
docker build -f Dockerfile.e2e -t sinch-cli-e2e .
|
|
220
|
-
docker run --rm -e SINCH_KEY_ID="..." ... sinch-cli-e2e
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
3. **Watch CI pipeline:**
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
glab ci view
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
4. **Never commit with failing E2E tests** - The pipeline will block you anyway
|
|
230
|
-
|
|
231
|
-
## Files
|
|
232
|
-
|
|
233
|
-
- `scripts/e2e-test.sh` - Main E2E test script
|
|
234
|
-
- `Dockerfile.e2e` - Docker image for isolated testing
|
|
235
|
-
- `.gitlab-ci.yml` - CI pipeline configuration
|
|
236
|
-
- `scripts/README-E2E.md` - This documentation
|
|
237
|
-
|
|
238
|
-
## Support
|
|
239
|
-
|
|
240
|
-
- **Issues:** Report at GitLab project issues
|
|
241
|
-
- **CI Logs:** View in GitLab CI pipeline
|
|
242
|
-
- **Local Debugging:** Add `set -x` to script for verbose output
|
package/scripts/e2e-test.sh
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
set -e # Exit on error
|
|
3
|
-
|
|
4
|
-
echo "=== Sinch CLI E2E Test ==="
|
|
5
|
-
echo ""
|
|
6
|
-
|
|
7
|
-
# Generate unique test name
|
|
8
|
-
TEST_NAME="e2e-test"
|
|
9
|
-
FUNCTION_ID=""
|
|
10
|
-
FAILED=0
|
|
11
|
-
|
|
12
|
-
# Colors for output
|
|
13
|
-
RED='\033[0;31m'
|
|
14
|
-
GREEN='\033[0;32m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
NC='\033[0m' # No Color
|
|
17
|
-
|
|
18
|
-
# Function to cleanup on exit
|
|
19
|
-
cleanup() {
|
|
20
|
-
EXIT_CODE=$?
|
|
21
|
-
|
|
22
|
-
if [ -n "$FUNCTION_ID" ]; then
|
|
23
|
-
echo ""
|
|
24
|
-
echo "${YELLOW}Cleaning up test function: $FUNCTION_ID${NC}"
|
|
25
|
-
sinch functions delete "$FUNCTION_ID" --force 2>/dev/null || true
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
# If script exited with error and FAILED wasn't set, set it now
|
|
29
|
-
if [ $EXIT_CODE -ne 0 ] && [ $FAILED -eq 0 ]; then
|
|
30
|
-
FAILED=$EXIT_CODE
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
if [ $FAILED -eq 0 ]; then
|
|
34
|
-
echo ""
|
|
35
|
-
echo "${GREEN}=== E2E Test Complete ===${NC}"
|
|
36
|
-
echo "${GREEN}All tests passed!${NC}"
|
|
37
|
-
else
|
|
38
|
-
echo ""
|
|
39
|
-
echo "${RED}=== E2E Test Failed ===${NC}"
|
|
40
|
-
echo "${RED}Exit code: $FAILED${NC}"
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
exit $FAILED
|
|
44
|
-
}
|
|
45
|
-
trap cleanup EXIT INT TERM
|
|
46
|
-
|
|
47
|
-
# Test 1: Check CLI is installed
|
|
48
|
-
echo "Test 1: CLI installation check"
|
|
49
|
-
if ! command -v sinch >/dev/null 2>&1; then
|
|
50
|
-
echo "${RED}FAIL: sinch command not found${NC}"
|
|
51
|
-
FAILED=1
|
|
52
|
-
exit 1
|
|
53
|
-
fi
|
|
54
|
-
CLI_VERSION=$(sinch --version)
|
|
55
|
-
echo "${GREEN}PASS: CLI installed (version: $CLI_VERSION)${NC}"
|
|
56
|
-
echo ""
|
|
57
|
-
|
|
58
|
-
# Test 2: List functions (will fail if auth not configured via env vars)
|
|
59
|
-
echo "Test 2: List functions"
|
|
60
|
-
if ! sinch functions list >/dev/null 2>&1; then
|
|
61
|
-
echo "${RED}FAIL: Cannot list functions${NC}"
|
|
62
|
-
echo "Make sure these environment variables are set:"
|
|
63
|
-
echo " - SINCH_KEY_ID"
|
|
64
|
-
echo " - SINCH_KEY_SECRET"
|
|
65
|
-
echo " - SINCH_APPLICATION_KEY"
|
|
66
|
-
echo " - SINCH_APPLICATION_SECRET"
|
|
67
|
-
FAILED=1
|
|
68
|
-
exit 1
|
|
69
|
-
fi
|
|
70
|
-
echo "${GREEN}PASS: List functions OK${NC}"
|
|
71
|
-
echo ""
|
|
72
|
-
|
|
73
|
-
# Test 3: Init function
|
|
74
|
-
echo "Test 3: Initialize function"
|
|
75
|
-
TEST_DIR="/tmp/$TEST_NAME"
|
|
76
|
-
cd /tmp
|
|
77
|
-
|
|
78
|
-
if ! sinch functions init simple-voice-ivr \
|
|
79
|
-
--name "$TEST_NAME" \
|
|
80
|
-
--skip-install \
|
|
81
|
-
--non-interactive >/dev/null 2>&1; then
|
|
82
|
-
echo "${RED}FAIL: Function init failed${NC}"
|
|
83
|
-
FAILED=1
|
|
84
|
-
exit 1
|
|
85
|
-
fi
|
|
86
|
-
echo "${GREEN}PASS: Function initialized${NC}"
|
|
87
|
-
echo ""
|
|
88
|
-
|
|
89
|
-
# Test 4: Deploy function
|
|
90
|
-
echo "Test 4: Deploy function (this may take a minute...)"
|
|
91
|
-
cd "$TEST_NAME" || {
|
|
92
|
-
echo "${RED}FAIL: Cannot change to function directory${NC}"
|
|
93
|
-
FAILED=1
|
|
94
|
-
exit 1
|
|
95
|
-
}
|
|
96
|
-
DEPLOY_OUTPUT=$(sinch functions deploy --non-interactive 2>&1) || {
|
|
97
|
-
echo "${RED}FAIL: Deploy command failed${NC}"
|
|
98
|
-
echo "$DEPLOY_OUTPUT"
|
|
99
|
-
FAILED=1
|
|
100
|
-
exit 1
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
# Extract function ID from deploy output
|
|
104
|
-
FUNCTION_ID=$(echo "$DEPLOY_OUTPUT" | grep "Function ID:" | awk '{print $NF}')
|
|
105
|
-
if [ -z "$FUNCTION_ID" ]; then
|
|
106
|
-
echo "${RED}FAIL: Deploy succeeded but no function ID found${NC}"
|
|
107
|
-
echo "$DEPLOY_OUTPUT"
|
|
108
|
-
FAILED=1
|
|
109
|
-
exit 1
|
|
110
|
-
fi
|
|
111
|
-
echo "${GREEN}PASS: Function deployed (ID: $FUNCTION_ID)${NC}"
|
|
112
|
-
echo ""
|
|
113
|
-
|
|
114
|
-
# Test 5: Verify deployment
|
|
115
|
-
echo "Test 5: Verify function is accessible"
|
|
116
|
-
sleep 2 # Give it a moment to be fully available
|
|
117
|
-
if ! sinch functions list | grep -q "$TEST_NAME"; then
|
|
118
|
-
echo "${RED}FAIL: Function not found in list${NC}"
|
|
119
|
-
FAILED=1
|
|
120
|
-
exit 1
|
|
121
|
-
fi
|
|
122
|
-
echo "${GREEN}PASS: Function is accessible${NC}"
|
|
123
|
-
echo ""
|
|
124
|
-
|
|
125
|
-
# Test 6: Check function status
|
|
126
|
-
echo "Test 6: Check function status"
|
|
127
|
-
if ! sinch functions status "$FUNCTION_ID" >/dev/null 2>&1; then
|
|
128
|
-
echo "${YELLOW}WARN: Cannot fetch function status (non-fatal)${NC}"
|
|
129
|
-
else
|
|
130
|
-
echo "${GREEN}PASS: Function status OK${NC}"
|
|
131
|
-
fi
|
|
132
|
-
echo ""
|
|
133
|
-
|
|
134
|
-
# Test 7: Check logs are accessible
|
|
135
|
-
echo "Test 7: Check logs are accessible"
|
|
136
|
-
if ! sinch functions logs "$FUNCTION_ID" --limit 10 >/dev/null 2>&1; then
|
|
137
|
-
echo "${YELLOW}WARN: Cannot fetch logs (non-fatal)${NC}"
|
|
138
|
-
else
|
|
139
|
-
echo "${GREEN}PASS: Logs accessible${NC}"
|
|
140
|
-
fi
|
|
141
|
-
echo ""
|
|
142
|
-
|
|
143
|
-
# Test 8: Delete function (cleanup will also handle this)
|
|
144
|
-
echo "Test 8: Delete function"
|
|
145
|
-
if ! sinch functions delete "$FUNCTION_ID" --force >/dev/null 2>&1; then
|
|
146
|
-
echo "${RED}FAIL: Delete failed${NC}"
|
|
147
|
-
FAILED=1
|
|
148
|
-
exit 1
|
|
149
|
-
fi
|
|
150
|
-
echo "${GREEN}PASS: Function deleted${NC}"
|
|
151
|
-
FUNCTION_ID="" # Clear so cleanup doesn't try again
|
|
152
|
-
echo ""
|
|
153
|
-
|
|
154
|
-
# All tests passed
|
|
155
|
-
exit 0
|
package/scripts/post-build.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
async function postBuild() {
|
|
7
|
-
console.log('Running post-build tasks...');
|
|
8
|
-
|
|
9
|
-
// Ensure dist directory exists
|
|
10
|
-
await fs.ensureDir('dist');
|
|
11
|
-
|
|
12
|
-
// Copy package.json to dist for runtime requires
|
|
13
|
-
await fs.copy('package.json', 'dist/package.json');
|
|
14
|
-
|
|
15
|
-
// Copy skills folder to dist
|
|
16
|
-
const skillsSource = path.join(__dirname, '..', 'skills');
|
|
17
|
-
const skillsDest = path.join(__dirname, '..', 'dist', 'skills');
|
|
18
|
-
if (await fs.pathExists(skillsSource)) {
|
|
19
|
-
await fs.copy(skillsSource, skillsDest, { overwrite: true });
|
|
20
|
-
console.log('Copied skills to dist/skills');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Ensure the CLI entry point works
|
|
24
|
-
const distIndex = path.join('dist', 'index.js');
|
|
25
|
-
if (await fs.pathExists(distIndex)) {
|
|
26
|
-
// Add shebang if not present
|
|
27
|
-
const content = await fs.readFile(distIndex, 'utf8');
|
|
28
|
-
if (!content.startsWith('#!/usr/bin/env node')) {
|
|
29
|
-
await fs.writeFile(distIndex, '#!/usr/bin/env node\n' + content);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
console.log('Post-build tasks completed.');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
postBuild().catch(console.error);
|
package/scripts/setup-dev.js
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const { execSync, spawn } = require('child_process');
|
|
7
|
-
|
|
8
|
-
const SINCH_BIN = path.join(os.homedir(), '.sinch', 'bin');
|
|
9
|
-
const BINARY_NAME = process.platform === 'win32' ? 'sinch.exe' : 'sinch';
|
|
10
|
-
const BINARY_SRC = path.join(__dirname, '..', 'dist', 'sinch-bun.exe');
|
|
11
|
-
const BINARY_DEST = path.join(SINCH_BIN, BINARY_NAME);
|
|
12
|
-
|
|
13
|
-
async function setup() {
|
|
14
|
-
console.log('\n🔧 Setting up Sinch CLI development environment\n');
|
|
15
|
-
|
|
16
|
-
// 1. Copy Bun binary to ~/.sinch/bin/
|
|
17
|
-
console.log('📦 Installing Bun binary...');
|
|
18
|
-
await fs.ensureDir(SINCH_BIN);
|
|
19
|
-
|
|
20
|
-
if (await fs.pathExists(BINARY_SRC)) {
|
|
21
|
-
await fs.copy(BINARY_SRC, BINARY_DEST, { overwrite: true });
|
|
22
|
-
if (process.platform !== 'win32') {
|
|
23
|
-
await fs.chmod(BINARY_DEST, 0o755);
|
|
24
|
-
}
|
|
25
|
-
console.log(` ✅ Installed to ${BINARY_DEST}`);
|
|
26
|
-
} else {
|
|
27
|
-
console.log(' ⚠️ Bun binary not found — run "npm run build:binary" first');
|
|
28
|
-
console.log(` Expected at: ${BINARY_SRC}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// 2. Generate PowerShell profile additions
|
|
32
|
-
if (process.platform === 'win32') {
|
|
33
|
-
await setupPowerShell();
|
|
34
|
-
} else {
|
|
35
|
-
await setupShell();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 3. Install shell completions
|
|
39
|
-
console.log('\n🔧 Installing shell completions...');
|
|
40
|
-
try {
|
|
41
|
-
await installCompletions();
|
|
42
|
-
console.log(' ✅ Completions installed');
|
|
43
|
-
} catch (e) {
|
|
44
|
-
console.log(` ⚠️ Completion install failed: ${e.message}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 4. Smoke test
|
|
48
|
-
console.log('\n🧪 Smoke test...');
|
|
49
|
-
try {
|
|
50
|
-
const version = execSync(`"${BINARY_DEST}" --version`, { encoding: 'utf8' }).trim();
|
|
51
|
-
console.log(` ✅ sinch binary v${version}`);
|
|
52
|
-
} catch (e) {
|
|
53
|
-
console.log(` ⚠️ Binary smoke test failed: ${e.message}`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
const devVersion = execSync('sinch --version', { encoding: 'utf8' }).trim();
|
|
58
|
-
console.log(` ✅ sinch dev (npm link) v${devVersion}`);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.log(` ⚠️ npm link not set up: ${e.message}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
console.log('\n✨ Setup complete!\n');
|
|
64
|
-
console.log('Commands:');
|
|
65
|
-
console.log(' sinch <cmd> → Fast Bun binary (for daily use)');
|
|
66
|
-
console.log(' sf <cmd> → Shortcut for "sinch functions <cmd>"');
|
|
67
|
-
console.log(' sd <cmd> → Dev version via npm link (for CLI development)');
|
|
68
|
-
console.log('');
|
|
69
|
-
|
|
70
|
-
if (process.platform === 'win32') {
|
|
71
|
-
console.log('⚡ Restart PowerShell or run: . $PROFILE');
|
|
72
|
-
} else {
|
|
73
|
-
console.log('⚡ Restart your shell or run: source ~/.bashrc');
|
|
74
|
-
}
|
|
75
|
-
console.log('');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function setupPowerShell() {
|
|
79
|
-
console.log('\n🖥️ Setting up PowerShell aliases...');
|
|
80
|
-
|
|
81
|
-
const profileSnippet = `
|
|
82
|
-
# ── Sinch CLI aliases (auto-generated by sinch-cli setup) ──
|
|
83
|
-
function sinch { & "$env:USERPROFILE\\.sinch\\bin\\sinch.exe" @args }
|
|
84
|
-
function sf { sinch functions @args }
|
|
85
|
-
function sd { & sinch.cmd @args }
|
|
86
|
-
# ── End Sinch CLI aliases ──
|
|
87
|
-
`.trim();
|
|
88
|
-
|
|
89
|
-
// Find PowerShell profile
|
|
90
|
-
const profilePath = await getPowerShellProfilePath();
|
|
91
|
-
|
|
92
|
-
if (!profilePath) {
|
|
93
|
-
console.log(' ⚠️ Could not detect PowerShell profile path');
|
|
94
|
-
console.log(' Add these to your PowerShell profile manually:\n');
|
|
95
|
-
console.log(profileSnippet);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Read existing profile
|
|
100
|
-
let profileContent = '';
|
|
101
|
-
if (await fs.pathExists(profilePath)) {
|
|
102
|
-
profileContent = await fs.readFile(profilePath, 'utf8');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Check if already installed
|
|
106
|
-
if (profileContent.includes('Sinch CLI aliases')) {
|
|
107
|
-
// Replace existing block
|
|
108
|
-
const replaced = profileContent.replace(
|
|
109
|
-
/# ── Sinch CLI aliases.*?# ── End Sinch CLI aliases ──/s,
|
|
110
|
-
profileSnippet
|
|
111
|
-
);
|
|
112
|
-
await fs.writeFile(profilePath, replaced);
|
|
113
|
-
console.log(` ✅ Updated aliases in ${profilePath}`);
|
|
114
|
-
} else {
|
|
115
|
-
// Append
|
|
116
|
-
await fs.writeFile(profilePath, profileContent + '\n\n' + profileSnippet + '\n');
|
|
117
|
-
console.log(` ✅ Added aliases to ${profilePath}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function setupShell() {
|
|
122
|
-
console.log('\n🖥️ Setting up shell aliases...');
|
|
123
|
-
|
|
124
|
-
const snippet = `
|
|
125
|
-
# ── Sinch CLI aliases (auto-generated by sinch-cli setup) ──
|
|
126
|
-
alias sinch='$HOME/.sinch/bin/sinch'
|
|
127
|
-
sf() { sinch functions "$@"; }
|
|
128
|
-
sd() { command sinch "$@"; }
|
|
129
|
-
# ── End Sinch CLI aliases ──
|
|
130
|
-
`.trim();
|
|
131
|
-
|
|
132
|
-
// Try .bashrc, then .zshrc
|
|
133
|
-
const rcFile = (await fs.pathExists(path.join(os.homedir(), '.zshrc')))
|
|
134
|
-
? path.join(os.homedir(), '.zshrc')
|
|
135
|
-
: path.join(os.homedir(), '.bashrc');
|
|
136
|
-
|
|
137
|
-
let content = '';
|
|
138
|
-
if (await fs.pathExists(rcFile)) {
|
|
139
|
-
content = await fs.readFile(rcFile, 'utf8');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (content.includes('Sinch CLI aliases')) {
|
|
143
|
-
const replaced = content.replace(
|
|
144
|
-
/# ── Sinch CLI aliases.*?# ── End Sinch CLI aliases ──/s,
|
|
145
|
-
snippet
|
|
146
|
-
);
|
|
147
|
-
await fs.writeFile(rcFile, replaced);
|
|
148
|
-
console.log(` ✅ Updated aliases in ${rcFile}`);
|
|
149
|
-
} else {
|
|
150
|
-
await fs.writeFile(rcFile, content + '\n\n' + snippet + '\n');
|
|
151
|
-
console.log(` ✅ Added aliases to ${rcFile}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function installCompletions() {
|
|
156
|
-
execSync('node scripts/postinstall.js', {
|
|
157
|
-
cwd: path.join(__dirname, '..'),
|
|
158
|
-
env: { ...process.env, SINCH_FORCE_POSTINSTALL: '1' },
|
|
159
|
-
stdio: 'pipe',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function getPowerShellProfilePath() {
|
|
164
|
-
return new Promise((resolve) => {
|
|
165
|
-
// Try pwsh first, then powershell
|
|
166
|
-
const tryShell = (cmd) => {
|
|
167
|
-
return new Promise((res) => {
|
|
168
|
-
const ps = spawn(cmd, ['-NoProfile', '-Command', '$PROFILE'], {
|
|
169
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
170
|
-
});
|
|
171
|
-
let out = '';
|
|
172
|
-
ps.stdout.on('data', (d) => (out += d.toString()));
|
|
173
|
-
ps.on('close', (code) => res(code === 0 ? out.trim() : null));
|
|
174
|
-
ps.on('error', () => res(null));
|
|
175
|
-
});
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
tryShell('pwsh').then((p) => {
|
|
179
|
-
if (p) return resolve(p);
|
|
180
|
-
tryShell('powershell').then((p2) => resolve(p2));
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
setup().catch((err) => {
|
|
186
|
-
console.error('Setup failed:', err.message);
|
|
187
|
-
process.exit(1);
|
|
188
|
-
});
|
package/scripts/smoke-test.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Smoke test for the Bun-compiled binary.
|
|
5
|
-
* Verifies the binary works for key code paths that could differ from Node.js.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { execSync } = require('child_process');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
|
|
12
|
-
const rawBinary =
|
|
13
|
-
process.argv[2] ||
|
|
14
|
-
path.join(os.homedir(), '.sinch', 'bin', process.platform === 'win32' ? 'sinch.exe' : 'sinch');
|
|
15
|
-
const BINARY = path.resolve(rawBinary);
|
|
16
|
-
|
|
17
|
-
let passed = 0;
|
|
18
|
-
let failed = 0;
|
|
19
|
-
|
|
20
|
-
function test(name, fn) {
|
|
21
|
-
try {
|
|
22
|
-
fn();
|
|
23
|
-
console.log(` ✅ ${name}`);
|
|
24
|
-
passed++;
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.log(` ❌ ${name}: ${e.message}`);
|
|
27
|
-
failed++;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function run(args) {
|
|
32
|
-
return execSync(`"${BINARY}" ${args}`, {
|
|
33
|
-
encoding: 'utf8',
|
|
34
|
-
timeout: 10000,
|
|
35
|
-
env: { ...process.env, CI: '1' },
|
|
36
|
-
}).trim();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log(`\n🧪 Smoke testing: ${BINARY}\n`);
|
|
40
|
-
|
|
41
|
-
test('--version returns semver', () => {
|
|
42
|
-
const version = run('--version');
|
|
43
|
-
if (!/^\d+\.\d+\.\d+/.test(version)) {
|
|
44
|
-
throw new Error(`Got: ${version}`);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('--help shows all top-level commands', () => {
|
|
49
|
-
const help = run('--help');
|
|
50
|
-
const required = ['functions', 'templates', 'voice', 'auth', 'config', 'fax', 'sip', 'numbers'];
|
|
51
|
-
for (const cmd of required) {
|
|
52
|
-
if (!help.includes(cmd)) {
|
|
53
|
-
throw new Error(`Missing command: ${cmd}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('functions --help shows subcommands', () => {
|
|
59
|
-
const help = run('functions --help');
|
|
60
|
-
const required = ['init', 'list', 'deploy', 'dev', 'status', 'logs'];
|
|
61
|
-
for (const cmd of required) {
|
|
62
|
-
if (!help.includes(cmd)) {
|
|
63
|
-
throw new Error(`Missing subcommand: ${cmd}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('fax --help shows subcommands', () => {
|
|
69
|
-
const help = run('fax --help');
|
|
70
|
-
if (!help.includes('send') || !help.includes('list')) {
|
|
71
|
-
throw new Error('Missing fax subcommands');
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('sip --help shows subcommands', () => {
|
|
76
|
-
const help = run('sip --help');
|
|
77
|
-
if (!help.includes('trunks') || !help.includes('endpoints')) {
|
|
78
|
-
throw new Error('Missing sip subcommands');
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('config --help shows subcommands', () => {
|
|
83
|
-
const help = run('config --help');
|
|
84
|
-
if (!help.includes('set') || !help.includes('get') || !help.includes('profile')) {
|
|
85
|
-
throw new Error('Missing config subcommands');
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
console.log(`\n${passed} passed, ${failed} failed\n`);
|
|
90
|
-
process.exit(failed > 0 ? 1 : 0);
|