seqpulse 0.4.0 → 0.5.1
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 +13 -8
- package/bin/seqpulse.js +45 -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 `@nassir_gouomba/seqpulse@0.5.0` 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0` 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
|
@@ -8,16 +8,16 @@ SeqPulse Node SDK couvre deux usages:
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install seqpulse
|
|
11
|
+
npm install @nassir_gouomba/seqpulse
|
|
12
12
|
# or
|
|
13
|
-
pnpm add seqpulse
|
|
13
|
+
pnpm add @nassir_gouomba/seqpulse
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Runtime (Express)
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
const express = require("express");
|
|
20
|
-
const seqpulse = require("seqpulse");
|
|
20
|
+
const seqpulse = require("@nassir_gouomba/seqpulse");
|
|
21
21
|
|
|
22
22
|
const app = express();
|
|
23
23
|
|
|
@@ -53,7 +53,7 @@ app.listen(3000, () => {
|
|
|
53
53
|
## CI/CD client (trigger/finish)
|
|
54
54
|
|
|
55
55
|
```js
|
|
56
|
-
const seqpulse = require("seqpulse");
|
|
56
|
+
const seqpulse = require("@nassir_gouomba/seqpulse");
|
|
57
57
|
|
|
58
58
|
const client = seqpulse.createCIClient({
|
|
59
59
|
baseUrl: process.env.SEQPULSE_BASE_URL,
|
|
@@ -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 @nassir_gouomba/seqpulse@0.5.0 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 @nassir_gouomba/seqpulse@0.5.0 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
|
@@ -21,6 +21,7 @@ Common options:
|
|
|
21
21
|
--env <name> (default: prod)
|
|
22
22
|
--non-blocking <true|false> (default: true)
|
|
23
23
|
--blocking (equivalent to --non-blocking false)
|
|
24
|
+
--output <field> (default: json)
|
|
24
25
|
--github-output <path> (writes outputs for GitHub Actions)
|
|
25
26
|
|
|
26
27
|
Trigger options:
|
|
@@ -97,6 +98,42 @@ function appendGithubOutput(filePath, pairs) {
|
|
|
97
98
|
fs.appendFileSync(filePath, `${lines}\n`);
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
function resolveOutputValue(result, output) {
|
|
102
|
+
const mode = String(output || "json").trim();
|
|
103
|
+
if (!mode || mode === "json") {
|
|
104
|
+
return JSON.stringify(result);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const aliases = {
|
|
108
|
+
deployment_id: "deploymentId",
|
|
109
|
+
http_status: "httpStatus",
|
|
110
|
+
};
|
|
111
|
+
const path = (aliases[mode] || mode).split(".");
|
|
112
|
+
|
|
113
|
+
let current = result;
|
|
114
|
+
for (const segment of path) {
|
|
115
|
+
if (current === undefined || current === null) {
|
|
116
|
+
current = "";
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
current = current[segment];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (current === undefined || current === null) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (typeof current === "object") {
|
|
127
|
+
return JSON.stringify(current);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return String(current);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function printResult(result, output) {
|
|
134
|
+
console.log(sanitizeOutput(resolveOutputValue(result, output)));
|
|
135
|
+
}
|
|
136
|
+
|
|
100
137
|
function resolveNonBlocking(options) {
|
|
101
138
|
if (options.blocking !== undefined) return false;
|
|
102
139
|
if (options["non-blocking"] !== undefined) return parseBoolean(options["non-blocking"], true);
|
|
@@ -130,7 +167,7 @@ async function runTrigger(options) {
|
|
|
130
167
|
http_status: result.httpStatus || "",
|
|
131
168
|
});
|
|
132
169
|
|
|
133
|
-
|
|
170
|
+
printResult({ action: "trigger", ...result }, options.output);
|
|
134
171
|
|
|
135
172
|
if (!result.ok && !clientConfig.nonBlocking) {
|
|
136
173
|
process.exitCode = 1;
|
|
@@ -140,7 +177,12 @@ async function runTrigger(options) {
|
|
|
140
177
|
async function runFinish(options) {
|
|
141
178
|
const clientConfig = buildClient(options);
|
|
142
179
|
const deploymentId = options["deployment-id"] || process.env.SEQPULSE_DEPLOYMENT_ID || "";
|
|
143
|
-
const jobStatus =
|
|
180
|
+
const jobStatus =
|
|
181
|
+
options["job-status"] ||
|
|
182
|
+
process.env.JOB_STATUS ||
|
|
183
|
+
process.env.CI_JOB_STATUS ||
|
|
184
|
+
process.env.BUILD_RESULT ||
|
|
185
|
+
"success";
|
|
144
186
|
const resultValue = options.result || seqpulse.inferResultFromPipelineStatus(jobStatus);
|
|
145
187
|
|
|
146
188
|
const client = seqpulse.createCIClient(clientConfig);
|
|
@@ -154,7 +196,7 @@ async function runFinish(options) {
|
|
|
154
196
|
http_status: result.httpStatus || "",
|
|
155
197
|
});
|
|
156
198
|
|
|
157
|
-
|
|
199
|
+
printResult({ action: "finish", ...result }, options.output);
|
|
158
200
|
|
|
159
201
|
if (!result.ok && !clientConfig.nonBlocking) {
|
|
160
202
|
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.1",
|
|
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
|
],
|