Hey peeps! We have a pact-broker (image: `dius/pac...
# pact-broker
a
Hey peeps! We have a pact-broker (image:
dius/pact-broker:2.100.0.1
) running on a k8s cluster reachable via an A record on a domain with a wildcard CA certificate. For some reason, our JVM provider verification is failing with the following error despite the docs mentioning that we shouldn’t need to do anything. Thoughts? Error:
Copy code
au.com.dius.pact.core.pactbroker.InvalidNavigationRequest: Failed to fetch the root HAL document
    Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
JVM provider dependency:
Copy code
testImplementation "au.com.dius.pact.provider:junit5spring:4.3.8"
A python consumer also fails to publish the pacts in CI because of a 400 error (I’m guessing it’s the same cause), though it works locally. Dependency:
pact-python==1.5.2
.
m
Who’s the provider of the certificate?
you wouldn’t get a
400
for an invalid certificate. You wouldn’t get a valid HTTP response at all
a
400
means bad request - a problem with the request.
a
Hey Matt!
Issued by: Sectigo RSA Domain Validation Secure Server CA
I’m aware of a
400
being a bad request. Not sure why publishing the same pacts fails in CI but works locally against the same broker (the env variables are similar identical in both environments) 🙂 Relevant logs from CI:
Copy code
...
        publish_process = Popen(command)                                                                                                                         
        publish_process.wait()                                                                                                                                   
        if publish_process.returncode != 0:                                                                                                                      
            url = self._get_broker_base_url()                                                                                                                    
>           raise RuntimeError(                                                                                                                                  
                f"There was an error while publishing to the pact broker at {url}.")                                                                             
E           RuntimeError: There was an error while publishing to the pact broker at <https://dev-broker.example.app>.                                  
                                                                                                                                                                 
/root/.local/lib/python3.8/site-packages/pact/broker.py:94: RuntimeError                                                                                         
--------------------------- Captured stdout teardown ---------------------------                                                                                 
INFO  going to shutdown ...                                                                                                                                      
INFO  WEBrick::HTTPServer#start done.                                                                                                                            
--------------------------- Captured stderr teardown ---------------------------                                                                                 
PactBroker::Client::Hal::ErrorResponseReturned - Error making request to <https://dev-broker.example.app> status=400                                   
=========================== short test summary info ============================                                                                                 
ERROR tests/consumer/test_experiment_service_consumer.py::test_get_available_interventions                                                                       
========================== 6 passed, 1 error in 1.95s ==========================

PactBroker::Client::Hal::ErrorResponseReturned - Error making request to <https://dev-broker.example.app> status=400                                   
Error in atexit._run_exitfuncs:                                                                                                                                  
Traceback (most recent call last):                                                                                                                               
  File "/root/.local/lib/python3.8/site-packages/pact/pact.py", line 243, in stop_service                                                                        
    self.publish(                                                                                                                                                
  File "/root/.local/lib/python3.8/site-packages/pact/broker.py", line 94, in publish                                                                            
    raise RuntimeError(                                                                                                                                          
RuntimeError: There was an error while publishing to the pact broker at <https://dev-broker.example.app>.
m
Didn’t realise comodo is now Sectigo. Interesting!
So it should work, obviously, assuming it’s in your default truststore
This is going to require a good understanding of how certificates are stored and used on your system. Usually, each language / OS has a place that stores certificate bundles and uses them at runtime. You’re going to need to check those to ensure the Sectigo RSA CA is in them
In java, it’s called a trust store
I can’t say for Python, it might just use the standard OS certificate chain (which will obviously differ across OS’s)
a
I didn’t realise that either until you just pointed that out! 😄. And thanks, down the rabbit hole I go.. I’m on macOS 🙂
👍 1
m
In the case of Python - assuming it is the problem - I’d first get some verbose/debug level logs so we can ensure it is the problem
then we need to check the bundled certificates in the Ruby standalone that’s packaged in there
a
Ah. Didn’t know there were certificates bundled. That explains why things worked fine using pactflow.io. Both Java and Python are problematic atm! Java for provider verifications (locally), and Python for publishing pacts (in CI). It’s a fun matrix haha
m
oh, lord
Didn’t know there were certificates bundled
For clarification. We don’t bundle any Pactflow specific certs, but Java and other libs/browsers/tools often bundle well known certs together (e.g. https://curl.se/docs/caextract.html)
a
The more I learn about computers, the more I realise how much I don’t know 😭
this 1
💯 1
Thanks for the link and the clarification!
b
If you set the env var DEBUG=true then you should get the response body that tells you what the 400 error is for. I'm surprised it's not included in the output already.
The error output for the pact broker client usually includes the body.
a
Hey @Beth (pactflow.io/Pact Broker/pact-ruby)! Thanks for the reply. We’re using pytest module to run the test files, and setting
DEBUG=true
didn’t make a difference unfortunately.
My current execution script is:
Copy code
export DEBUG=true # and other pact variables

ls tests/consumer/ | grep 'test_experiment' | \
    xargs -n 1 -I '{}' python3 -m pytest -vv --log-level=DEBUG --log-cli-level=DEBUG tests/consumer/'{}'
A little update on this: publishing the same pacts in CI using pactfoundation/pact-cli:latest fails with the 400 error too. Makes me believe this is a ruby issue! Any suggestions for logging the request body here except
DEBUG=true
?
m
hmm strange
there should be a way to pass the environment variable / flags to the underlying process.
--verbose
should do that I believe
a
Jinx! Finally figured out how to log the request. Turns out, it was
VERBOSE=true
flag. After setting that, I see this:
Copy code
Reading environment variable exporting file contents.                                                                                                            
Reading environment variable exporting file contents.                                                                                                            
-> "HTTP/1.1 400 Bad Request\r\n"                                                                                                                                
-> "Content-Type: text/plain\r\n"                                                                                                                                
-> "Content-Length: 0\r\n"                                                                                                                                       
-> "Status: 400 Bad Request\r\n"                                                                                                                                 
-> "Date: Mon, 20 Jun 2022 05:54:56 GMT\r\n"                                                                                                                     
-> "X-Powered-By: Phusion Passenger(R) 6.0.12\r\n"                                                                                                               
-> "Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.12\r\n"                                                                                                      
-> "Via: 1.1 google\r\n"                                                                                                                                         
-> "Alt-Svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\r\n"                                                                                             
-> "\r\n"                                                                                                                                                        
reading 0 bytes...                                                                                                                                               
-> ""
Despite the json files existing with correct read privileges..
I’ve tried both specifying the directory approach and individual pact files too. NADA
The same works locally however 🙃
Local:
Copy code
> docker run --rm pactfoundation/pact-cli:latest version
0.39.0
In CI, it’s
0.50.0
. Trying out
0.39.0.0
now in CI fingerscrossed
Pfft, it worked!
m
wait so you downgraded the CLI and it started working?
Could you please send the full verbose output here? (pls redact any credentials)
a
Yessir! As before, I stopped publishing the pacts from the python library, dunno how to enable verbosity there (maybe the same flag). Here’s the output from pact-cli directly:
Copy code
Executing command: export VERBOSE=true
------------------------------
Executing command: ls -lah myapp/src/tests/pacts
total 16K    
drwxr-xr-x    2 root     root         108 Jun 20 00:03 .
drwxr-xr-x    5 root     root         132 Jun 20 01:54 ..
-rw-r--r--    1 root     root        3.5K Jun 20 06:02 myapp-provider1.json
-rw-r--r--    1 root     root       10.4K Jun 20 06:02 myapp-provider2.json
------------------------------
Executing command: /pact/entrypoint.sh version
0.50.0
------------------------------
Executing command: /pact/entrypoint.sh publish myapp/src/tests/pacts/myapp-provider1.json --consumer-app-version fake-git-sha-for-demo-1234567 --tag-with-git-branch
opening connection to <http://blah.example.app:443|blah.example.app:443>...
opened
starting SSL for <http://blah.example.app:443|blah.example.app:443>...
SSL established, protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384
<- "GET /? HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: application/hal+json\r\nUser-Agent: Ruby\r\nAuthorization: [redacted]\r\n"
-> "HTTP/1.1 400 Bad Request\r\n"
-> "Content-Type: text/plain\r\n"
-> "Content-Length: 0\r\n"
-> "Status: 400 Bad Request\r\n"
-> "Date: Mon, 20 Jun 2022 06:02:37 GMT\r\n"
-> "X-Powered-By: Phusion Passenger(R) 6.0.12\r\n"
-> "Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.12\r\n"
-> "Via: 1.1 google\r\n"
-> "Alt-Svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\r\n"
-> "\r\n"
reading 0 bytes...
-> ""
read 0 bytes
Conn keep-alive
PactBroker::Client::Hal::ErrorResponseReturned - Error making request to <https://blah.example.app> status=400
Reading environment variable exporting file contents.
Reading environment variable exporting file contents.
Failed with exit code: 1
m
thanks, and what does it look like for the version that’s working?
(sorry I got a phone call when you posted)
You should also get a log entry in your broker (or if it’s Pactflow’s - our logs) that you can see why it was a 400
a
No worries, sorry, let me get back to you in a bit!
👍 1
So for the successful run:
Copy code
Tagged version fake-git-sha-for-demo-1234567 of myapp as "new/feature-branch"
Publishing myapp/provider1 pact to pact broker at <https://blah.example.app>
Regarding logs on broker, that was my first guess too, but I haven’t spotted any 🙂 The env variables we’re using on our deployment of image `dius/pact-broker:2.100.0.1`:
Copy code
- name: PACT_BROKER_LOG_LEVEL
  value: DEBUG
- name: PACT_BROKER_ALLOW_DANGEROUS_CONTRACT_MODIFICATION
  value: 'false'
- name: PACT_BROKER_DATABASE_CONNECT_MAX_RETRIES
  value: '10'
- name: PACT_BROKER_ALLOW_MISSING_MIGRATION_FILES
  value: 'false'
- name: PACT_BROKER_DATABASE_MAX_CONNECTIONS
  value: '1'
- name: PACT_BROKER_BASIC_AUTH_ENABLED
  value: 'true'
- name: PACT_BROKER_PUBLIC_HEARTBEAT
  value: 'true'
Also worth mentioning, I don’t think that specifying the SSL file/dir to the CLI tool has any effects. I tried specifying a SSL file and changed a character in it, and I didn’t receive any issues related to SSL connection establishment failure (this was using the local version
0.39.0
).
b
My bad @Akash, I should have known it was
VERBOSE
not
DEBUG
, sorry for the bum steer.
Copy code
-> "\r\n"
reading 0 bytes...
-> ""
ok, that should not be possible! Every 400 should return error text, so there’s a bug there somewhere.
Ok, what’s even more confusing is that’s a GET request to the index resource. There is no validation on a get to the index resource!
I’m not seeing any headers in that response that tell me the request ever reached the Pact Broker Ruby application. I think that response is coming from Passenger/Ngnix.
This is what you get if you curl the vanilla ruby application:
Copy code
$ curl -v <http://localhost:9292>
*   Trying ::1:9292...
* Connected to localhost (::1) port 9292 (#0)
> GET / HTTP/1.1
> Host: localhost:9292
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Vary: Accept
< Content-Type: application/hal+json;charset=utf-8
< Content-Length: 4300
< Date: Wed, 22 Jun 2022 04:30:10 GMT
< Server: Webmachine-Ruby/1.6.0 Rack/1.3
< X-Pact-Broker-Version: 2.101.0
< X-Content-Type-Options: nosniff
< Connection: Keep-Alive
The Server header may get overridden by the nginx value, but the X-Pact-Broker-Version header should be in there, and it’s not.
Can you try running a curl request against your broker as I have shown above, and share the output please?
a
Hi @Beth (pactflow.io/Pact Broker/pact-ruby), no worries regarding the confusion between the flag names! Since my last message here—I ended up decoupling the responsibility of publishing from the Python library
pact-python==1.5.2
and hence, downgrading the
pactfoundation/pact-cli:latest
image version to
0.39.0.0
. The problem is resolved since. Regarding your query, we’re on version
dius/pact-broker:2.100.0.1
and I curled both ports 9292 and 80. Results:
Copy code
root@160b72d08493:/home/app/pact_broker# curl -v <http://localhost:9292>
*   Trying 127.0.0.1:9292...
* TCP_NODELAY set
* connect to 127.0.0.1 port 9292 failed: Connection refused
*   Trying ::1:9292...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
*   Trying ::1:9292...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
* Failed to connect to localhost port 9292: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 9292: Connection refused
root@160b72d08493:/home/app/pact_broker# 
root@160b72d08493:/home/app/pact_broker# 
root@160b72d08493:/home/app/pact_broker# curl -v <http://localhost:80>
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
< Status: 401 Unauthorized
< WWW-Authenticate: Basic realm="Restricted area"
< Date: Thu, 23 Jun 2022 03:03:30 GMT
< X-Powered-By: Phusion Passenger(R) 6.0.12
< Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.12
< 
* Connection #0 to host localhost left intact
root@160b72d08493:/home/app/pact_broker#
m
you might need to get through the basic auth layer - could you please add the user/password to your curl, try again and share (redacting the values)?
a
Ah yep. These are local dev credentials so it’s fine:
Copy code
root@160b72d08493:/home/app/pact_broker# curl -v <http://localhost:9292> -u "user2:password"
*   Trying 127.0.0.1:9292...
* TCP_NODELAY set
* connect to 127.0.0.1 port 9292 failed: Connection refused
*   Trying ::1:9292...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
*   Trying ::1:9292...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
* Failed to connect to localhost port 9292: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 9292: Connection refused
root@160b72d08493:/home/app/pact_broker# 
root@160b72d08493:/home/app/pact_broker# curl -v <http://localhost:80> -u "user2:password"
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
* Server auth using Basic with user 'user2'
> GET / HTTP/1.1
> Host: localhost
> Authorization: Basic dXNlcjI6cGFzc3dvcmQ=
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/hal+json;charset=utf-8
< Content-Length: 4160
< Connection: keep-alive
< Status: 200 OK
< Date: Thu, 23 Jun 2022 03:13:10 GMT
< Vary: Accept
< X-Content-Type-Options: nosniff
< Server: Webmachine-Ruby/1.6.0 Rack/1.3
< X-Pact-Broker-Version: 2.100.0
< X-Powered-By: Phusion Passenger(R) 6.0.12
< 
{"_links":{"self":{"href":"<http://localhost>","title":"Index","templated":false},"pb:publish-pact":{"href":"<http://localhost/pacts/provider/{provider}/consumer/{consumer}/version/{consumerApplicationVersion}>","title":"Publish a pact","templated":true},"pb:publish-contracts":{"href":"<http://localhost/contracts/publish>","title":"Publish contracts","templated":false},"pb:latest-pact-versions":{"href":"<http://localhost/pacts/latest>","title":"Latest pact versions","templated":false},"pb:tagged-pact-versions":{"href":"<http://localhost/pacts/provider/{provider}/consumer/{consumer}/tag/{tag}>","title":"All versions of a pact for a given consumer, provider and consumer version tag","templated":false},"pb:pacticipants":{"href":"<http://localhost/pacticipants>","title":"Pacticipants","templated":false},"pb:pacticipant":{"href":"<http://localhost/pacticipants/{pacticipant}>","title":"Fetch pacticipant by name","templated":true},"pb:latest-provider-pacts":{"href":"<http://localhost/pacts/provider/{provider}/latest>","title":"Latest pacts by provider","templated":true},"pb:latest-provider-pacts-with-tag":{"href":"<http://localhost/pacts/provider/{provider}/latest/{tag}>","title":"Latest pacts for provider with the specified tag","templated":true},"pb:provider-pacts-with-tag":{"href":"<http://localhost/pacts/provider/{provider}/tag/{tag}>","title":"All pact versions for the provider with the specified consumer version tag","templated":true},"pb:provider-pacts":{"href":"<http://localhost/pacts/provider/{provider}>","title":"All pact versions for the specified provider","templated":true},"pb:latest-version":{"href":"<http://localhost/pacticipants/{pacticipant}/latest-version>","title":"Latest pacticipant version","templated":true},"pb:latest-tagged-version":{"href":"<http://localhost/pacticipants/{pacticipant}/latest-version/{tag}>","title":"Latest pacticipant version with the specified tag","templated":true},"pb:webhooks":{"href":"<http://localhost/webhooks>","title":"Webhooks","templated":false},"pb:webhook":{"href":"<http://localhost/webhooks/{uuid}>","title":"Webhook","templated":true},"pb:integrations":{"href":"<http://localhost/integrations>","title":"Integrations","templated":false},"pb:pacticipant-version-tag":{"href":"<http://localhost/pacticipants/{pacticipant}/versions/{version}/tags/{tag}>","title":"Get, create or delete a tag for a pacticipant version","templated":true},"pb:pacticipant-branch-version":{"href":"<http://localhost/pacticipants/{pacticipant}/branches/{branch}/versions/{version}>","title":"Get or add/create a pacticipant version for a branch","templated":true},"pb:pacticipant-version":{"href":"<http://localhost/pacticipants/{pacticipant}/versions/{version}>","title":"Get, create or delete a pacticipant version","templated":true},"pb:metrics":{"href":"<http://localhost/metrics>","title":"Get Pact Broker metrics"},"pb:can-i-deploy-pacticipant-version-to-tag":{"href":"<http://localhost/can-i-deploy?pacticipant={pacticipant}&version={version}&to={tag}>","title":"Determine if an application version can be safely deployed to an environment identified by the given tag","templated":true},"pb:can-i-deploy-pacticipant-version-to-environment":{"href":"<http://localhost/can-i-deploy?pacticipant={pacticipant}&version={version}&environment={environment}>","title":"Determine if an application version can be safely deployed to an environment","templated":true},"pb:provider-pacts-for-verification":{"href":"<http://localhost/pacts/provider/{provider}/for-verification>","title":"Pact versions to be verified for the specified provider","templated":true},"beta:provider-pacts-for-verification":{"name":"beta","href":"<http://localhost/pacts/provider/{provider}/for-verification>","title":"DEPRECATED - please use pb:provider-pacts-for-verification","templated":true},"curies":[{"name":"pb","href":"<http://localhost/doc/{rel}?context=index>","templated":true},{"name":"beta","href":"<http://localhost/doc/{rel}?context=index>","templated":true}],"pb:environments":{"title":"Environments","href":"<http://localhost/environments>","templated":false},"pb:environment":{"title":"Environment","* Connection #0 to host localhost left intact
root@160b72d08493:/home/app/pact_broker#
b
OK, you can see that got through to the ruby app because of the headers
Copy code
< Server: Webmachine-Ruby/1.6.0 Rack/1.3
< X-Pact-Broker-Version: 2.100.0
0.39.0.0
is over a year old
a
Without being any close to your levels of insights in the repos, I don’t think it’s the broker that’s the problem—it was the publisher failing to read the Pact json files.
b
we don’t want you to be stuck on that.
😲 1
And you still have the same error with the
0.50.0.29
tag of the pact docker cli?
a
I can’t say for sure which patch version
latest
was previously pointing to in CI, since we don’t have access to the docker daemon. Here are my results from a quick few tests:
b
that’s a testing matrix I like to see.
🙌 1
a
The k8s dev deployment is on a public facing URL backed by a wildcard cert. Didn’t see any SSL issues 😄
Haha yeah! It was getting confusing to word that out. Tables FTW
b
and what happens if you try to publish to your real broker from your local dev machine using the 0.50.0.29 image?
a
I forgot to add that row, it passes 🙂/😞
b
hm.
what made you try 0.39.0.0?
a
Good question! My local “latest” was previously pointing to
0.39.0
(dunno which patch version) when publishing was failing for the Python and pact-cli (
0.59.0
) on CI
In fact, we were able to publish from the python library
pact-python==1.5.2
locally to k8s dev too.
b
My next thought is for you to run 0.39.0.0 with VERBOSE=true on, and then run 0.50.0.29 with VERBOSE=true on, and compare the output, character for character.
a
In CI?
b
I cannot for the life of me think of anything that could be different about a “GET /” request that could be different, but weird things happen. Yes, in CI, seeing as that’s the only place it’s reproducible.
You didn’t redact any paths out of the logs when you pasted them did you?
There’s not a lot that can go wrong with this request!
Copy code
<- "GET /? HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: application/hal+json\r\nUser-Agent: Ruby\r\nAuthorization: [redacted]\r\n"
a
Yeah, I can’t think of something either unfortunately. Long shot but is it a PATCH request perhaps? I just tried publishing with both versions again in CI, same error as in the previous message, except that we aren’t using
--tag-with-git-branch
anymore.
b
The HTTP logs say it’s a GET, right?
Ok, now the logs for the working one. And then we can put them side by side.
a
Yeah, I’m not sure if the underlaying library (since it’s trying to include a body) is actually making a PATCH/PUT request but reporting the request method as GET. Like I said, long shot.
b
I’ve not heard of that happening. But tech be weird.
1000 1
a
Already done a diff compare with the working one, logs:
Copy code
Executing command: export VERBOSE=true
------------------------------
Executing command: ls -lah myapp/src/tests/pacts
total 16K    
drwxr-xr-x    2 root     root         108 Jun 23 01:39 .
drwxr-xr-x    5 root     root         132 Jun 23 01:39 ..
-rw-r--r--    1 root     root        3.5K Jun 23 03:24 myapp-provider1.json
-rw-r--r--    1 root     root       10.4K Jun 23 03:24 myapp-provider2.json
------------------------------
Executing command: /pact/entrypoint.sh version
0.39.0
------------------------------
Executing command: /pact/entrypoint.sh publish myapp/src/tests/pacts --consumer-app-version $PACT_CONSUMER_VERSION

Publishing myapp/provider1 pact to pact broker at <https://blah.example.app>
The latest version of this pact can be accessed at the following URL:
<https://blah.example.app/pacts/provider/provider1/consumer/myapp/latest>
Publishing myapp/provider2 pact to pact broker at <https://blah.example.app>
The latest version of this pact can be accessed at the following URL:
<https://blah.example.app/pacts/provider/provider2/consumer/myapp/latest>


Successfully ran freestyle step: Publish Python pacts
Reading environment variable exporting file contents.
Reading environment variable exporting file contents.
b
How are you going with this @Akash? I can’t see anything in the output there that hints at what the problem is.
a
We stopped looking into it, didn’t seem worth fixing.
m
Hmm. My concern is now you’ve pinned your CLI to an old version and at some point in the future you’ll have to deal with it. i.e. you’re just putting off a problem that future Akash (or worse, somebody without the context) will have to solve.
👆🏼 1
a
True. We're aware of that and prefer locking down software versions to avoid unforeseen breakages regardless :)
👍 1
b
Ideally you can lock it to a version that’s less than 1 year old though @Akash!