Unfortunatelly this is not working. I had different results trying to using either pak, pkgdepends, pkgcache, the R package curl and curl itself.
It is pretty difficult to write a reprex for this kind of issue but I tried. The codebase is quite complex to navigate so I can't pin point where something happens between pak and pkgdepends to have different behaviours.
library(testthat)
library(withr)
library(processx)
library(glue)
library(fs)
library(rlang)
library(pak)
library(pkgdepends)
library(pkgcache)
library(curl)
# actual value ommited
repository_url <- "https://<host>/<user>/<project>"
curl
test_that("without the correct CA bundle curl fails with a self-signed certificate error", {
expect_error({
tmp <- run(
"curl",
c(
"--request", "HEAD",
"--fail",
"--silent",
"--show-error",
"--location",
"--url", repository_url
),
echo = TRUE,
env = c(CURL_CA_BUNDLE = path_wd("ca-certificates_minus_self-signed.crt"))
)
})
})
#> curl: (60) SSL certificate problem: self-signed certificate
#> More details here: https://curl.se/docs/sslcerts.html
#>
#> curl failed to verify the legitimacy of the server and therefore could not
#> establish a secure connection to it. To learn more about this situation and
#> how to fix it, please visit the web page mentioned above.
#> Test passed 🎊
test_that("with the correct CA bundle curl succeeds with a self-signed certificate error", {
expect_no_error({
tmp <- run(
"curl",
c(
"--request", "HEAD",
"--fail",
"--silent",
"--show-error",
"--location",
"--url", repository_url
),
echo = TRUE,
env = c(CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt")
)
})
})
#> Test passed 🎊
proving we can control which CA bundle curl should use
and that the installed one (/etc/ssl/certs/ca-certificates.crt
) works.
curl (R package)
test_that("without the correct CA bundle the R package curl fails with a self-signed certificate error", {
expect_error({
with_envvar(
new = c("CURL_CA_BUNDLE" = path_wd("ca-certificates_minus_self-signed.crt")),
{
handle <- new_handle()
handle_setopt(handle, customrequest = "HEAD")
tmp <- curl_fetch_memory(
repository_url,
handle = handle
)
}
)
})
})
#> ── Failure: without the correct CA bundle the R package curl fails with a self-signed certificate error ──
#> `{ ... }` did not throw an error.
#> Error:
#> ! Test failed
it doesn’t fail, so we didn’t change the CA bundle to use
test_that("with the correct CA bundle the R package curl succeeds with a self-signed certificate error", {
expect_no_error({
with_envvar(
new = c("CURL_CA_BUNDLE" = "/etc/ssl/certs/ca-certificates.crt"),
{
handle <- new_handle()
handle_setopt(handle, customrequest = "HEAD")
tmp <- curl_fetch_memory(
repository_url,
handle = handle
)
}
)
})
})
#> Test passed 🎊
It succeeds, but it is a coincidence,
because the default location for the used CA bundle works,
not because we directed curl to use the correct bundle.
pkgdepends
test_that("with the default CA bundle pkgdepends works with self-signed certificate", {
solution <- expect_no_error({
prop <- new_pkg_deps(glue("git::{repository_url}.git"))$solve()
prop$get_solution()
})
expect_equal(solution$status, "OK")
expect_equal(solution$problem$total, 198)
})
#> Test passed 😸
test_that("with the correct CA bundle pkgdepends works with self-signed certificate", {
with_options(
new = list(async_http_cainfo = "/etc/ssl/certs/ca-certificates.crt"),
{
solution <- expect_no_error({
prop <- new_pkg_deps(glue("git::{repository_url}.git"))$solve()
prop$get_solution()
})
expect_equal(solution$status, "OK")
expect_equal(solution$problem$total, 198)
}
)
})
#> Test passed 🎊
test_that("with the wrong CA bundle pkgdepends fails with self-signed certificate", {
with_options(
new = list(async_http_cainfo = path_wd("ca-certificates_minus_self-signed.crt")),
{
solution <- expect_no_error({
prop <- new_pkg_deps(glue("git::{repository_url}.git"))$solve()
prop$get_solution()
})
expect_equal(solution$status, "FAILED")
expect_equal(solution$problem$total, 2)
}
)
})
#> ── Failure: with the wrong CA bundle pkgdepends fails with self-signed certificate ──
#> solution$status not equal to "FAILED".
#> 1/1 mismatches
#> x[1]: "OK"
#> y[1]: "FAILED"
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_equal(solution$status, "FAILED")
#>
#> ── Failure: with the wrong CA bundle pkgdepends fails with self-signed certificate ──
#> solution$problem$total not equal to 2.
#> 1/1 mismatches
#> [1] 198 - 2 == 196
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_equal(solution$problem$total, 2)
#> Error:
#> ! Test failed
so we can control the CA bundle using the R option async_http_cainfo
for pkgdepends.
pak
Default use case
test_that("with the default CA bundle pak succeeds to install package", {
with_options(
new = list(),
{
# hack to hide full backtrace with actual URLs and IP addresses
result <- catch_cnd(
{
pkg_install(glue("git::{repository_url}.git"))
},
classes = c("error")
)
expect_null(result)
expect_equal(
result$message,
expected = ""
)
}
)
})
#> ── Failure: with the default CA bundle pak succeeds to install package ─────────
#> `result` is not NULL
#>
#> `actual` is an S3 object of class <callr_status_error/callr_error/rlib_error_3_0/rlib_error/error/condition>, a list
#> `expected` is NULL
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_null(result)
#>
#> ── Failure: with the default CA bundle pak succeeds to install package ─────────
#> result$message not equal to "".
#> 1/1 mismatches
#> x[1]: "error in pak subprocess"
#> y[1]: ""
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_equal(result$message, expected = "")
#> Error:
#> ! Test failed
unlike pkgdepends
, here pak
fails.
Using the R option async_http_cainfo
test_that("using the option async_http_cainfo to point pak to use the correct CA bundle works to install package", {
with_options(
new = list(async_http_cainfo = "/etc/ssl/certs/ca-certificates.crt"),
{
# hack to hide full backtrace with actual URLs and IP addresses
result <- catch_cnd(
{
pkg_install(glue("git::{repository_url}.git"))
},
classes = c("error")
)
expect_null(result)
expect_equal(
result$message,
expected = ""
)
}
)
})
#> ── Failure: using the option async_http_cainfo to point pak to use the correct CA bundle works to install package ──
#> `result` is not NULL
#>
#> `actual` is an S3 object of class <callr_status_error/callr_error/rlib_error_3_0/rlib_error/error/condition>, a list
#> `expected` is NULL
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_null(result)
#>
#> ── Failure: using the option async_http_cainfo to point pak to use the correct CA bundle works to install package ──
#> result$message not equal to "".
#> 1/1 mismatches
#> x[1]: "error in pak subprocess"
#> y[1]: ""
#> Backtrace:
#> ▆
#> 1. ├─rlang::with_options(...)
#> 2. └─testthat::expect_equal(result$message, expected = "")
#> Error:
#> ! Test failed
Using the environment variable CURL_CA_BUNDLE
test_that("using CURL_CA_BUNDLE to point to the correct CA bundle pak works to install package", {
with_envvar(
new = c("CURL_CA_BUNDLE" = "/etc/ssl/certs/ca-certificates.crt"),
{
# hack to hide full backtrace with actual URLs and IP addresses
result <- catch_cnd(
{
pkg_install(glue("git::{repository_url}.git"))
},
classes = c("error")
)
expect_null(result)
expect_equal(
result$message,
expected = ""
)
}
)
})
#> ── Failure: using CURL_CA_BUNDLE to point to the correct CA bundle pak works to install package ──
#> `result` is not NULL
#>
#> `actual` is an S3 object of class <callr_status_error/callr_error/rlib_error_3_0/rlib_error/error/condition>, a list
#> `expected` is NULL
#> Backtrace:
#> ▆
#> 1. ├─withr::with_envvar(...)
#> 2. │ └─base::force(code)
#> 3. └─testthat::expect_null(result)
#>
#> ── Failure: using CURL_CA_BUNDLE to point to the correct CA bundle pak works to install package ──
#> result$message not equal to "".
#> 1/1 mismatches
#> x[1]: "error in pak subprocess"
#> y[1]: ""
#> Backtrace:
#> ▆
#> 1. ├─withr::with_envvar(...)
#> 2. │ └─base::force(code)
#> 3. └─testthat::expect_equal(result$message, expected = "")
#> Error:
#> ! Test failed