@devopsplaybook.io/common-utils 1.0.0-beta.5.9c5ecde → 1.0.0

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.
Files changed (3) hide show
  1. package/AGENTS.md +84 -0
  2. package/README.md +413 -1
  3. package/package.json +1 -1
package/AGENTS.md ADDED
@@ -0,0 +1,84 @@
1
+ # AGENTS.md
2
+
3
+ This file provides context for AI coding agents working on this repository.
4
+
5
+ ## Repository Overview
6
+
7
+ `@devopsplaybook.io/common-utils` is a shared npm library that centralizes utility modules used across devopsplaybook.io server projects. It serves two purposes:
8
+
9
+ 1. **Node.js library** (`src/`): Database access (SQLite via `better-sqlite3`, PostgreSQL via `pg.Pool`), configuration loading, OpenTelemetry context management, and small helpers.
10
+ 2. **Reusable GitHub Actions workflows** (`.github/workflows/reusable-*.yml`): Standardized CI/CD pipelines adopted by all projects in the organization.
11
+
12
+ ## Architecture
13
+
14
+ ```
15
+ index.ts # Barrel re-exports for all modules
16
+ src/
17
+ OTelContext.ts # createOTelContext() factory (tracer/meter/logger singletons)
18
+ ConfigBase.ts # Abstract base class for 3-layer config (env > config.json > defaults)
19
+ SqlDbUtils.ts # SQLite operations (better-sqlite3, synchronous)
20
+ PostgresDbUtils.ts # PostgreSQL operations (pg.Pool, async/Promise)
21
+ DbUtils.ts # Unified facade dispatching to Sql or Postgres
22
+ DbUtilsNoTelemetry.ts # Same DB ops without OTel span overhead
23
+ SystemCommand.ts # Promise wrapper around child_process.exec
24
+ Timeout.ts # Promise wrapper around setTimeout
25
+ *.spec.ts # Co-located test files
26
+ .github/workflows/
27
+ main-build.yml # Caller: push to main -> reusable-npm-merge
28
+ pr-check.yml # Caller: PR to main -> reusable-npm-pr
29
+ npm-upgrade.yml # Caller: weekly schedule -> reusable-npm-upgrade
30
+ reusable-npm-merge.yml # Lint + test + build + publish release to npm
31
+ reusable-npm-pr.yml # Lint + test + build + publish beta tag + comment PR
32
+ reusable-npm-upgrade.yml # npm-check-updates + auto PR
33
+ reusable-pr-verify.yml # Matrix Node.js + multi-platform Docker build for PRs
34
+ reusable-merge-build.yml # Matrix Node.js + Docker build with version tags on merge
35
+ ```
36
+
37
+ ## Key Conventions
38
+
39
+ - **TypeScript**: Target ES2019, CommonJS output, strict mode enabled, declarations generated.
40
+ - **ESLint**: Uses `typescript-eslint` with `strict` and `stylistic` rule sets. Minimize `eslint-disable` comments; use them only when the type system cannot express the constraint (e.g., `@typescript-eslint/no-explicit-any` for `this as any` dynamic field access in `ConfigBase`).
41
+ - **Tests**: Jest with `ts-jest`. Spec files live next to source (`*.spec.ts`). Run with `npm run test`. The `tsconfig.spec.json` includes jest types.
42
+ - **No default exports**: All modules use named exports only.
43
+ - **OTel dependency injection**: Every DB module exposes a `*SetOTel(tracer, logger)` function that must be called before `*Init()`. OTel instances are stored as module-level singletons.
44
+ - **ModuleLogger pattern**: `StandardLogger` only exposes `createModuleLogger(name)`. DB modules call `logger.createModuleLogger("ModuleName")` internally. Never call `.info()` or `.error()` directly on a `StandardLogger`.
45
+ - **SQLite-first SQL**: Write SQL with `?` placeholders. The `DbUtils` facade and `DbUtilsNoTelemetry` module auto-convert to `$1, $2, ...` for Postgres via `convertToPostgresPlaceholders()`.
46
+ - **Migration convention**: SQL files named `init-NNNN.sql`. `init-0000.sql` must create the `metadata` table. Subsequent files are applied in lexicographic order; applied versions are tracked in `metadata` for idempotency.
47
+
48
+ ## Build and Verification
49
+
50
+ ```bash
51
+ npm install
52
+ npm run build # tsc -> dist/
53
+ npm run lint # eslint src (must pass with 0 errors)
54
+ npm run test # jest --coverage (all tests must pass)
55
+ ```
56
+
57
+ All three commands must pass before committing. The CI pipeline (`reusable-npm-merge.yml`) runs the same checks.
58
+
59
+ ## Dependencies
60
+
61
+ | Package | Role |
62
+ | ------------------------------- | ------------------------------------------------------------------------------------------ |
63
+ | `@devopsplaybook.io/otel-utils` | `StandardTracer`, `StandardLogger`, `StandardMeter`, `ModuleLogger`, `ConfigOTelInterface` |
64
+ | `better-sqlite3` | Synchronous SQLite driver (NOT the callback-based `sqlite3`) |
65
+ | `pg` | PostgreSQL client with connection pooling |
66
+ | `uuid` | v14+ (ESM -- requires `jest.mock("uuid")` in tests) |
67
+ | `fs-extra` | Async/sync file operations, `readJson`/`ensureDir` |
68
+
69
+ ## Known Gotchas
70
+
71
+ - **uuid ESM**: `uuid` v14+ ships ESM. In any test that transitively imports `uuid`, add `jest.mock("uuid", () => ({ v4: () => "mock-uuid-1234" }))` to avoid `SyntaxError: Unexpected token 'export'`.
72
+ - **better-sqlite3 is synchronous**: `SqlDbUtils` functions return values directly (not Promises). `PostgresDbUtils` functions return Promises. The `DbUtils` facade returns `number | Promise<number>` depending on the active backend.
73
+ - **eslint-disable placement**: `eslint-disable-next-line` applies to the **immediately following line only**. When disabling a rule inside a function call argument, place the comment directly before the offending expression, not before the function call.
74
+ - **pg callback typing**: Always explicitly type pg callback parameters: `(error: Error | null, result: { rowCount: number | null })`. TypeScript cannot infer these from the overloaded `pool.query` signature.
75
+
76
+ ## Adopting in Other Projects
77
+
78
+ See the [README](./README.md) for full adoption instructions. The typical pattern:
79
+
80
+ 1. Add `@devopsplaybook.io/common-utils` as a dependency.
81
+ 2. Replace the project's `OTelContext.ts` with `createOTelContext()`.
82
+ 3. Replace the project's `Config` class to `extend ConfigBase`.
83
+ 4. Replace DB utility files with re-exports from `common-utils`.
84
+ 5. Update `App.ts` to call `*SetOTel()` and `*Init()` from `common-utils`.
package/README.md CHANGED
@@ -1 +1,413 @@
1
- # common-utils
1
+ # @devopsplaybook.io/common-utils
2
+
3
+ Shared utility modules for [devopsplaybook.io](https://github.com/devopsplaybook-io) projects. Provides OpenTelemetry-aware database access (SQLite and PostgreSQL), configuration loading, telemetry context management, and reusable GitHub Actions CI/CD workflows.
4
+
5
+ ## Contents
6
+
7
+ - [Node.js Library](#nodejs-library)
8
+ - [Installation](#installation)
9
+ - [Modules](#modules)
10
+ - [Quick Start](#quick-start)
11
+ - [Shared GitHub Actions Workflows](#shared-github-actions-workflows)
12
+ - [Reusable Workflows](#reusable-workflows)
13
+ - [Adopting in Your Project](#adopting-in-your-project)
14
+
15
+ ---
16
+
17
+ ## Node.js Library
18
+
19
+ ### Installation
20
+
21
+ ```bash
22
+ npm install @devopsplaybook.io/common-utils
23
+ ```
24
+
25
+ **Peer dependencies** (installed automatically):
26
+
27
+ | Package | Purpose |
28
+ | ------------------------------- | --------------------------------------------------- |
29
+ | `@devopsplaybook.io/otel-utils` | `StandardTracer`, `StandardLogger`, `StandardMeter` |
30
+ | `@opentelemetry/api` | OTel API (`SpanStatusCode`) |
31
+ | `@opentelemetry/sdk-trace-base` | `Span` type |
32
+ | `better-sqlite3` | Synchronous SQLite driver |
33
+ | `pg` | PostgreSQL client (`Pool`) |
34
+ | `fs-extra` | File system helpers |
35
+ | `uuid` | UUID generation for JWT keys |
36
+
37
+ ### Modules
38
+
39
+ #### `OTelContext` -- Telemetry Singleton Factory
40
+
41
+ Creates an isolated set of OTel singletons (tracer, meter, logger) for a server process.
42
+
43
+ ```ts
44
+ import { createOTelContext } from "@devopsplaybook.io/common-utils";
45
+ import { StandardTracer, StandardMeter } from "@devopsplaybook.io/otel-utils";
46
+
47
+ const otel = createOTelContext();
48
+ otel.OTelSetTracer(new StandardTracer(config));
49
+ otel.OTelSetMeter(new StandardMeter(config));
50
+ otel.OTelLogger().initOTel(config);
51
+
52
+ // Later:
53
+ const tracer = otel.OTelTracer();
54
+ const span = tracer.startSpan("my-operation");
55
+ ```
56
+
57
+ | Export | Description |
58
+ | --------------------- | --------------------------------------------------------------------------------------------- |
59
+ | `createOTelContext()` | Returns `{ OTelTracer, OTelSetTracer, OTelMeter, OTelSetMeter, OTelLogger, OTelRequestSpan }` |
60
+
61
+ ---
62
+
63
+ #### `ConfigBase` -- Configuration Base Class
64
+
65
+ Abstract class implementing the three-layer override strategy:
66
+
67
+ 1. **Environment variable** (highest priority)
68
+ 2. **config.json** file value
69
+ 3. **Default** declared on the class property
70
+
71
+ ```ts
72
+ import { ConfigBase } from "@devopsplaybook.io/common-utils";
73
+
74
+ class MyConfig extends ConfigBase {
75
+ public MY_SETTING = "default";
76
+
77
+ constructor() {
78
+ super("my-service");
79
+ this.addConfigField({ field: "MY_SETTING" });
80
+ }
81
+
82
+ async reload(): Promise<void> {
83
+ await super.reload((msg) => console.log(msg));
84
+ }
85
+ }
86
+
87
+ const config = new MyConfig();
88
+ await config.reload();
89
+ ```
90
+
91
+ **Built-in fields** (pre-registered, no `addConfigField` needed):
92
+
93
+ | Field | Default | Sensitive |
94
+ | -------------------------------------- | -------------------- | ----------------------------------- |
95
+ | `API_PORT` | `8080` | No |
96
+ | `JWT_VALIDITY_DURATION` | `8035200` (3 months) | No |
97
+ | `CORS_POLICY_ORIGIN` | `""` | No |
98
+ | `DATA_DIR` | `/data` | No |
99
+ | `JWT_KEY` | `uuidv4()` | Yes |
100
+ | `LOG_LEVEL` | `"info"` | No |
101
+ | `DATABASE_TYPE` | `"sqlite"` | No |
102
+ | `DATABASE_POSTGRES_HOST` | `""` | No |
103
+ | `DATABASE_POSTGRES_PORT` | `5432` | No |
104
+ | `DATABASE_POSTGRES_USER` | `""` | No |
105
+ | `DATABASE_POSTGRES_PASSWORD` | `""` | Yes |
106
+ | `DATABASE_POSTGRES_DATABASE` | `""` | No |
107
+ | All `OPENTELEMETRY_COLLECTOR_*` fields | Various | No (except `_AUTHORIZATION_HEADER`) |
108
+
109
+ ---
110
+
111
+ #### `SqlDbUtils` -- SQLite Database Access
112
+
113
+ Synchronous database operations using `better-sqlite3`, with OTel tracing on every call.
114
+
115
+ ```ts
116
+ import {
117
+ SqlDbUtilsSetOTel,
118
+ SqlDbUtilsInit,
119
+ SqlDbUtilsExecSQL,
120
+ SqlDbUtilsQuerySQL,
121
+ } from "@devopsplaybook.io/common-utils";
122
+
123
+ // At startup
124
+ SqlDbUtilsSetOTel(tracer, logger);
125
+ await SqlDbUtilsInit(span, config, path.resolve(__dirname, "../sql"));
126
+
127
+ // Read
128
+ const rows = SqlDbUtilsQuerySQL(span, "SELECT * FROM users WHERE id = ?", [
129
+ userId,
130
+ ]);
131
+
132
+ // Write
133
+ const changes = SqlDbUtilsExecSQL(
134
+ span,
135
+ "UPDATE users SET name = ? WHERE id = ?",
136
+ [name, userId],
137
+ );
138
+ ```
139
+
140
+ | Export | Signature | Description |
141
+ | ----------------------- | ------------------------------ | ------------------------------------------------ |
142
+ | `SqlDbUtilsSetOTel` | `(tracer, logger)` | Inject OTel instances |
143
+ | `SqlDbUtilsInit` | `(span, config, sqlDir)` | Open DB and run migrations |
144
+ | `SqlDbUtilsExecSQL` | `(span, sql, params?)` | Execute write, returns `number` (changes) |
145
+ | `SqlDbUtilsQuerySQL` | `(span, sql, params?, debug?)` | Execute read, returns `any[]` |
146
+ | `SqlDbUtilsExecSQLFile` | `(span, filename)` | Execute an entire SQL file |
147
+ | `SqlDbUtilsGetDatabase` | `()` | Returns the `better-sqlite3` `Database` instance |
148
+
149
+ **Migration convention**: Files named `init-NNNN.sql` in `sqlDir`, applied in order. A `metadata` table tracks applied versions for idempotent re-runs. `init-0000.sql` must exist (creates the `metadata` table).
150
+
151
+ ---
152
+
153
+ #### `PostgresDbUtils` -- PostgreSQL Database Access
154
+
155
+ Async (Promise-based) database operations using `pg.Pool`, with OTel tracing.
156
+
157
+ | Export | Signature | Description |
158
+ | ---------------------------------- | ------------------------------ | ---------------------------------------- |
159
+ | `PostgresDbUtilsSetOTel` | `(tracer, logger)` | Inject OTel instances |
160
+ | `PostgresDbUtilsInit` | `(span, config, sqlDir)` | Create pool and run migrations |
161
+ | `PostgresDbUtilsExecSQL` | `(span, sql, params?)` | Execute write, returns `Promise<number>` |
162
+ | `PostgresDbUtilsQuerySQL` | `(span, sql, params?, debug?)` | Execute read, returns `Promise<any[]>` |
163
+ | `PostgresDbUtilsExecSQLFile` | `(span, filename)` | Execute an entire SQL file |
164
+ | `PostgresDbUtilsGetPool` | `()` | Returns the `pg.Pool` instance |
165
+ | `PostgresDbUtilsTransactionStart` | `(span)` | Begin a transaction (`BEGIN`) |
166
+ | `PostgresDbUtilsTransactionCommit` | `(span)` | Commit a transaction (`COMMIT`) |
167
+
168
+ Pool defaults: `max: 20`, `idleTimeoutMillis: 30000`, `connectionTimeoutMillis: 10000`.
169
+
170
+ ---
171
+
172
+ #### `DbUtils` -- Unified Database Facade
173
+
174
+ Dispatches to SQLite or Postgres based on `config.DATABASE_TYPE`. Write SQL using SQLite-style `?` placeholders; they are automatically converted to `$1, $2, ...` for Postgres.
175
+
176
+ ```ts
177
+ import {
178
+ DbUtilsSetOTel,
179
+ DbUtilsInit,
180
+ DbUtilsExecSQL,
181
+ DbUtilsQuerySQL,
182
+ } from "@devopsplaybook.io/common-utils";
183
+
184
+ DbUtilsSetOTel(tracer, logger);
185
+ await DbUtilsInit(span, config, sqlDir);
186
+
187
+ // Works with both SQLite and Postgres -- placeholders auto-converted
188
+ const rows = DbUtilsQuerySQL(span, "SELECT * FROM users WHERE id = ?", [
189
+ userId,
190
+ ]);
191
+ ```
192
+
193
+ | Export | Description |
194
+ | --------------------------------------------- | -------------------------------------------- |
195
+ | `DbUtilsSetOTel(tracer, logger)` | Set OTel on both backends |
196
+ | `DbUtilsInit(span, config, sqlDir)` | Init the active backend |
197
+ | `DbUtilsExecSQL(span, sql, params?)` | Write with auto-conversion |
198
+ | `DbUtilsQuerySQL(span, sql, params?, debug?)` | Read with auto-conversion |
199
+ | `DbUtilsGetDatabase()` | Returns native handle (`Database` or `Pool`) |
200
+ | `DbUtilsGetType()` | Returns `"sqlite"` or `"postgres"` |
201
+ | `convertToPostgresPlaceholders(sql)` | Converts `?` to `$1, $2, ...` |
202
+
203
+ ---
204
+
205
+ #### `DbUtilsNoTelemetry` -- High-Throughput Path
206
+
207
+ Same SQL operations but **without** creating OTel spans. Use on hot paths where span overhead matters.
208
+
209
+ ```ts
210
+ import {
211
+ DbUtilsNoTelemetrySetLogger,
212
+ DbUtilsNoTelemetryExecSQL,
213
+ DbUtilsNoTelemetryBatchInsert,
214
+ } from "@devopsplaybook.io/common-utils";
215
+
216
+ DbUtilsNoTelemetrySetLogger(logger);
217
+
218
+ DbUtilsNoTelemetryExecSQL("INSERT INTO log (msg) VALUES (?)", [message]);
219
+
220
+ // Batch insert: builds multi-row INSERT
221
+ DbUtilsNoTelemetryBatchInsert(
222
+ "INTO prices (token, price, ts)", // table + columns
223
+ 3, // number of columns
224
+ [
225
+ ["BTC", 65000, "2024-01-01"],
226
+ ["ETH", 3200, "2024-01-01"],
227
+ ], // rows
228
+ );
229
+ ```
230
+
231
+ | Export | Description |
232
+ | --------------------------------------------------------- | --------------------------------- |
233
+ | `DbUtilsNoTelemetrySetLogger(logger)` | Inject logger for error reporting |
234
+ | `DbUtilsNoTelemetryExecSQL(sql, params?)` | Write without spans |
235
+ | `DbUtilsNoTelemetryQuerySQL(sql, params?, debug?)` | Read without spans |
236
+ | `DbUtilsNoTelemetryBatchInsert(tableCols, numCols, rows)` | Optimized multi-row INSERT |
237
+
238
+ ---
239
+
240
+ #### `SystemCommand` -- Shell Command Execution
241
+
242
+ ```ts
243
+ import { SystemCommandExecute } from "@devopsplaybook.io/common-utils";
244
+
245
+ const output = await SystemCommandExecute("ls -la /tmp", { cwd: "/home" });
246
+ ```
247
+
248
+ #### `Timeout` -- Promise-based Delay
249
+
250
+ ```ts
251
+ import { TimeoutWait } from "@devopsplaybook.io/common-utils";
252
+
253
+ await TimeoutWait(5000); // wait 5 seconds
254
+ ```
255
+
256
+ ### Quick Start
257
+
258
+ ```ts
259
+ import {
260
+ createOTelContext,
261
+ ConfigBase,
262
+ DbUtilsSetOTel,
263
+ DbUtilsInit,
264
+ } from "@devopsplaybook.io/common-utils";
265
+ import { StandardTracer, StandardMeter } from "@devopsplaybook.io/otel-utils";
266
+
267
+ // 1. Config
268
+ class AppConfig extends ConfigBase {
269
+ constructor() {
270
+ super("my-app");
271
+ }
272
+ async reload() {
273
+ await super.reload((m) => console.log(m));
274
+ }
275
+ }
276
+ const config = new AppConfig();
277
+ await config.reload();
278
+
279
+ // 2. OTel
280
+ const otel = createOTelContext();
281
+ otel.OTelSetTracer(new StandardTracer(config));
282
+ otel.OTelSetMeter(new StandardMeter(config));
283
+ otel.OTelLogger().initOTel(config);
284
+
285
+ // 3. Database
286
+ DbUtilsSetOTel(otel.OTelTracer(), otel.OTelLogger());
287
+ const span = otel.OTelTracer().startSpan("init");
288
+ await DbUtilsInit(span, config, path.resolve(__dirname, "../sql"));
289
+ span.end();
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Shared GitHub Actions Workflows
295
+
296
+ The `.github/workflows/` directory contains **reusable workflows** that other repositories can call to standardize their CI/CD pipelines.
297
+
298
+ ### Reusable Workflows
299
+
300
+ | Workflow | File | Trigger | Purpose |
301
+ | --------------- | -------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
302
+ | **NPM Merge** | `reusable-npm-merge.yml` | `workflow_call` | Lint, test, build, and publish a release to npm on merge to main. Uploads coverage to Quality Dashboard. Only publishes if the version doesn't already exist. |
303
+ | **NPM PR** | `reusable-npm-pr.yml` | `workflow_call` | Lint, test, and build on PR. Publishes a **beta** version tagged `beta` and comments the PR with install instructions. |
304
+ | **NPM Upgrade** | `reusable-npm-upgrade.yml` | `workflow_call` | Runs `npm-check-updates -u`, bumps the patch version, and opens a PR. Supports monorepo sub-folders via `npm_services` input. |
305
+ | **PR Verify** | `reusable-pr-verify.yml` | `workflow_call` | Matrix build/lint/test for multiple Node.js apps plus multi-platform Docker build. For monorepos with Docker images. |
306
+ | **Merge Build** | `reusable-merge-build.yml` | `workflow_call` | Same as PR Verify but on merge. Tags Docker images with `latest`, version, major, and minor tags. |
307
+
308
+ ### Inputs and Secrets
309
+
310
+ #### NPM Workflows (`reusable-npm-merge`, `reusable-npm-pr`)
311
+
312
+ | Input | Required | Default | Description |
313
+ | ------------------ | -------- | ------- | --------------------------------------------------------- |
314
+ | `node_version` | No | `"18"` | Node.js version |
315
+ | `npm_package_name` | Yes | -- | npm package name (e.g. `@devopsplaybook.io/common-utils`) |
316
+
317
+ | Secret | Required | Description |
318
+ | ------------------------- | -------- | ----------------------------------------- |
319
+ | `NPM_TOKEN` | Yes | npm publish token |
320
+ | `QUALITY_DASHBOARD_URL` | No | Quality Dashboard URL for coverage upload |
321
+ | `QUALITY_DASHBOARD_TOKEN` | No | Quality Dashboard upload token |
322
+
323
+ #### NPM Upgrade (`reusable-npm-upgrade`)
324
+
325
+ | Input | Required | Default | Description |
326
+ | -------------- | -------- | --------------------------------------- | ------------------------------------------------------------- |
327
+ | `npm_services` | Yes | -- | JSON array of sub-folder paths (e.g. `'["server","client"]'`) |
328
+ | `pr_branch` | No | `feature/YYYY.MM.DD-dependency-updates` | Branch name for the PR |
329
+
330
+ #### Docker/Node Workflows (`reusable-pr-verify`, `reusable-merge-build`)
331
+
332
+ | Input | Required | Default | Description |
333
+ | ---------------------- | -------- | ---------------------------- | ------------------------------------- |
334
+ | `docker_platforms` | No | `linux/arm64/v8,linux/amd64` | Docker build platforms |
335
+ | `node_app_directories` | No | `""` | JSON array of Node.js app directories |
336
+ | `node_version` | No | `"22"` | Node.js version |
337
+
338
+ | Secret | Required | Description |
339
+ | ------------------------- | -------- | ------------------------------ |
340
+ | `DOCKER_HUB_USERNAME` | Yes | Docker Hub username |
341
+ | `DOCKER_HUB_ACCESS_TOKEN` | Yes | Docker Hub access token |
342
+ | `QUALITY_DASHBOARD_URL` | No | Quality Dashboard URL |
343
+ | `QUALITY_DASHBOARD_TOKEN` | No | Quality Dashboard upload token |
344
+
345
+ ### Adopting in Your Project
346
+
347
+ Create a caller workflow in `.github/workflows/main-build.yml`:
348
+
349
+ ```yaml
350
+ name: Main Build
351
+ on:
352
+ push:
353
+ branches: ["main"]
354
+ jobs:
355
+ npm-merge:
356
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-merge.yml@main
357
+ with:
358
+ npm_package_name: "@your-scope/your-package"
359
+ secrets:
360
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
361
+ ```
362
+
363
+ And `.github/workflows/pr-check.yml`:
364
+
365
+ ```yaml
366
+ name: PR Check
367
+ on:
368
+ pull_request:
369
+ branches: ["main"]
370
+ permissions:
371
+ contents: read
372
+ pull-requests: write
373
+ issues: write
374
+ jobs:
375
+ npm-pr:
376
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-pr.yml@main
377
+ with:
378
+ npm_package_name: "@your-scope/your-package"
379
+ secrets:
380
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
381
+ ```
382
+
383
+ And `.github/workflows/npm-upgrade.yml` for weekly dependency updates:
384
+
385
+ ```yaml
386
+ name: NPM Upgrade
387
+ on:
388
+ schedule:
389
+ - cron: "0 6 * * 1" # Monday 6am UTC
390
+ jobs:
391
+ npm-upgrade:
392
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-upgrade.yml@main
393
+ permissions:
394
+ contents: write
395
+ pull-requests: write
396
+ with:
397
+ npm_services: "[]"
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Development
403
+
404
+ ```bash
405
+ npm install
406
+ npm run build # TypeScript compilation -> dist/
407
+ npm run lint # ESLint (strict + stylistic)
408
+ npm run test # Jest with coverage
409
+ ```
410
+
411
+ ## License
412
+
413
+ ISC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devopsplaybook.io/common-utils",
3
- "version": "1.0.0-beta.5.9c5ecde",
3
+ "version": "1.0.0",
4
4
  "description": "Shared utility modules for devopsplaybook.io projects (DB, Config, OTel context, system helpers)",
5
5
  "keywords": [
6
6
  "Open Telemetry",