Skip to content

build: Docker build fixes for libpq cross compile - #1

Closed
s373nZ wants to merge 296 commits into
arowser:multifrom
s373nZ:7827-libpq-multiarch--fixes
Closed

build: Docker build fixes for libpq cross compile#1
s373nZ wants to merge 296 commits into
arowser:multifrom
s373nZ:7827-libpq-multiarch--fixes

Conversation

@s373nZ

@s373nZ s373nZ commented Dec 9, 2024

Copy link
Copy Markdown

I performed a lot of testing on your great initial changes today and started making fixes that came up for me in the Docker build. Submitting the changes here to your branch, and also opening a duplicate PR in the CLN repository.

  • Rebased against origin/master.
  • Fixed wget.
  • Added Postgres build dependencies libicu-dev, bison and flex.
  • Removed the existing libpq Debian package installs.
  • Defined POSTGRES_CONFIG declaration for each cross compile target.
  • Defined PG_CONFIG once, directly after the libpq compile and before the configure step.
  • Upgraded Debian base image to Bookworm.
  • Upgraded Python to 3.11.

rustyrussell and others added 30 commits November 18, 2024 11:03
These will deadlock once we hook into rpc_command, so avoid them.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Because we initalized plugin->io_rpc_conn *after* calling plugin->init,
send_outreq would do a (harmless, in our case) wakeup on an uninitialized address:

```
==1164079== Conditional jump or move depends on uninitialised value(s)
==1164079==    at 0x1628FC: backend_wake (poll.c:227)
==1164079==    by 0x160B98: io_wake (io.c:384)
==1164079==    by 0x1160A8: ld_rpc_send (libplugin.c:255)
==1164079==    by 0x1187E0: send_outreq (libplugin.c:1099)
==1164079==    by 0x115041: init (xpay.c:1620)
```

Solution is simple: set plugin->io_rpc_conn to NULL, and don't wake it in this case.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Note: won't work with grpc (or probably other tools), since the output
is different.  But good for testing.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Config: option `xpay-handle-pay` can be used to call xpay when pay is used in many cases (but output is different from pay!)
1 hour is what mpay uses, so stick with that for now.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
These are automatically marked "important", in the sense that we won't startup
if they are not working, but this wasn't meant to disallow stopping them.

Changelog-Changed: JSON-RPC: built-in plugins can now be stopped using "plugin stop".
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
As the first user of a persistent layer, this tripped tests which
assumed the datastore would be empty!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is required for VLS which wants to know (and potentially decline) invoices
we're trying to pay.

