Skip to content

Commit 5986725

Browse files
committed
ci: replace Docker with PSFirebird for unified Windows/Linux testing
Use PSFirebird (v1.2.2+) instead of Docker to run Firebird on both Ubuntu and Windows runners. This removes the platform split where Linux used Docker and Windows was untested. Changes: - Install PSFirebird module in each CI job - Use New-FirebirdEnvironment / Start-FirebirdInstance / Stop-FirebirdInstance to manage Firebird lifecycle on both platforms - Initialize SYSDBA via embedded isql (archive-extracted Firebird ships with an empty security database; embedded connections bypass authentication) - Set ISC_USER / ISC_PASSWORD env vars at job level for driver test auth - Seed firebird.log before server start so Service API get_log() tests pass (archive-extracted Firebird never writes startup messages to this file) - Use GetClientLibraryPath() on the FirebirdEnvironment object to pass the correct client library to the test suite on both platforms - Fix LASTEXITCODE false-failure workaround (PSFirebird 1.2.2 fixes the leak in Get-FirebirdEnvironment / New-FirebirdEnvironment) Minor bugs fixed (no separate Issue/PR needed): - Convert client_lib Path to str before assigning to driver_config (Path objects are not accepted by the StringOption config field) - Fix inet:// protocol URL construction for Windows drive-letter paths: _connect_helper was missing the '/' separator before the database path when host is set, producing malformed URLs like inet://host:3050D:\\path - Remove spurious utf8filename=true from test_connect_config database config (caused 'unavailable database' on Windows when attaching to an existing DB) - Fix test_connect_helper expected DSN values to match the corrected URL format
1 parent a2415ef commit 5986725

File tree

4 files changed

+26
-42
lines changed

4 files changed

+26
-42
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030

3131
env:
3232
GITHUB_TOKEN: ${{ github.token }}
33+
ISC_USER: SYSDBA
34+
ISC_PASSWORD: masterkey
3335

3436
steps:
3537
- uses: actions/checkout@v4
@@ -46,49 +48,31 @@ jobs:
4648
run: |
4749
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
4850
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser -ForceBootstrap -ErrorAction Continue
49-
Install-Module -Name PSFirebird -Force -AllowClobber -Scope CurrentUser -Repository PSGallery
51+
Install-Module -Name PSFirebird -MinimumVersion 1.2.2 -Force -AllowClobber -Scope CurrentUser -Repository PSGallery
5052
5153
- name: Create Firebird environment
5254
run: |
5355
$tempPath = if ($IsWindows) { $env:TEMP } else { '/tmp' }
5456
$fbPath = Join-Path $tempPath 'firebird-${{ matrix.firebird-version }}'
55-
New-FirebirdEnvironment -Version '${{ matrix.fb-semver }}' -Path $fbPath | Out-Null
57+
$fbEnv = New-FirebirdEnvironment -Version '${{ matrix.fb-semver }}' -Path $fbPath
58+
Write-Output $fbEnv
5659
Write-Output "FB_PATH=$fbPath" >> $env:GITHUB_ENV
60+
Write-Output "FB_CLIENT_LIB=$($fbEnv.GetClientLibraryPath())" >> $env:GITHUB_ENV
5761
5862
- name: Configure and start Firebird
5963
run: |
6064
# Configure RemoteAuxPort for Firebird events support
6165
$confPath = Join-Path $env:FB_PATH 'firebird.conf'
6266
Write-FirebirdConfiguration -Path $confPath -Configuration @{ RemoteAuxPort = 3051 }
67+
# Initialize SYSDBA via embedded connection (ISC_USER/ISC_PASSWORD env vars are set at job level)
68+
$initDb = New-FirebirdDatabase -Database (Join-Path $env:FB_PATH 'init.fdb') -Environment $env:FB_PATH
69+
"CREATE USER SYSDBA PASSWORD 'masterkey';" | Invoke-FirebirdIsql -Database $initDb -Environment $env:FB_PATH
70+
Remove-FirebirdDatabase -Database $initDb -Force
71+
# Seed firebird.log so get_log() service API calls return non-empty content
72+
# (Firebird does not write startup messages in this mode; tests assert log is non-empty)
73+
"Firebird`tServer started" | Out-File -FilePath (Join-Path $env:FB_PATH 'firebird.log') -Encoding utf8 -NoNewline:$false
6374
# Start Firebird server
64-
Start-FirebirdInstance -Environment $env:FB_PATH | Out-Null
65-
# Wait for the server to be ready
66-
Write-Output "Waiting for Firebird to be ready..."
67-
for ($i = 1; $i -le 30; $i++) {
68-
try {
69-
$tcp = [System.Net.Sockets.TcpClient]::new('localhost', 3050)
70-
$tcp.Close()
71-
Write-Output "Firebird is ready!"
72-
break
73-
} catch {
74-
if ($i -eq 30) { Write-Error "Firebird failed to start"; exit 1 }
75-
Write-Output "Waiting... ($i/30)"
76-
Start-Sleep -Seconds 2
77-
}
78-
}
79-
80-
- name: Find Firebird client library
81-
run: |
82-
if ($IsWindows) {
83-
$clientLib = Join-Path $env:FB_PATH 'fbclient.dll'
84-
} else {
85-
foreach ($name in @('lib/libfbclient.so.2', 'lib/libfbclient.so')) {
86-
$p = Join-Path $env:FB_PATH $name
87-
if (Test-Path $p) { $clientLib = $p; break }
88-
}
89-
}
90-
Write-Output "Found client library: $clientLib"
91-
Write-Output "FB_CLIENT_LIB=$clientLib" >> $env:GITHUB_ENV
75+
Start-FirebirdInstance -Environment $env:FB_PATH | Write-Output
9276
9377
- name: Run tests
9478
run: hatch test -- --host=localhost --port=3050 "--client-lib=$env:FB_CLIENT_LIB" -v

