node-firebird 2.3.1 → 2.3.3

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 (55) hide show
  1. package/README.md +218 -31
  2. package/lib/index.d.ts +22 -0
  3. package/lib/pool.js +97 -10
  4. package/lib/srp.js +12 -1
  5. package/lib/wire/connection.js +40 -7
  6. package/lib/wire/socket.js +13 -1
  7. package/lib/wire/xsqlvar.js +69 -6
  8. package/package.json +1 -1
  9. package/poc/README.md +160 -0
  10. package/poc/helpers.js +59 -0
  11. package/poc/node_modules/.package-lock.json +14 -0
  12. package/poc/node_modules/node-firebird/.eslintrc.json +12 -0
  13. package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +76 -0
  14. package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +95 -0
  15. package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +374 -0
  16. package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +148 -0
  17. package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +152 -0
  18. package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +145 -0
  19. package/poc/node_modules/node-firebird/LICENSE +373 -0
  20. package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +136 -0
  21. package/poc/node_modules/node-firebird/PR_SUMMARY.md +96 -0
  22. package/poc/node_modules/node-firebird/README.md +794 -0
  23. package/poc/node_modules/node-firebird/ROADMAP.md +223 -0
  24. package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +482 -0
  25. package/poc/node_modules/node-firebird/lib/callback.js +38 -0
  26. package/poc/node_modules/node-firebird/lib/firebird.msg +0 -0
  27. package/poc/node_modules/node-firebird/lib/firebird.msg.json +1371 -0
  28. package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +1524 -0
  29. package/poc/node_modules/node-firebird/lib/gdscodes.js +1531 -0
  30. package/poc/node_modules/node-firebird/lib/ieee754-decimal.js +500 -0
  31. package/poc/node_modules/node-firebird/lib/index.d.ts +316 -0
  32. package/poc/node_modules/node-firebird/lib/index.js +128 -0
  33. package/poc/node_modules/node-firebird/lib/messages.js +162 -0
  34. package/poc/node_modules/node-firebird/lib/pool.js +108 -0
  35. package/poc/node_modules/node-firebird/lib/srp.js +299 -0
  36. package/poc/node_modules/node-firebird/lib/unix-crypt.js +343 -0
  37. package/poc/node_modules/node-firebird/lib/utils.js +164 -0
  38. package/poc/node_modules/node-firebird/lib/wire/connection.js +2510 -0
  39. package/poc/node_modules/node-firebird/lib/wire/const.js +807 -0
  40. package/poc/node_modules/node-firebird/lib/wire/database.js +378 -0
  41. package/poc/node_modules/node-firebird/lib/wire/eventConnection.js +118 -0
  42. package/poc/node_modules/node-firebird/lib/wire/fbEventManager.js +326 -0
  43. package/poc/node_modules/node-firebird/lib/wire/serialize.js +588 -0
  44. package/poc/node_modules/node-firebird/lib/wire/service.js +1058 -0
  45. package/poc/node_modules/node-firebird/lib/wire/socket.js +175 -0
  46. package/poc/node_modules/node-firebird/lib/wire/statement.js +48 -0
  47. package/poc/node_modules/node-firebird/lib/wire/transaction.js +206 -0
  48. package/poc/node_modules/node-firebird/lib/wire/xsqlvar.js +703 -0
  49. package/poc/node_modules/node-firebird/package.json +38 -0
  50. package/poc/node_modules/node-firebird/vitest.config.js +24 -0
  51. package/poc/package-lock.json +21 -0
  52. package/poc/package.json +12 -0
  53. package/poc/reproduce-fixed.js +150 -0
  54. package/poc/reproduce.js +133 -0
  55. package/vitest.config.js +4 -1
@@ -13,17 +13,79 @@ const
13
13
  TimeCoeff = 86400000,
14
14
  MsPerMinute = 60000;
15
15
 