As a nice side effect, our "check" command for xpay now does much more thorough
checking of arguments.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Changed: offers: bolt12 now enabled by default (finally!)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
… now.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
And about to be deprecated.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Deprecated: Config: `experimental-offers` (it's now the default).
Commit 5318459 broke the build without
SQLite because this code:

	new = tal_fmt(NULL, template,
	              IF_SQLITE3(sqlite3_libversion_number()));

preprocesses into:

	new = tal_fmt(NULL, template,
	              );

which has a syntax error. Fix it by moving the comma into the macro
argument.

Fixes: 5318459
Changelog-None
The old `long_description` was removed and deprecated a while ago
without adding a proper replacement for plugin developers.
The getmanifest JSON that was to be used for that only knows `name` and `usage`.

This PR adds an optional `description` parameter that will be filled
with the methods docstring `__doc__` (if set).

Example:

    @p.method("example")
    def some_method(...)
        """some description"""
        ...

Changelog-Add: optional description paramter to Plugin.Method
We need to wait for *l2* to see the channel in CHANNELD_NORMAL,
otherwise the array here is empty:

```
	chan = only_one([c for c in l1.rpc.listpeerchannels(l2.info['id'])['channels'] if c['state'] == 'CHANNELD_NORMAL'])
        amount = chan['funding']['local_funds_msat']
        assert amount > Millisatoshi(str((1 << 24) - 1) + "sat")
    
        # We should know we can spend that much!
        spendable = chan['spendable_msat']
        assert spendable > Millisatoshi(str((1 << 24) - 1) + "sat")
    
        # So should peer.
>       chan = only_one([c for c in l2.rpc.listpeerchannels(l1.info['id'])['channels'] if c['state'] == 'CHANNELD_NORMAL'])

tests/test_connection.py:3552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

arr = []

    def only_one(arr):
        """Many JSON RPC calls return an array; often we only expect a single entry
        """
>       assert len(arr) == 1
E       AssertionError
```
I can't reproduce this, but CI did (with Elements):

```
[gw3] linux -- Python 3.8.18 /home/runner/.cache/pypoetry/virtualenvs/cln-meta-project-AqJ9wMix-py3.8/bin/python

node_factory = <pyln.testing.utils.NodeFactory object at 0x7fd0e20f57f0>
bitcoind = <pyln.testing.utils.ElementsD object at 0x7fd0e307dbe0>
executor = <concurrent.futures.thread.ThreadPoolExecutor object at 0x7fd0e307da30>

    @pytest.mark.openchannel('v1')
    @pytest.mark.openchannel('v2')
    def test_lightningd_still_loading(node_factory, bitcoind, executor):
        """Test that we recognize we haven't got all blocks from bitcoind"""
    
        mock_release = Event()
    
        # This is slow enough that we're going to notice.
        def mock_getblock(r):
            conf_file = os.path.join(bitcoind.bitcoin_dir, 'bitcoin.conf')
            brpc = RawProxy(btc_conf_file=conf_file)
            if r['params'][0] == slow_blockid:
                mock_release.wait(TIMEOUT)
            return {
                "result": brpc._call(r['method'], *r['params']),
                "error": None,
                "id": r['id']
            }
    
        # Start it, establish channel, get extra funds.
        l1, l2, l3 = node_factory.get_nodes(3, opts=[{'may_reconnect': True,
                                                      'wait_for_bitcoind_sync': False},
                                                     {'may_reconnect': True,
                                                      'wait_for_bitcoind_sync': False},
                                                     {}])
        node_factory.join_nodes([l1, l2])
    
        # Balance l1<->l2 channel
        l1.pay(l2, 10**9 // 2)
    
        l1.stop()
    
        # Now make sure l2 is behind.
        bitcoind.generate_block(2)
        # Make sure l2/l3 are synced
        sync_blockheight(bitcoind, [l2, l3])
    
        # Make it slow grabbing the final block.
        slow_blockid = bitcoind.rpc.getblockhash(bitcoind.rpc.getblockcount())
        l1.daemon.rpcproxy.mock_rpc('getblock', mock_getblock)
    
        l1.start(wait_for_bitcoind_sync=False)
    
        # It will warn about being out-of-sync.
        assert 'warning_bitcoind_sync' not in l1.rpc.getinfo()
        assert 'warning_lightningd_sync' in l1.rpc.getinfo()
    
        # Make sure it's connected to l2 (otherwise we get TEMPORARY_CHANNEL_FAILURE)
        wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'])
    
        # Payments will succced.
        l1.pay(l2, 1000)
>       assert l1.daemon.is_in_log(r"Sending HTLC while still syncing with bitcoin network \(104 vs 105\)")
E       AssertionError: assert None
E        +  where None = <bound method TailableProc.is_in_log of <pyln.testing.utils.LightningD object at 0x7fd0e20f9fa0>>('Sending HTLC while still syncing with bitcoin network \\(104 vs 105\\)')
E        +    where <bound method TailableProc.is_in_log of <pyln.testing.utils.LightningD object at 0x7fd0e20f9fa0>> = <pyln.testing.utils.LightningD object at 0x7fd0e20f9fa0>.is_in_log
E        +      where <pyln.testing.utils.LightningD object at 0x7fd0e20f9fa0> = <fixtures.LightningNode object at 0x7fd0e20f59d0>.daemon
```

What was in logs was:

```
lightningd-1 2024-11-18T05:33:50.634Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Sending HTLC while still syncing with bitcoin network (103 vs 105)
```

Implying that l1 was an extra block behind.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
tests/test_plugin.py::test_important_plugin does this, and it's inelegant:

```
lightningd-1 2024-11-18T07:33:09.433Z **BROKEN** plugin-fail_by_itself.py: Plugin marked as important, shutting down lightningd!
lightningd-1 2024-11-18T07:33:09.451Z DEBUG   lightningd: io_break: lightningd_exit
lightningd-1 2024-11-18T07:33:09.533Z DEBUG   connectd: REPLY WIRE_CONNECTD_START_SHUTDOWN_REPLY with 0 fds
lightningd-1 2024-11-18T07:33:09.575Z DEBUG   lightningd: io_break: connectd_start_shutdown_reply
lightningd-1 2024-11-18T07:33:09.802Z DEBUG   lightningd: Looking for [autoclean,failedforwards,num]
{'github_repository': 'ElementsProject/lightning', 'github_sha': '0729de783e95c5208b1706f7d27b23904596bb71', 'github_ref': 'refs/pull/7835/merge', 'github_ref_name': 'HEAD', 'github_run_id': 11887300979, 'github_head_ref': 'guilt/fix-flakes8', 'github_run_number': 11566, 'github_base_ref': 'master', 'github_run_attempt': '1', 'testname': 'test_important_plugin', 'start_time': 1731915163, 'end_time': 1731915190, 'outcome': 'fail'}
----------------------------- Captured stderr call -----------------------------
No plugin for askrene-create-layer ?
Lost connection to the RPC socket.Reading JSON input: Connection reset by peerReading JSON input: Connection reset by peerReading JSON input: Connection reset by peerReading JSON input: Connection reset by peer
--------------------------- Captured stdout teardown ---------------------------
------------------------------- Valgrind errors --------------------------------
Valgrind error file: valgrind-errors.28639
==28639== Invalid read of size 8
==28639==    at 0x168310: command_exec (jsonrpc.c:808)
==28639==    by 0x168A98: rpc_command_hook_final (jsonrpc.c:954)
==28639==    by 0x1AD48C: plugin_hook_call_next (plugin_hook.c:196)
==28639==    by 0x1AD407: plugin_hook_callback (plugin_hook.c:183)
==28639==    by 0x1A6074: plugin_response_handle (plugin.c:663)
==28639==    by 0x1A62F0: plugin_read_json_one (plugin.c:775)
==28639==    by 0x1A652D: plugin_read_json (plugin.c:826)
==28639==    by 0x390200: next_plan (io.c:60)
==28639==    by 0x390E56: do_plan (io.c:422)
==28639==    by 0x390EBD: io_ready (io.c:439)
==28639==    by 0x3932F1: io_loop (poll.c:455)
==28639==    by 0x1ABBE4: shutdown_plugins (plugin.c:2588)
==28639==  Address 0x5d25a20 is 48 bytes inside a block of size 88 free'd
==28639==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==28639==    by 0x3A31FB: del_tree (tal.c:456)
==28639==    by 0x3A317B: del_tree (tal.c:447)
==28639==    by 0x3A34DC: tal_free (tal.c:532)
==28639==    by 0x1ABAF3: shutdown_plugins (plugin.c:2575)
==28639==    by 0x16E0D3: main (lightningd.c:1514)
==28639==  Block was alloc'd at
==28639==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==28639==    by 0x3A2BCA: allocate (tal.c:256)
==28639==    by 0x3A3252: tal_alloc_ (tal.c:473)
==28639==    by 0x1A815E: plugin_rpcmethod_add (plugin.c:1425)
==28639==    by 0x1A83F9: plugin_rpcmethods_add (plugin.c:1470)
==28639==    by 0x1A965A: plugin_parse_getmanifest_response (plugin.c:1850)
==28639==    by 0x1A971F: plugin_manifest_cb (plugin.c:1872)
==28639==    by 0x1A6074: plugin_response_handle (plugin.c:663)
==28639==    by 0x1A62F0: plugin_read_json_one (plugin.c:775)
==28639==    by 0x1A652D: plugin_read_json (plugin.c:826)
==28639==    by 0x390200: next_plan (io.c:60)
==28639==    by 0x390E56: do_plan (io.c:422)
==28639==
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We can see the log message about cleanup just after the test ends.

```
>       assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1
E       assert 0 == 1

tests/test_plugin.py:3232: AssertionError
```
...
```

lightningd-3 2024-11-18T07:52:55.402Z INFO    lightningd: setconfig: autoclean-cycle 10 (updated /tmp/ltests-ao0p8pem/test_autoclean_1/lightning-3/regtest/config:4)
...
lightningd-3 2024-11-18T07:52:59.747Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: peer_out WIRE_QUERY_CHANNEL_RANGE
lightningd-3 2024-11-18T07:52:59.747Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-gossipd: reply_channel_range 0+109 (of 0+109) 2 scids
lightningd-3 2024-11-18T07:52:59.747Z DEBUG   gossipd: seeker: state = NORMAL No unannounced nodes
{'github_repository': 'ElementsProject/lightning', 'github_sha': '0729de783e95c5208b1706f7d27b23904596bb71', 'github_ref': 'refs/pull/7835/merge', 'github_ref_name': 'HEAD', 'github_run_id': 11887300979, 'github_head_ref': 'guilt/fix-flakes8', 'github_run_number': 11566, 'github_base_ref': 'master', 'github_run_attempt': '1', 'testname': 'test_autoclean', 'start_time': 1731916359, 'end_time': 1731916385, 'outcome': 'fail'}
--------------------------- Captured stdout teardown ---------------------------
lightningd-3 2024-11-18T07:53:05.503Z DEBUG   plugin-autoclean: cleaned 1 from expiredinvoices
lightningd-3 2024-11-18T07:53:05.503Z DEBUG   plugin-autoclean: setting next timer
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Sometimes l1 ratelimits before l2, and l2 receives the warning message, not l1:

```
>       assert l1.daemon.is_in_log('WARNING: Ratelimited onion_message: exceeded one per 250msec')
E       AssertionError: assert None
E        +  where None = <bound method TailableProc.is_in_log of <pyln.testing.utils.LightningD object at 0x7f13435f45b0>>('WARNING: Ratelimited onion_message: exceeded one per 250msec')
E        +    where <bound method TailableProc.is_in_log of <pyln.testing.utils.LightningD object at 0x7f13435f45b0>> = <pyln.testing.utils.LightningD object at 0x7f13435f45b0>.is_in_log
E        +      where <pyln.testing.utils.LightningD object at 0x7f13435f45b0> = <fixtures.LightningNode object at 0x7f13435cbb80>.daemon
...
lightningd-1 2024-11-19T00:45:43.721Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: peer_in WIRE_ONION_MESSAGE
lightningd-1 2024-11-19T00:45:43.721Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: peer_out WIRE_WARNING
lightningd-2 2024-11-19T00:45:43.722Z DEBUG   0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_out WIRE_ONION_MESSAGE
lightningd-2 2024-11-19T00:45:43.722Z DEBUG   connectd: REPLY WIRE_CONNECTD_INJECT_ONIONMSG_REPLY with 0 fds
lightningd-2 2024-11-19T00:45:43.722Z DEBUG   0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_in WIRE_WARNING
lightningd-2 2024-11-19T00:45:43.722Z INFO    0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Received WIRE_WARNING: WARNING: Ratelimited onion_message: exceeded one per 250msec
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Keep a proper cache of all possible ones.  I think this may be the
timeout problem: according to the logs, channeld_fakenet stops responding
and thus HTLCs eventually time out.

```
```
2024-12-16T23:16:16.4874420Z lightningd-1 2024-12-16T22:45:14.068Z UNUSUAL 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: Adding HTLC 18446744073709551615 too slow: killing connection
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Make sure balancing payment is fully cleared before trying to get a routeL

```
    def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors):

        # now we send one 'sticky' htlc: l4->l1
        amt = 10**8 // 2
        sticky_inv = l1.rpc.invoice(amt, '2', 'sticky')