src/firebird/driver/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2180,7 +2180,7 @@ def _connect_helper(dsn: str, host: str, port: str, database: str, protocol: Net
21802180
# Unix absolute path - use double slash so Firebird keeps the leading /
21812181
dsn += f'/{database}' # Results in inet://host//absolute/path
21822182
elif ':' in database: # Windows path (e.g., C:\...)
2183-
dsn += database # Concatenate directly without separator
2183+
dsn += f'/{database}' # Results in inet://host:port/D:\path
21842184
else: # Relative/alias
21852185
dsn += f'/{database}'
21862186
else:

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def pytest_configure(config):
122122
client_lib = Path(client_lib)
123123
if not client_lib.is_file():
124124
pytest.exit(f"Client library '{client_lib}' not found!")
125-
driver_config.fb_client_library.value = client_lib
125+
driver_config.fb_client_library.value = str(client_lib)
126126
#
127127
if host := config.getoption('host'):
128128
_vars_['host'] = host

tests/test_connection.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,19 @@ def test_connect_helper():
8484
dsn = driver.core._connect_helper(None, IP, None, DB_LINUX_PATH, NetProtocol.INET)
8585
assert dsn == f'inet://{IP}/{DB_LINUX_PATH}' # Double slash for absolute path
8686
dsn = driver.core._connect_helper(None, HOST, None, DB_WIN_PATH, NetProtocol.INET)
87-
assert dsn == f'inet://{HOST}{DB_WIN_PATH}'
87+
assert dsn == f'inet://{HOST}/{DB_WIN_PATH}'
8888
# 3. TCP/IP with Port
8989
dsn = driver.core._connect_helper(None, HOST, PORT, DB_ALIAS, NetProtocol.INET)
9090
assert dsn == f'inet://{HOST}:{PORT}/{DB_ALIAS}'
9191
dsn = driver.core._connect_helper(None, IP, PORT, DB_LINUX_PATH, NetProtocol.INET)
9292
assert dsn == f'inet://{IP}:{PORT}/{DB_LINUX_PATH}' # Double slash for absolute path
9393
dsn = driver.core._connect_helper(None, HOST, SVC_NAME, DB_WIN_PATH, NetProtocol.INET)
94-
assert dsn == f'inet://{HOST}:{SVC_NAME}{DB_WIN_PATH}'
94+
assert dsn == f'inet://{HOST}:{SVC_NAME}/{DB_WIN_PATH}'
9595
# 4. Named pipes
9696
dsn = driver.core._connect_helper(None, NPIPE_HOST, None, DB_ALIAS, NetProtocol.WNET)
9797
assert dsn == f'wnet://{NPIPE_HOST}/{DB_ALIAS}'
9898
dsn = driver.core._connect_helper(None, NPIPE_HOST, SVC_NAME, DB_WIN_PATH, NetProtocol.WNET)
99-
assert dsn == f'wnet://{NPIPE_HOST}:{SVC_NAME}{DB_WIN_PATH}'
99+
assert dsn == f'wnet://{NPIPE_HOST}:{SVC_NAME}/{DB_WIN_PATH}'
100100

101101
def test_connect_dsn(dsn, db_file):
102102
with connect(dsn) as con:
@@ -130,7 +130,6 @@ def test_connect_config(fb_vars, db_file, driver_cfg):
130130
[test_db1]
131131
server = server.local
132132
database = {db_file}
133-
utf8filename = true
134133
charset = UTF8
135134
sql_dialect = 3
136135
"""
@@ -163,21 +162,22 @@ def test_connect_config(fb_vars, db_file, driver_cfg):
163162

164163
if host:
165164
# protocols
166-
# For protocol URLs with absolute paths, we need double slash to preserve leading /
167-
# inet://host//absolute/path so Firebird doesn't strip the leading /
165+
# For protocol URLs the path always needs a / separator:
166+
# - Unix: inet://host//absolute/path (double slash to keep the leading /)
167+
# - Windows: inet://host:port/D:\path (single slash before drive letter)
168168
if str(db_file).startswith('/'):
169-
dsn = f'{host}:{port}/{db_file}' # Extra / for absolute paths
169+
proto_path = f'{host}:{port}/{db_file}' # Extra / for Unix absolute paths
170170
else:
171-
dsn = f'{host}:{port}{db_file}'
171+
proto_path = f'{host}:{port}/{db_file}' # Single / for Windows drive-letter paths
172172
cfg = driver_cfg.get_database('test_db1')
173173
cfg.protocol.value = NetProtocol.INET
174174
with connect('test_db1') as con:
175175
assert con._att is not None
176-
assert con.dsn == f'inet://{dsn}'
176+
assert con.dsn == f'inet://{proto_path}'
177177
cfg.protocol.value = NetProtocol.INET4
178178
with connect('test_db1') as con:
179179
assert con._att is not None
180-
assert con.dsn == f'inet4://{dsn}'
180+
assert con.dsn == f'inet4://{proto_path}'
181181

182182
def test_properties(db_connection):
183183
con = db_connection # Use the fixture

0 commit comments

Comments
 (0)