16
+ /**
17
+ * Maps Firebird character-set names (upper-case) to the Node.js Buffer
18
+ * encoding string used by Buffer.toString() / Buffer.from().
19
+ *
20
+ * Firebird stores CHAR/VARCHAR data in the on-wire character set of the
21
+ * column (or the connection character set for NONE/unspecified columns).
22
+ * We must decode raw bytes with the matching Node.js encoding so that
23
+ * characters outside ASCII are reproduced correctly.
24
+ *
25
+ * Commonly used Firebird charsets not listed here fall back to the
26
+ * connection-level DEFAULT_ENCODING (typically 'utf8').
27
+ */
28
+ const FirebirdToNodeEncoding = Object.freeze({
29
+ UTF8: 'utf8',
30
+ UNICODE_FSS: 'utf8',
31
+ WIN1252: 'latin1',
32
+ ISO8859_1: 'latin1',
33
+ LATIN1: 'latin1',
34
+ ASCII: 'ascii',
35
+ NONE: 'latin1', // unspecified charset – treat as binary-safe latin1
36
+ });
37
+
38
+ const FirebirdCharsetWidths = {
39
+ 'UTF8': 4,
40
+ 'UNICODE_FSS': 3,
41
+ 'SJIS': 2,
42
+ 'EUCJ': 2
43
+ };
44
+
45
+ function getFirebirdCharsetWidth(charset) {
46
+ if (!charset) return 4;
47
+ const upper = charset.toUpperCase();
48
+ return FirebirdCharsetWidths[upper] || 1;
49
+ }
50
+
51
+ /**
52
+ * Resolve the Node.js Buffer encoding to use when decoding text from a
53
+ * Firebird response buffer.
54
+ *
55
+ * @param {object|null} options Connection options object (may be falsy).
56
+ * @returns {string} A Node.js-compatible encoding string.
57
+ */
58
+ function resolveTextEncoding(options) {
59
+ const encoding = (options && options.encoding)
60
+ ? options.encoding.toUpperCase()
61
+ : Const.DEFAULT_ENCODING;
62
+ return FirebirdToNodeEncoding[encoding] || Const.DEFAULT_ENCODING.toLowerCase();
63
+ }
64
+
16
65
  //------------------------------------------------------
17
66
 