>       route = l4.rpc.getroute(l1.info['id'], amt, 1)['route']

tests/test_closing.py:1232:

>           raise RpcError(method, payload, resp['error'])
E           pyln.client.lightning.RpcError: RPC call failed: method: getroute, payload: {'id': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', 'amount_msat': 50000000, 'riskfactor': 1, 'cltv': 9}, error: {'code': 205, 'message': 'Could not find a route'}
```
Just because we've seen the block doesn't mean onchaind has finished
starting up.

```
 _____________________________ test_restorefrompeer _____________________________
[gw0] linux -- Python 3.10.15 /home/runner/.cache/pypoetry/virtualenvs/cln-meta-project-AqJ9wMix-py3.10/bin/python

node_factory = <pyln.testing.utils.NodeFactory object at 0x7fb8f3887f70>
bitcoind = <pyln.testing.utils.BitcoinD object at 0x7fb8f3886f50>

    @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3")
    def test_restorefrompeer(node_factory, bitcoind):
        """
        Test restorefrompeer
        """
        l1, l2 = node_factory.get_nodes(2, [{'broken_log': 'ERROR: Unknown commitment #.*, recovering our funds!',
                                             'experimental-peer-storage': None,
                                             'may_reconnect': True,
                                             'allow_bad_gossip': True},
                                            {'experimental-peer-storage': None,
                                             'may_reconnect': True}])
    
        l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
    
        c12, _ = l1.fundchannel(l2, 10**5)
        assert l1.daemon.is_in_log('Peer storage sent!')
        assert l2.daemon.is_in_log('Peer storage sent!')
    
        l1.stop()
        os.unlink(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "lightningd.sqlite3"))
    
        l1.start()
        assert l1.daemon.is_in_log('Server started with public key')
    
        # If this happens fast enough, connect fails with "disconnected
        # during connection"
        try:
            l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
        except RpcError as err:
            assert "disconnected during connection" in err.error['message']
    
        l1.daemon.wait_for_log('peer_in WIRE_YOUR_PEER_STORAGE')
    
        assert l1.rpc.restorefrompeer()['stubs'][0] == _['channel_id']
    
        l1.daemon.wait_for_log('peer_out WIRE_ERROR')
        l2.daemon.wait_for_log('State changed from CHANNELD_NORMAL to AWAITING_UNILATERAL')
    
        bitcoind.generate_block(5, wait_for_mempool=1)
        sync_blockheight(bitcoind, [l1, l2])
    
        l1.daemon.wait_for_log(r'All outputs resolved.*')
        wait_for(lambda: l1.rpc.listfunds()["channels"][0]["state"] == "ONCHAIN")
    
        # Check if funds are recovered.
        assert l1.rpc.listfunds()["channels"][0]["state"] == "ONCHAIN"
