seqpulse 0.4.0 → 0.5.2
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/CI_CD_SNIPPETS_MULTI-PLATFORM.md +221 -0
- package/README.md +9 -4
- package/bin/seqpulse.js +50 -3
- package/index.js +2 -0
- package/package.json +2 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# CI/CD Snippets Multi-Platform
|
|
2
|
+
|
|
3
|
+
Snippets de reference pour integrer `SeqPulse` avec la meme logique sur plusieurs plateformes CI/CD:
|
|
4
|
+
|
|
5
|
+
1. `trigger` avant le deploy
|
|
6
|
+
2. deploy applicatif
|
|
7
|
+
3. `finish` toujours execute, meme en cas d'echec
|
|
8
|
+
|
|
9
|
+
Tous les snippets ci-dessous utilisent le package publie `seqpulse@0.5.1` et la sortie ciblee `--output deploymentId`.
|
|
10
|
+
|
|
11
|
+
## Variables requises
|
|
12
|
+
|
|
13
|
+
Definir ces secrets/variables dans votre plateforme CI:
|
|
14
|
+
|
|
15
|
+
- `SEQPULSE_BASE_URL`
|
|
16
|
+
- `SEQPULSE_API_KEY`
|
|
17
|
+
- `SEQPULSE_METRICS_ENDPOINT`
|
|
18
|
+
|
|
19
|
+
## Design cible
|
|
20
|
+
|
|
21
|
+
- `trigger` reste non bloquant pour ne pas casser le deploy si SeqPulse est indisponible
|
|
22
|
+
- `deploymentId` est capture dans une variable CI, pas dans un JSON parse fragile
|
|
23
|
+
- `finish` recoit un statut explicite venant de la plateforme
|
|
24
|
+
|
|
25
|
+
## GitHub Actions
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
name: deploy
|
|
29
|
+
|
|
30
|
+
on:
|
|
31
|
+
push:
|
|
32
|
+
branches: [main]
|
|
33
|
+
|
|
34
|
+
jobs:
|
|
35
|
+
deploy:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
env:
|
|
38
|
+
SEQPULSE_BASE_URL: ${{ secrets.SEQPULSE_BASE_URL }}
|
|
39
|
+
SEQPULSE_API_KEY: ${{ secrets.SEQPULSE_API_KEY }}
|
|
40
|
+
SEQPULSE_METRICS_ENDPOINT: ${{ secrets.SEQPULSE_METRICS_ENDPOINT }}
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
|
|
45
|
+
- uses: actions/setup-node@v4
|
|
46
|
+
with:
|
|
47
|
+
node-version: 20
|
|
48
|
+
cache: npm
|
|
49
|
+
|
|
50
|
+
- name: Install
|
|
51
|
+
run: npm ci
|
|
52
|
+
|
|
53
|
+
- name: SeqPulse Trigger
|
|
54
|
+
id: seqpulse_trigger
|
|
55
|
+
run: |
|
|
56
|
+
DEPLOYMENT_ID="$(npx -y seqpulse@0.5.1 ci trigger \
|
|
57
|
+
--env prod \
|
|
58
|
+
--branch "${GITHUB_REF_NAME}" \
|
|
59
|
+
--non-blocking true \
|
|
60
|
+
--output deploymentId)"
|
|
61
|
+
echo "deployment_id=${DEPLOYMENT_ID}" >> "$GITHUB_OUTPUT"
|
|
62
|
+
|
|
63
|
+
- name: Deploy
|
|
64
|
+
run: ./deploy.sh
|
|
65
|
+
|
|
66
|
+
- name: SeqPulse Finish
|
|
67
|
+
if: ${{ always() }}
|
|
68
|
+
run: |
|
|
69
|
+
npx -y seqpulse@0.5.1 ci finish \
|
|
70
|
+
--deployment-id "${{ steps.seqpulse_trigger.outputs.deployment_id }}" \
|
|
71
|
+
--job-status "${{ job.status }}" \
|
|
72
|
+
--non-blocking true
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## GitLab CI
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
deploy_prod:
|
|
79
|
+
image: node:20
|
|
80
|
+
stage: deploy
|
|
81
|
+
script:
|
|
82
|
+
- npm ci
|
|
83
|
+
- |
|
|
84
|
+
DEPLOYMENT_ID="$(npx -y seqpulse@0.5.1 ci trigger \
|
|
85
|
+
--env prod \
|
|
86
|
+
--branch "${CI_COMMIT_REF_NAME}" \
|
|
87
|
+
--non-blocking true \
|
|
88
|
+
--output deploymentId)"
|
|
89
|
+
printf 'SEQPULSE_DEPLOYMENT_ID=%s\n' "$DEPLOYMENT_ID" > .seqpulse.env
|
|
90
|
+
- ./deploy.sh
|
|
91
|
+
after_script:
|
|
92
|
+
- . ./.seqpulse.env 2>/dev/null || true
|
|
93
|
+
- |
|
|
94
|
+
npx -y seqpulse@0.5.1 ci finish \
|
|
95
|
+
--deployment-id "${SEQPULSE_DEPLOYMENT_ID}" \
|
|
96
|
+
--job-status "${CI_JOB_STATUS}" \
|
|
97
|
+
--non-blocking true
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## CircleCI
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
version: 2.1
|
|
104
|
+
|
|
105
|
+
jobs:
|
|
106
|
+
deploy:
|
|
107
|
+
docker:
|
|
108
|
+
- image: cimg/node:20.11
|
|
109
|
+
steps:
|
|
110
|
+
- checkout
|
|
111
|
+
|
|
112
|
+
- run:
|
|
113
|
+
name: Install
|
|
114
|
+
command: npm ci
|
|
115
|
+
|
|
116
|
+
- run:
|
|
117
|
+
name: SeqPulse Trigger
|
|
118
|
+
command: |
|
|
119
|
+
DEPLOYMENT_ID="$(npx -y seqpulse@0.5.1 ci trigger \
|
|
120
|
+
--env prod \
|
|
121
|
+
--branch "${CIRCLE_BRANCH}" \
|
|
122
|
+
--non-blocking true \
|
|
123
|
+
--output deploymentId)"
|
|
124
|
+
echo "export SEQPULSE_DEPLOYMENT_ID=${DEPLOYMENT_ID}" >> "$BASH_ENV"
|
|
125
|
+
|
|
126
|
+
- run:
|
|
127
|
+
name: Deploy
|
|
128
|
+
command: |
|
|
129
|
+
set +e
|
|
130
|
+
./deploy.sh
|
|
131
|
+
DEPLOY_EXIT_CODE=$?
|
|
132
|
+
if [ "$DEPLOY_EXIT_CODE" -eq 0 ]; then
|
|
133
|
+
echo 'export SEQPULSE_JOB_STATUS=success' >> "$BASH_ENV"
|
|
134
|
+
else
|
|
135
|
+
echo 'export SEQPULSE_JOB_STATUS=failed' >> "$BASH_ENV"
|
|
136
|
+
fi
|
|
137
|
+
exit "$DEPLOY_EXIT_CODE"
|
|
138
|
+
|
|
139
|
+
- run:
|
|
140
|
+
name: SeqPulse Finish
|
|
141
|
+
when: always
|
|
142
|
+
command: |
|
|
143
|
+
. "$BASH_ENV"
|
|
144
|
+
npx -y seqpulse@0.5.1 ci finish \
|
|
145
|
+
--deployment-id "${SEQPULSE_DEPLOYMENT_ID}" \
|
|
146
|
+
--job-status "${SEQPULSE_JOB_STATUS:-failed}" \
|
|
147
|
+
--non-blocking true
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Jenkins
|
|
151
|
+
|
|
152
|
+
```groovy
|
|
153
|
+
pipeline {
|
|
154
|
+
agent any
|
|
155
|
+
|
|
156
|
+
options {
|
|
157
|
+
disableConcurrentBuilds()
|
|
158
|
+
timestamps()
|
|
159
|
+
timeout(time: 20, unit: 'MINUTES')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
environment {
|
|
163
|
+
SEQPULSE_BASE_URL = credentials('seqpulse_base_url')
|
|
164
|
+
SEQPULSE_API_KEY = credentials('seqpulse_api_key')
|
|
165
|
+
SEQPULSE_METRICS_ENDPOINT = credentials('seqpulse_metrics_endpoint')
|
|
166
|
+
SEQPULSE_DEPLOYMENT_ID = ''
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
stages {
|
|
170
|
+
stage('Install') {
|
|
171
|
+
steps {
|
|
172
|
+
sh 'npm ci'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
stage('SeqPulse Trigger') {
|
|
177
|
+
steps {
|
|
178
|
+
script {
|
|
179
|
+
def branch = env.CHANGE_BRANCH ?: env.BRANCH_NAME ?: env.GIT_BRANCH ?: 'main'
|
|
180
|
+
env.SEQPULSE_DEPLOYMENT_ID = sh(
|
|
181
|
+
script: """
|
|
182
|
+
npx -y seqpulse@0.5.1 ci trigger \
|
|
183
|
+
--env prod \
|
|
184
|
+
--branch "${branch}" \
|
|
185
|
+
--non-blocking true \
|
|
186
|
+
--output deploymentId
|
|
187
|
+
""",
|
|
188
|
+
returnStdout: true
|
|
189
|
+
).trim()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
stage('Deploy') {
|
|
195
|
+
steps {
|
|
196
|
+
sh './deploy.sh'
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
post {
|
|
202
|
+
always {
|
|
203
|
+
script {
|
|
204
|
+
def jobStatus = (currentBuild.currentResult ?: 'SUCCESS').toLowerCase()
|
|
205
|
+
sh """
|
|
206
|
+
npx -y seqpulse@0.5.1 ci finish \
|
|
207
|
+
--deployment-id "${env.SEQPULSE_DEPLOYMENT_ID ?: ''}" \
|
|
208
|
+
--job-status "${jobStatus}" \
|
|
209
|
+
--non-blocking true
|
|
210
|
+
"""
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Notes pratiques
|
|
218
|
+
|
|
219
|
+
- Si le package est deja installe dans le projet, vous pouvez remplacer `npx -y seqpulse@0.5.1` par `npx seqpulse`.
|
|
220
|
+
- Pour les deploys `prod`, ajoutez un gate explicite sur `main`/tag si votre pipeline est multi-branches.
|
|
221
|
+
- Le pattern recommande est toujours le meme: `trigger -> deploy -> finish`.
|
package/README.md
CHANGED
|
@@ -85,22 +85,27 @@ async function runDeployment() {
|
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
87
|
# Trigger
|
|
88
|
-
npx -y seqpulse@0.
|
|
88
|
+
DEPLOYMENT_ID="$(npx -y seqpulse@0.5.1 ci trigger \
|
|
89
89
|
--env prod \
|
|
90
|
-
--
|
|
90
|
+
--output deploymentId)"
|
|
91
91
|
|
|
92
92
|
# Finish
|
|
93
|
-
npx -y seqpulse@0.
|
|
94
|
-
--deployment-id "$
|
|
93
|
+
npx -y seqpulse@0.5.1 ci finish \
|
|
94
|
+
--deployment-id "$DEPLOYMENT_ID" \
|
|
95
95
|
--job-status "$JOB_STATUS"
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
Notes:
|
|
99
99
|
|
|
100
|
+
- `--output deploymentId` permet de capturer directement l'identifiant de deployment.
|
|
100
101
|
- `--github-output` ecrit `deployment_id`, `status`, `http_status`.
|
|
101
102
|
- mode par defaut: non-bloquant (`--non-blocking true`).
|
|
102
103
|
- pour mode strict: `--blocking`.
|
|
103
104
|
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
- [Multi-platform CI/CD snippets](./CI_CD_SNIPPETS_MULTI-PLATFORM.md)
|
|
108
|
+
|
|
104
109
|
## Compatibility note
|
|
105
110
|
|
|
106
111
|
Cette evolution **n'ecrase pas** le SDK runtime actuel:
|
package/bin/seqpulse.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
const fs = require("node:fs");
|
|
5
5
|
const seqpulse = require("../index.js");
|
|
6
6
|
|
|
7
|
+
const BOOLEAN_FLAGS = new Set(["blocking", "help", "h"]);
|
|
8
|
+
|
|
7
9
|
function printUsage() {
|
|
8
10
|
console.error(`Usage:
|
|
9
11
|
seqpulse ci trigger [options]
|
|
@@ -21,6 +23,7 @@ Common options:
|
|
|
21
23
|
--env <name> (default: prod)
|
|
22
24
|
--non-blocking <true|false> (default: true)
|
|
23
25
|
--blocking (equivalent to --non-blocking false)
|
|
26
|
+
--output <field> (default: json)
|
|
24
27
|
--github-output <path> (writes outputs for GitHub Actions)
|
|
25
28
|
|
|
26
29
|
Trigger options:
|
|
@@ -56,6 +59,9 @@ function parseArgs(argv) {
|
|
|
56
59
|
const key = token.slice(2);
|
|
57
60
|
const next = argv[i + 1];
|
|
58
61
|
if (!next || next.startsWith("--")) {
|
|
62
|
+
if (!BOOLEAN_FLAGS.has(key)) {
|
|
63
|
+
throw new Error(`Option --${key} requires a value`);
|
|
64
|
+
}
|
|
59
65
|
options[key] = true;
|
|
60
66
|
continue;
|
|
61
67
|
}
|
|
@@ -97,6 +103,42 @@ function appendGithubOutput(filePath, pairs) {
|
|
|
97
103
|
fs.appendFileSync(filePath, `${lines}\n`);
|
|
98
104
|
}
|
|
99
105
|
|
|
106
|
+
function resolveOutputValue(result, output) {
|
|
107
|
+
const mode = String(output || "json").trim();
|
|
108
|
+
if (!mode || mode === "json") {
|
|
109
|
+
return JSON.stringify(result);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const aliases = {
|
|
113
|
+
deployment_id: "deploymentId",
|
|
114
|
+
http_status: "httpStatus",
|
|
115
|
+
};
|
|
116
|
+
const path = (aliases[mode] || mode).split(".");
|
|
117
|
+
|
|
118
|
+
let current = result;
|
|
119
|
+
for (const segment of path) {
|
|
120
|
+
if (current === undefined || current === null) {
|
|
121
|
+
current = "";
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
current = current[segment];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (current === undefined || current === null) {
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof current === "object") {
|
|
132
|
+
return JSON.stringify(current);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return String(current);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function printResult(result, output) {
|
|
139
|
+
console.log(sanitizeOutput(resolveOutputValue(result, output)));
|
|
140
|
+
}
|
|
141
|
+
|
|
100
142
|
function resolveNonBlocking(options) {
|
|
101
143
|
if (options.blocking !== undefined) return false;
|
|
102
144
|
if (options["non-blocking"] !== undefined) return parseBoolean(options["non-blocking"], true);
|
|
@@ -130,7 +172,7 @@ async function runTrigger(options) {
|
|
|
130
172
|
http_status: result.httpStatus || "",
|
|
131
173
|
});
|
|
132
174
|
|
|
133
|
-
|
|
175
|
+
printResult({ action: "trigger", ...result }, options.output);
|
|
134
176
|
|
|
135
177
|
if (!result.ok && !clientConfig.nonBlocking) {
|
|
136
178
|
process.exitCode = 1;
|
|
@@ -140,7 +182,12 @@ async function runTrigger(options) {
|
|
|
140
182
|
async function runFinish(options) {
|
|
141
183
|
const clientConfig = buildClient(options);
|
|
142
184
|
const deploymentId = options["deployment-id"] || process.env.SEQPULSE_DEPLOYMENT_ID || "";
|
|
143
|
-
const jobStatus =
|
|
185
|
+
const jobStatus =
|
|
186
|
+
options["job-status"] ||
|
|
187
|
+
process.env.JOB_STATUS ||
|
|
188
|
+
process.env.CI_JOB_STATUS ||
|
|
189
|
+
process.env.BUILD_RESULT ||
|
|
190
|
+
"success";
|
|
144
191
|
const resultValue = options.result || seqpulse.inferResultFromPipelineStatus(jobStatus);
|
|
145
192
|
|
|
146
193
|
const client = seqpulse.createCIClient(clientConfig);
|
|
@@ -154,7 +201,7 @@ async function runFinish(options) {
|
|
|
154
201
|
http_status: result.httpStatus || "",
|
|
155
202
|
});
|
|
156
203
|
|
|
157
|
-
|
|
204
|
+
printResult({ action: "finish", ...result }, options.output);
|
|
158
205
|
|
|
159
206
|
if (!result.ok && !clientConfig.nonBlocking) {
|
|
160
207
|
process.exitCode = 1;
|
package/index.js
CHANGED
|
@@ -228,9 +228,11 @@ function buildCiIdempotencyKey() {
|
|
|
228
228
|
|
|
229
229
|
function inferBranchName() {
|
|
230
230
|
return (
|
|
231
|
+
process.env.CHANGE_BRANCH ||
|
|
231
232
|
process.env.GITHUB_REF_NAME ||
|
|
232
233
|
process.env.CI_COMMIT_REF_NAME ||
|
|
233
234
|
process.env.CIRCLE_BRANCH ||
|
|
235
|
+
process.env.GIT_BRANCH ||
|
|
234
236
|
process.env.BRANCH_NAME ||
|
|
235
237
|
"unknown"
|
|
236
238
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seqpulse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "SeqPulse SDK for metrics endpoint instrumentation and HMAC validation",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"index.d.ts",
|
|
19
19
|
"bin/seqpulse.js",
|
|
20
20
|
"README.md",
|
|
21
|
+
"CI_CD_SNIPPETS_MULTI-PLATFORM.md",
|
|
21
22
|
"LICENSE",
|
|
22
23
|
"scripts/smoke.js"
|
|
23
24
|
],
|