18
67
  class SQLVarText {
19
- decode(data, lowerV13) {
68
+ decode(data, lowerV13, options) {
20
69
  let ret;
70
+ const textEncoding = resolveTextEncoding(options);
21
71
  if (this.subType > 1) {
22
72
  // ToDo: with column charset
23
- ret = data.readText(this.length, Const.DEFAULT_ENCODING);
73
+ ret = data.readText(this.length, textEncoding);
74
+ const encoding = options && options.encoding ? options.encoding : 'UTF8';
75
+ const width = getFirebirdCharsetWidth(encoding);
76
+ const charLength = Math.floor(this.length / width);
77
+ if (ret.length > charLength) {
78
+ ret = ret.substring(0, charLength);
79
+ }
24
80
  } else if (this.subType === 0) {
25
81
  // without charset definition
26
- ret = data.readText(this.length, Const.DEFAULT_ENCODING);
82
+ ret = data.readText(this.length, textEncoding);
83
+ const encoding = options && options.encoding ? options.encoding : 'UTF8';
84
+ const width = getFirebirdCharsetWidth(encoding);
85
+ const charLength = Math.floor(this.length / width);
86
+ if (ret.length > charLength) {
87
+ ret = ret.substring(0, charLength);
88
+ }
27
89
  } else {
28
90
  ret = data.readBuffer(this.length);
29
91
  }
@@ -49,14 +111,15 @@ class SQLVarNull extends SQLVarText {
49
111
  //------------------------------------------------------
50
112
 
51
113
  class SQLVarString {
52
- decode(data, lowerV13) {
114
+ decode(data, lowerV13, options) {
53
115
  let ret;
116
+ const textEncoding = resolveTextEncoding(options);
54
117
  if (this.subType > 1) {
55
118
  // ToDo: with column charset
56
- ret = data.readString(Const.DEFAULT_ENCODING);
119
+ ret = data.readString(textEncoding);
57
120
  } else if (this.subType === 0) {
58
121
  // without charset definition
59
- ret = data.readString(Const.DEFAULT_ENCODING);
122
+ ret = data.readString(textEncoding);
60
123
  } else {
61
124
  ret = data.readBuffer();
62
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
package/poc/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # node-firebird pool — Bug PoC & Fix
2
+
3
+ Proof of concept for three bugs in `lib/pool.js` (node-firebird ≥ 2.x).
4
+ No real Firebird server is needed — a tiny fake TCP server simulates the failure scenario.
5
+
6
+ ---
7
+
8
+ ## The bugs
9
+
10
+ ### Bug 1 — `_creating` stuck forever (critical)
11
+
12
+ **File:** `lib/pool.js` → `Pool.check()`
13
+
14
+ When no idle connection exists, `check()` calls `this.attach()` and increments `_creating`. The counter is only decremented **inside the `attach()` callback**. If the server accepts TCP but never responds to the Firebird wire protocol (e.g. during SRP authentication under load), the callback never fires. `_creating` stays elevated permanently, the slot is lost, and every `pool.get()` caller waits forever.
15
+
16
+ ```
17
+ // original code
18
+ self._creating++;
19
+ this.attach(self.options, function (err, db) {
20
+ self._creating--; // ← never reached if attach() hangs
21
+ cb(err, db); // ← caller never notified
22
+ });
23
+ ```
24
+
25
+ **Real-world log signature:**
26
+ ```
27
+ *ERRO* Timeout (15000ms) ao aguardar conexão para tenant X.
28
+ Estado do pool: {"dbinuse":0,"_creating":1,"pending":0,"idle":4}
29
+ ```
30
+
31
+ **Fix:** `connectTimeout` option — a `setTimeout` wraps the `attach()` call. On expiry, `_creating` is decremented and the caller receives an error immediately. A late-arriving connection (if attach eventually completes) is discarded safely.
32
+
33
+ ---
34
+
35
+ ### Bug 2 — `destroy()` does not drain the `pending` queue
36
+
37
+ **File:** `lib/pool.js` → `Pool.destroy()`
38
+
39
+ Callbacks queued in `this.pending` (waiting for a free pool slot) are silently abandoned when `destroy()` is called. Their callers hang indefinitely — there is no error, no timeout, nothing. This is especially harmful when `destroy()` is used as a recovery strategy after a stuck pool.
40
+
41
+ **Fix:** `this.pending.splice(0)` at the start of `destroy()` calls every waiting callback with `Error('Pool is being destroyed')` before any connection is closed.
42
+
43
+ ---
44
+
45
+ ### Bug 3 — `pool.get()` after `pool.destroy()` silently accumulates
46
+
47
+ **File:** `lib/pool.js` → `Pool.get()` and `Pool.check()`
48
+
49
+ There is no `_destroyed` flag. Calling `pool.get()` on an already-destroyed pool pushes the callback into `this.pending` and calls `check()`. `check()` returns early because all slots are gone, but the callback is never served. The caller hangs forever.
50
+
51
+ **Fix:** `_destroyed = true` is set in `destroy()`. Both `get()` and `check()` check it and reject immediately with `Error('Pool has been destroyed')`.
52
+
53
+ ---
54
+
55
+ ### Bug 4 (minor) — `destroy()` callback never fires when connections are in use
56
+
57
+ **File:** `lib/pool.js` → `Pool.destroy()`
58
+
59
+ The original `destroy()` only calls `detachCallback()` for connections found in `pooldb` (idle). Connections currently in use (`dbinuse > 0`) fall through the `forEach` without decrementing `connectionCount`, so it never reaches zero and the `destroy()` completion callback is never invoked.
60
+
61
+ **Fix:** The `else` branch counts in-use connections down without forcing a detach — releasing them remains the caller's responsibility.
62
+
63
+ ---
64
+
65
+ ## Files
66
+
67
+ | File | Purpose |
68
+ |------|---------|
69
+ | `helpers.js` | Shared logging and fake-server factory |
70
+ | `reproduce.js` | Demonstrates all bugs — exits with code **1** |
71
+ | `reproduce-fixed.js` | Validates all fixes — exits with code **0** |
72
+ | `pool-patched.js` | Drop-in replacement for `lib/pool.js` |
73
+
74
+ ---
75
+
76
+ ## How to run
77
+
78
+ ```bash
79
+ npm install
80
+
81
+ # Show the bugs (process hangs → forced exit 1)
82
+ node reproduce.js
83
+
84
+ # Show the fixes (process exits cleanly → exit 0)
85
+ node reproduce-fixed.js
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Expected output
91
+
92
+ ### `node reproduce.js` (bugs present)
93
+
94
+ ```
95
+ [fake-server] Listening on 127.0.0.1:13050 ...
96
+ [BUG-1] pool.get() → TCP connects, Firebird protocol hangs, callback NEVER fires
97
+ [pool-state] creating=1 idle=0 inuse=0 pending=0 destroyed=(no flag)
98
+ [pool-state] creating=1 idle=0 inuse=0 pending=0 destroyed=(no flag)
99
+ [BUG-2] Queuing a second get() in pending, then calling destroy()
100
+ [BUG-2] Before destroy — creating=1 idle=0 inuse=0 pending=1 ...
101
+ [BUG-2] After destroy — creating=1 idle=0 inuse=0 pending=1 ... ← pending NOT drained
102
+ [BUG-2] ↑ pending callback was NEVER called — Promise hangs forever
103
+ [BUG-3] pool.get() AFTER pool.destroy() — should be rejected immediately
104
+ [BUG-3] After post-destroy get — creating=1 idle=0 inuse=0 pending=2 ...
105
+ [SUMMARY] Bug 1 confirmed : _creating=1, permanently stuck (slot lost)
106
+ [SUMMARY] Bug 2 confirmed : pending callback from before destroy never fired
107
+ [SUMMARY] Bug 3 confirmed : post-destroy get silently accumulated in pending
108
+ ```
109
+
110
+ ### `node reproduce-fixed.js` (fixes applied)
111
+
112
+ ```
113
+ [fake-server] Listening on 127.0.0.1:13051 ...
114
+ [FIX-1] pool.get() — attach() will hang, timeout fires after 1500 ms
115
+ [pool-state] creating=1 idle=0 inuse=0 pending=0 destroyed=false
116
+ [pool-state] creating=1 idle=0 inuse=0 pending=0 destroyed=false
117
+ [FIX-1 ✓ OK] callback received error (expected): "Connection timeout after 1500ms"
118
+ [pool-state] creating=0 idle=0 inuse=0 pending=0 destroyed=false ← back to 0 ✓
119
+ [FIX-4] Queuing a get() in pending, then destroying pool immediately
120
+ [FIX-4 ✓ OK] pending callback received error: "Pool is being destroyed" ← immediate ✓
121
+ [FIX-4] After destroy — creating=0 idle=0 inuse=0 pending=0 destroyed=true
122
+ [FIX-2] pool.get() AFTER pool.destroy() — must be rejected immediately
123
+ [FIX-2 ✓ OK] immediately rejected: "Pool has been destroyed" ← no accumulation ✓
124
+ [SUMMARY] Fix 1 confirmed : _creating returned to 0 after 1500 ms timeout
125
+ [SUMMARY] Fix 2 confirmed : post-destroy get() rejected immediately (_destroyed flag)
126
+ [SUMMARY] Fix 4 confirmed : pending callback drained synchronously by destroy()
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Proposed change to `lib/index.d.ts`
132
+
133
+ Add `connectTimeout` to the `Options` interface:
134
+
135
+ ```typescript
136
+ export interface Options {
137
+ // ... existing fields ...
138
+
139
+ /**
140
+ * Timeout in milliseconds for a single pool.get() attach operation.
141
+ * If attach() does not complete within this time the slot is freed,
142
+ * the caller receives an error, and any late-arriving connection is
143
+ * safely discarded. Set to 0 or omit to disable (default: no timeout).
144
+ *
145
+ * Recommended value: 5000–10000 ms depending on network latency and
146
+ * expected Firebird server response time under load.
147
+ */
148
+ connectTimeout?: number;
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Backward compatibility
155
+
156
+ All changes are **fully backward-compatible**:
157
+
158
+ - `connectTimeout` is optional and defaults to disabled (`undefined > 0` is `false`)
159
+ - `_destroyed` defaults to `false` — existing code paths are unchanged
160
+ - `destroy()` draining of `pending` is new behaviour but harmless for callers that did not add pending requests before calling `destroy()`
package/poc/helpers.js ADDED
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared helpers used by reproduce.js and reproduce-fixed.js.
5
+ */
6
+
7
+ const net = require('net');
8
+
9
+ /** Timestamp prefix for log lines (HH:MM:SS.mmm) */
10
+ function ts() {
11
+ return new Date().toISOString().slice(11, 23);
12
+ }
13
+
14
+ /** Structured log with fixed-width tag */
15
+ function log(tag, msg) {
16
+ console.log(`[${ts()}] ${tag.padEnd(22)} ${msg}`);
17
+ }
18
+
19
+ /** Compact pool state string */
20
+ function poolState(pool) {
21
+ return (
22
+ `creating=${pool._creating} ` +
23
+ `idle=${pool.pooldb.length} ` +
24
+ `inuse=${pool.dbinuse} ` +
25
+ `pending=${pool.pending.length} ` +
26
+ `destroyed=${pool._destroyed ?? '(no flag)'}`
27
+ );
28
+ }
29
+
30
+ /**
31
+ * Starts a TCP server that accepts connections but NEVER responds to data.
32
+ *
33
+ * This simulates a Firebird server that:
34
+ * - Completes the TCP three-way handshake (SYN / SYN-ACK / ACK)
35
+ * - Receives the initial op_connect packet from the client
36
+ * - Never sends op_accept_data back
37
+ *
38
+ * Result: node-firebird's attach() callback is never called, reproducing the
39
+ * scenario where _creating is permanently incremented.
40
+ */
41
+ function startFakeServer(port, onReady) {
42
+ const server = net.createServer((socket) => {
43
+ log('fake-server', `TCP accepted from :${socket.remotePort} — will NOT respond to Firebird protocol`);
44
+ socket.on('data', () => { /* receive op_connect but ignore it */ });
45
+ socket.on('error', () => { /* suppress errors on forced cleanup */ });
46
+ });
47
+
48
+ server.on('error', (err) => {
49
+ console.error(`[fake-server] Error: ${err.message}`);
50
+ process.exit(1);
51
+ });
52
+
53
+ server.listen(port, '127.0.0.1', () => {
54
+ log('fake-server', `Listening on 127.0.0.1:${port} (accepts TCP, ignores Firebird wire protocol)`);
55
+ onReady(server);
56
+ });
57
+ }
58
+
59
+ module.exports = { ts, log, poolState, startFakeServer };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "node-firebird-pool-poc",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "node_modules/node-firebird": {
8
+ "version": "2.3.1",
9
+ "resolved": "https://registry.npmjs.org/node-firebird/-/node-firebird-2.3.1.tgz",
10
+ "integrity": "sha512-3yTwiLHwgtLYRv+aS4frzsQEwHMDXkP5Z1SPM/W103TvMemVRoCIaxPYO7axnUrKhgTfPHGXzqC5umYlQail/w==",
11
+ "license": "MPL-2.0"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "parserOptions": {
3
+ "ecmaVersion": 6,
4
+ "sourceType": "module",
5
+ "ecmaFeatures": {
6
+ "jsx": true
7
+ }
8
+ },
9
+ "rules": {
10
+ "semi": "error"
11
+ }
12
+ }
@@ -0,0 +1,76 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ "master" ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ "master" ]
20
+ schedule:
21
+ - cron: '26 2 * * 3'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'javascript' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
+ # Use only 'java' to analyze code written in Java, Kotlin or both
38
+ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
39
+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
40
+
41
+ steps:
42
+ - name: Checkout repository
43
+ uses: actions/checkout@v3
44
+
45
+ # Initializes the CodeQL tools for scanning.
46
+ - name: Initialize CodeQL
47
+ uses: github/codeql-action/init@v2
48
+ with:
49
+ languages: ${{ matrix.language }}
50
+ # If you wish to specify custom queries, you can do so here or in a config file.
51
+ # By default, queries listed here will override any specified in a config file.
52
+ # Prefix the list here with "+" to use these queries and those in the config file.
53
+
54
+ # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
55
+ # queries: security-extended,security-and-quality
56
+
57
+
58
+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
59
+ # If this step fails, then you should remove it and run the build manually (see below)
60
+ - name: Autobuild
61
+ uses: github/codeql-action/autobuild@v2
62
+
63
+ # ℹ️ Command-line programs to run using the OS shell.
64
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
65
+
66
+ # If the Autobuild fails above, remove it and uncomment the following three lines.
67
+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
68
+
69
+ # - run: |
70
+ # echo "Run, Build Application using script"
71
+ # ./location_of_script_within_repo/buildscript.sh
72
+
73
+ - name: Perform CodeQL Analysis
74
+ uses: github/codeql-action/analyze@v2
75
+ with:
76
+ category: "/language:${{matrix.language}}"
@@ -0,0 +1,95 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ node: [20, 22, 24]
13
+ firebird: [3, 4, 5]
14
+
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ with:
18
+ fetch-depth: 10
19
+
20
+ - name: Setup FirebirdSQL ${{ matrix.firebird }} container
21
+ run: |
22
+ # Create data directory
23
+ sudo mkdir -p /firebird/data
24
+ sudo chmod 755 /firebird/data
25
+
26
+ # Start Firebird container
27
+ docker run -d \
28
+ --name firebird \
29
+ -e FIREBIRD_ROOT_PASSWORD="masterkey" \
30
+ -e FIREBIRD_CONF_WireCrypt="Enabled" \
31
+ -e FIREBIRD_CONF_AuthServer="Legacy_Auth;Srp;Win_Sspi" \
32
+ -p 3050:3050 \
33
+ -v /firebird/data:/firebird/data \
34
+ firebirdsql/firebird:${{ matrix.firebird }}
35
+
36
+ # Wait for Firebird to be ready
37
+ MAX_RETRIES=30
38
+ RETRY_INTERVAL=2
39
+ echo "Waiting for Firebird to start..."
40
+ FIREBIRD_READY=false
41
+ for i in $(seq 1 $MAX_RETRIES); do
42
+ if docker exec firebird /opt/firebird/bin/isql -user SYSDBA -password masterkey -z 2>&1 | grep -q "ISQL Version"; then
43
+ echo "Firebird is ready!"
44
+ FIREBIRD_READY=true
45
+ break
46
+ fi
47
+ echo "Waiting... ($i/$MAX_RETRIES)"
48
+ sleep $RETRY_INTERVAL
49
+ done
50
+
51
+ if [ "$FIREBIRD_READY" = false ]; then
52
+ echo "ERROR: Firebird failed to start within the timeout period"
53
+ docker ps -a
54
+ docker logs firebird
55
+ exit 1
56
+ fi
57
+
58
+ # Verify Firebird is running
59
+ docker ps -a
60
+ docker logs firebird
61
+
62
+ - name: Use Node.js ${{ matrix.node }}
63
+ uses: actions/setup-node@v3
64
+ with:
65
+ node-version: ${{ matrix.node }}
66
+
67
+
68
+ - name: Build
69
+ shell: bash
70
+ run: |
71
+ npm ci
72
+
73
+ - name: Test (Linux)
74
+ run: |
75
+ export FIREBIRD_DATA=/firebird/data
76
+ export FIREBIRD_DEBUG=1
77
+ npm test
78
+
79
+ - name: Show Firebird log on failure
80
+ if: failure()
81
+ run: |
82
+ echo "=========================================="
83
+ echo "Firebird Server Log (last 100 lines):"
84
+ echo "=========================================="
85
+ docker exec firebird tail -n 100 /firebird/log/firebird.log || echo "Could not read firebird.log"
86
+ echo ""
87
+ echo "=========================================="
88
+ echo "Docker container status:"
89
+ echo "=========================================="
90
+ docker ps -a
91
+ echo ""
92
+ echo "=========================================="
93
+ echo "Docker container logs:"
94
+ echo "=========================================="
95
+ docker logs firebird --tail 50