>       assert l2.rpc.listfunds()["channels"][0]["state"] == "ONCHAIN"
E       AssertionError: assert 'FUNDING_SPEND_SEEN' == 'ONCHAIN'
E         - ONCHAIN
E         + FUNDING_SPEND_SEEN

tests/test_misc.py:3044: AssertionError
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@s373nZ
s373nZ force-pushed the 7827-libpq-multiarch--fixes branch from abd6276 to 3dabe66 Compare December 22, 2024 16:52
These workflows are failing due to `ubuntu-latest` being updated from version `22.04` to `24.04`.

Reference: actions/runner-images#10636

Changelog-None.
Poetry will no longer include the `poetry-plugin-export` plugin by default, which is essential for exporting dependencies. So, we now need to install it explicitly.
Fix for `The Poetry configuration is invalid:  - project must contain ['name'] properties`
@s373nZ
s373nZ force-pushed the 7827-libpq-multiarch--fixes branch 2 times, most recently from 399cec8 to 81452e4 Compare January 11, 2025 12:56
ShahanaFarooqui and others added 7 commits January 13, 2025 12:36
The BIP70 name given as testnet4 to correspond with chaininfo value.
Changelog-Added: Support for Bitcoin `testnet4`
This doesn't do anything, because it's trying to create a symlink for a
verison that doesn't exist. The version installed via brew is 0.22.5.

In any case, on any recent macOS system, this should not be necessary.

Changelog-None.
@s373nZ
s373nZ force-pushed the 7827-libpq-multiarch--fixes branch from 81452e4 to 35ae98a Compare January 14, 2025 16:11
@ShahanaFarooqui
ShahanaFarooqui force-pushed the 7827-libpq-multiarch--fixes branch 3 times, most recently from 2485147 to 1d2620b Compare January 14, 2025 21:28
arowser and others added 7 commits January 14, 2025 13:54
- Add Postgres dependencies: bison, flex and libiu-dev.
- Fix missing `&&` in chained wget commands.
- Add `POSTGRES_CONFIG` and `PG_CONFIG` for all architectures.
- Remove existing `libpq` Ubuntu packages.
- Copy libpq libraries from builder directly to final image.

Changelog-Fixed: Fixes Postgres driver availability for arm64 and arm32 Docker images.
Undertaken to upgrade QEMU to 7.2. Also upgrades Python to 3.11
implicitly and migrates Python dependency management to virtual environments.

Changelog-Changed: Released Docker images are now based on Debian Bookworm
- Align indentation.
- Use multi-line `ENV` where values don't depend on each other.

Changelog-None
- Install `poetry-plugin-export` as a separate step.
- Remove `--no-update` option from `poetry lock` as it's now default behavior.
- Add `poetry lock` to the command chain after removing cln-rest and wss-proxy.

https://github.com/python-poetry/poetry/releases/tag/2.0.0
Main application poetry.lock file as well as clnrest and wss-proxy.
To ensure the workflow uses updated files, including the Dockerfile, from the same branch for testing.
@ShahanaFarooqui
ShahanaFarooqui force-pushed the 7827-libpq-multiarch--fixes branch from 1d2620b to 3286bab Compare January 14, 2025 21:54
@s373nZ s373nZ closed this Apr 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.