feat: R-minor-sensitive binary builds (full + iterative) #84

Merged
pat-s merged 15 commits from t3code/69b1319c into main 2026-06-14 08:04:41 +00:00
17 changed files with 555 additions and 259 deletions
Showing only changes of commit 251bba8f23 - Show all commits

style: apply air and prettier formatting repo-wide

Patrick Schratz 2026-06-13 21:23:37 +02:00
Signed by: pat-s
GPG key ID: 3C6318841EF78925

View file

@ -6,7 +6,6 @@ when:
- event: cron - event: cron
cron: archive-missed-packages cron: archive-missed-packages
matrix: matrix:
include: include:
- CODENAME: jammy - CODENAME: jammy

View file

@ -39,7 +39,7 @@ depends_on:
steps: steps:
- name: 'Build binaries' - name: 'Build binaries'
image: "reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}" image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}'
pull: true pull: true
environment: environment:
RED_HAT_DEV_PW: RED_HAT_DEV_PW:
@ -57,7 +57,7 @@ steps:
# normal env vars # normal env vars
GIT_USER: pat-s GIT_USER: pat-s
# set the location of the 'pkgcache' cache dir which persists the R package dependencies needed to install the packages themselves # set the location of the 'pkgcache' cache dir which persists the R package dependencies needed to install the packages themselves
R_PKG_CACHE_DIR: "" R_PKG_CACHE_DIR: ''
R_LIBS_USER: /mnt/cache/R-pkgs R_LIBS_USER: /mnt/cache/R-pkgs
CCACHE_DIR: /mnt/cache/ccache CCACHE_DIR: /mnt/cache/ccache
NCPUS: 2 NCPUS: 2

View file

@ -54,7 +54,7 @@ depends_on:
steps: steps:
- name: 'Build binaries' - name: 'Build binaries'
image: "reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}" image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}'
pull: true pull: true
environment: environment:
RED_HAT_DEV_PW: RED_HAT_DEV_PW:

View file

@ -11,16 +11,34 @@ s3fs::s3_file_system(
arch = "arm64" arch = "arm64"
os = "alpine321" os = "alpine321"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/%s/%s/latest/src/contrib", arch, os)) files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/%s/%s/latest/src/contrib",
arch,
os
))
length(unique(sapply(strsplit(basename(files), "_"), function(x) x[1]))) length(unique(sapply(strsplit(basename(files), "_"), function(x) x[1])))
non_archived = unique(sapply(strsplit(basename(files), "_"), function(x) x[1])[duplicated(sapply(strsplit(basename(files), "_"), function(x) x[1]))]) non_archived = unique(sapply(strsplit(basename(files), "_"), function(x) {
x[1]
})[duplicated(sapply(strsplit(basename(files), "_"), function(x) x[1]))])
# RInno is not built because it only exists for Windows # RInno is not built because it only exists for Windows
non_archived = setdiff(non_archived, "RInno") non_archived = setdiff(non_archived, "RInno")
# future::plan("sequential", workers = 8) # future::plan("sequential", workers = 8)
future::plan("sequential", workers = 1) future::plan("sequential", workers = 1)
# future.apply::future_lapply(non_archived, function(x) archive_package(x, codename = os, arch = arch)) # future.apply::future_lapply(non_archived, function(x) archive_package(x, codename = os, arch = arch))
lapply(non_archived, function(x) archive_package(x, codename = os, arch = arch, s3_region = "hel1", s3_endpoint = "https://hel1.your-objectstorage.com", s3_bucket = "devxy-r-package-binaries-hel1", s3_access_key_id = Sys.getenv("HETZNER_S3_ACCESS_KEY_K3S", s3_secret_access_key = Sys.getenv("HETZNER_S3_SECRET_KEY_K3S")))) lapply(non_archived, function(x) {
archive_package(
x,
codename = os,
arch = arch,
s3_region = "hel1",
s3_endpoint = "https://hel1.your-objectstorage.com",
s3_bucket = "devxy-r-package-binaries-hel1",
s3_access_key_id = Sys.getenv(
"HETZNER_S3_ACCESS_KEY_K3S",
s3_secret_access_key = Sys.getenv("HETZNER_S3_SECRET_KEY_K3S")
)
)
})
# archive_package() # archive_package()
# grep("duckdb_", files, value = T) # grep("duckdb_", files, value = T)

View file

@ -1,4 +1,3 @@
### Scope: Check whether there are any incomplete entries in PACKAGES.rds. These will result in NA when calling available.packages() ### Scope: Check whether there are any incomplete entries in PACKAGES.rds. These will result in NA when calling available.packages()
foo = available.packages("https://cran.devxy.io/amd64/jammy/latest/src/contrib") foo = available.packages("https://cran.devxy.io/amd64/jammy/latest/src/contrib")
sum((is.na(foo[, "Version"]))) sum((is.na(foo[, "Version"])))
@ -17,7 +16,9 @@ foo = available.packages("https://cran.devxy.io/amd64/rhel9/latest/src/contrib")
sum((is.na(foo[, "Version"]))) sum((is.na(foo[, "Version"])))
which((is.na(foo[, "Version"]))) which((is.na(foo[, "Version"])))
foo = available.packages("https://cran.devxy.io/amd64/alpine320/latest/src/contrib") foo = available.packages(
"https://cran.devxy.io/amd64/alpine320/latest/src/contrib"
)
sum((is.na(foo[, "Version"]))) sum((is.na(foo[, "Version"])))
which((is.na(foo[, "Version"]))) which((is.na(foo[, "Version"])))
@ -40,6 +41,8 @@ foo = available.packages("https://cran.devxy.io/arm64/rhel9/latest/src/contrib")
sum((is.na(foo[, "Version"]))) sum((is.na(foo[, "Version"])))
which((is.na(foo[, "Version"]))) which((is.na(foo[, "Version"])))
foo = available.packages("https://cran.devxy.io/arm64/alpine320/latest/src/contrib") foo = available.packages(
"https://cran.devxy.io/arm64/alpine320/latest/src/contrib"
)
sum((is.na(foo[, "Version"]))) sum((is.na(foo[, "Version"])))
which((is.na(foo[, "Version"]))) which((is.na(foo[, "Version"])))

View file

@ -2,42 +2,58 @@ library(future)
future::plan(multisession) future::plan(multisession)
future::plan(sequential) future::plan(sequential)
time = Sys.time() time = Sys.time()
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib/Archive"), recurse = T) files <- s3fs::s3_dir_ls(
sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib/Archive"
),
recurse = T
)
Sys.time() - time Sys.time() - time
archive <- llply(dirs, function(dir) { archive <- llply(dirs, function(dir) {
files <- list.files(dir, recursive = FALSE, full.names = TRUE, pattern = "*.tar.gz") files <- list.files(
dir,
recursive = FALSE,
full.names = TRUE,
pattern = "*.tar.gz"
)
if (length(files) == 0) { if (length(files) == 0) {
print(paste0("Error: Empty directory: ", dir)) print(paste0("Error: Empty directory: ", dir))
return(NULL) return(NULL)
} }
info <- file.info(files) info <- file.info(files)
tryCatch({ tryCatch(
{
rownames(info) <- paste0(basename(dirname(files)), "/", basename(files)) rownames(info) <- paste0(basename(dirname(files)), "/", basename(files))
}, error = function(e) { },
error = function(e) {
print(paste0("Error: Exception catched for Archived directory: ", dir)) print(paste0("Error: Exception catched for Archived directory: ", dir))
print(e) print(e)
return(NULL) return(NULL)
}) }
)
info info
}) })
tryCatch({ tryCatch(
{
rownames(info) <- paste0(basename(dirname(files)), "/", basename(files)) rownames(info) <- paste0(basename(dirname(files)), "/", basename(files))
}, error = function(e) { },
error = function(e) {
print(paste0("Error: Exception catched for Archived directory: ", dir)) print(paste0("Error: Exception catched for Archived directory: ", dir))
print(e) print(e)
return(NULL) return(NULL)
}) }
)
curl::curl_download(
"https://cran.devxy.io/amd64/rhel9/latest/src/contrib/PACKAGES.db",
curl::curl_download("https://cran.devxy.io/amd64/rhel9/latest/src/contrib/PACKAGES.db", "PACKAGES.db") "PACKAGES.db"
)
con = DBI::dbConnect(RSQLite::SQLite(), "PACKAGES.db") con = DBI::dbConnect(RSQLite::SQLite(), "PACKAGES.db")
df = DBI::dbReadTable(con, "packages") df = DBI::dbReadTable(con, "packages")
@ -50,6 +66,14 @@ which(grepl("digest", df$Package))
system("cat /tmp/PACKAGES | grep '^Package: digest' | wc -l") system("cat /tmp/PACKAGES | grep '^Package: digest' | wc -l")
curl::curl_fetch_memory("https://cloud.r-project.org/src/contrib/Meta/archive.rds") curl::curl_fetch_memory(
remotes = readRDS(url("https://cloud.r-project.org/src/contrib/Meta/archive.rds", "rb")) "https://cloud.r-project.org/src/contrib/Meta/archive.rds"
packages = readRDS(url("https://cran.devxy.io/arm64/noble/latest/src/contrib/PACKAGES.rds", "rb")) )
remotes = readRDS(url(
"https://cloud.r-project.org/src/contrib/Meta/archive.rds",
"rb"
))
packages = readRDS(url(
"https://cran.devxy.io/arm64/noble/latest/src/contrib/PACKAGES.rds",
"rb"
))

View file

@ -37,10 +37,13 @@ future.apply::future_lapply(
files <- s3fs::s3_dir_ls( files <- s3fs::s3_dir_ls(
sprintf( sprintf(
"devxy-r-package-binaries-hel1/%s/%s/latest/src/contrib", "devxy-r-package-binaries-hel1/%s/%s/latest/src/contrib",
arch, codename arch,
codename
) )
) )
if (length(files) == 0) return(NULL) if (length(files) == 0) {
return(NULL)
}
files_b <- basename(files) files_b <- basename(files)
pkg_names <- sapply(strsplit(files_b, "_"), `[`, 1) pkg_names <- sapply(strsplit(files_b, "_"), `[`, 1)
dupes <- unique(pkg_names[duplicated(pkg_names)]) dupes <- unique(pkg_names[duplicated(pkg_names)])

View file

@ -7,9 +7,15 @@ arch <- Sys.getenv("ARCH")
token <- Sys.getenv("FORGEJO_TOKEN") token <- Sys.getenv("FORGEJO_TOKEN")
output_file <- Sys.getenv("REBUILD_PKG_LIST", "/tmp/rebuild_pkgs.txt") output_file <- Sys.getenv("REBUILD_PKG_LIST", "/tmp/rebuild_pkgs.txt")
if (nchar(platform) == 0) stop("PLATFORM env var is not set") if (nchar(platform) == 0) {
if (nchar(arch) == 0) stop("ARCH env var is not set") stop("PLATFORM env var is not set")
if (nchar(token) == 0) stop("FORGEJO_TOKEN env var is not set") }
if (nchar(arch) == 0) {
stop("ARCH env var is not set")
}
if (nchar(token) == 0) {
stop("FORGEJO_TOKEN env var is not set")
}
os_family <- if (grepl("^ubuntu", platform)) { os_family <- if (grepl("^ubuntu", platform)) {
"Ubuntu" "Ubuntu"
@ -21,12 +27,16 @@ os_family <- if (grepl("^ubuntu", platform)) {
platform platform
} }
issue_title <- sprintf("Missing package binaries for latest version (%s)", os_family) issue_title <- sprintf(
"Missing package binaries for latest version (%s)",
os_family
)
cat(sprintf("Searching for issue: %s\n", issue_title)) cat(sprintf("Searching for issue: %s\n", issue_title))
search_url <- sprintf( search_url <- sprintf(
"%s/repos/%s/issues?type=issues&state=open&q=%s&limit=50", "%s/repos/%s/issues?type=issues&state=open&q=%s&limit=50",
forgejo_base, repo, forgejo_base,
repo,
utils::URLencode(issue_title, reserved = TRUE) utils::URLencode(issue_title, reserved = TRUE)
) )
search_resp <- request(search_url) |> search_resp <- request(search_url) |>
@ -34,7 +44,9 @@ search_resp <- request(search_url) |>
req_perform() req_perform()
issues <- resp_body_json(search_resp, simplifyVector = FALSE) issues <- resp_body_json(search_resp, simplifyVector = FALSE)
match_idx <- which(vapply(issues, function(x) x$title, character(1)) == issue_title) match_idx <- which(
vapply(issues, function(x) x$title, character(1)) == issue_title
)
if (length(match_idx) == 0) { if (length(match_idx) == 0) {
cat("No matching issue found - nothing to rebuild\n") cat("No matching issue found - nothing to rebuild\n")
@ -55,7 +67,10 @@ lines <- strsplit(body, "\n", fixed = TRUE)[[1]]
plat_header <- sprintf("## %s", platform) plat_header <- sprintf("## %s", platform)
plat_idx <- which(lines == plat_header) plat_idx <- which(lines == plat_header)
if (length(plat_idx) == 0) { if (length(plat_idx) == 0) {
cat(sprintf("No section found for platform %s - nothing to rebuild\n", platform)) cat(sprintf(
"No section found for platform %s - nothing to rebuild\n",
platform
))
writeLines(character(0), output_file) writeLines(character(0), output_file)
q("no") q("no")
} }
@ -64,7 +79,11 @@ if (length(plat_idx) == 0) {
arch_pattern <- sprintf("^### %s ", arch) arch_pattern <- sprintf("^### %s ", arch)
arch_idx <- which(grepl(arch_pattern, lines) & seq_along(lines) > plat_idx[1]) arch_idx <- which(grepl(arch_pattern, lines) & seq_along(lines) > plat_idx[1])
if (length(arch_idx) == 0) { if (length(arch_idx) == 0) {
cat(sprintf("No section found for arch %s under %s - nothing to rebuild\n", arch, platform)) cat(sprintf(
"No section found for arch %s under %s - nothing to rebuild\n",
arch,
platform
))
writeLines(character(0), output_file) writeLines(character(0), output_file)
q("no") q("no")
} }
@ -82,6 +101,11 @@ pkg_lines <- section_lines[grepl("^- ", section_lines)]
pkgs <- sub("^- ([^ ]+) \\(.*\\)$", "\\1", pkg_lines) pkgs <- sub("^- ([^ ]+) \\(.*\\)$", "\\1", pkg_lines)
pkgs <- pkgs[nchar(pkgs) > 0 & pkgs != "_None_"] pkgs <- pkgs[nchar(pkgs) > 0 & pkgs != "_None_"]
cat(sprintf("Found %d rebuildable packages for %s/%s\n", length(pkgs), platform, arch)) cat(sprintf(
"Found %d rebuildable packages for %s/%s\n",
length(pkgs),
platform,
arch
))
writeLines(pkgs, output_file) writeLines(pkgs, output_file)
cat(sprintf("Wrote package list to %s\n", output_file)) cat(sprintf("Wrote package list to %s\n", output_file))

View file

@ -6,9 +6,13 @@ archived <- quickcode::archivedPkg() |>
pull(name) pull(name)
# Query all distinct pkgs in the DB # Query all distinct pkgs in the DB
con <- DBI::dbConnect(RPostgres::Postgres(), con <- DBI::dbConnect(
dbname = "build_metadata", host = "r-binaries.devxy.io", RPostgres::Postgres(),
port = 15432, user = "r_binaries", password = Sys.getenv("PGPASS"), dbname = "build_metadata",
host = "r-binaries.devxy.io",
port = 15432,
user = "r_binaries",
password = Sys.getenv("PGPASS"),
sslmode = "require" sslmode = "require"
) )
@ -22,7 +26,9 @@ to_process <- pkgs_db[pkgs_db %in% archived]
# for all matches, set 'removed = TRUE' # for all matches, set 'removed = TRUE'
sapply(to_process, function(.x) { sapply(to_process, function(.x) {
DBI::dbExecute(con, "UPDATE single_builds SET removed = 'TRUE' where name = $1", DBI::dbExecute(
con,
"UPDATE single_builds SET removed = 'TRUE' where name = $1",
params = list(.x) params = list(.x)
) )
}) })

View file

@ -3,22 +3,34 @@ library(bincraft)
library(dplyr) library(dplyr)
library(DBI) library(DBI)
con <- DBI::dbConnect(RPostgres::Postgres(), con <- DBI::dbConnect(
dbname = "build_metadata", host = "r-binaries.devxy.io", RPostgres::Postgres(),
port = 15432, user = "r_binaries", password = Sys.getenv("PGPASS"), dbname = "build_metadata",
host = "r-binaries.devxy.io",
port = 15432,
user = "r_binaries",
password = Sys.getenv("PGPASS"),
sslmode = "require" sslmode = "require"
) )
`%nin%` <- Negate(`%in%`) `%nin%` <- Negate(`%in%`)
new_packages <- get_new_cran_packages(lubridate::interval(lubridate::today(), lubridate::today() - 2))$name new_packages <- get_new_cran_packages(lubridate::interval(
removed_pkgs <- get_removed_cran_packages(lubridate::interval(lubridate::today(), lubridate::today() - 2))$name lubridate::today(),
lubridate::today() - 2
))$name
removed_pkgs <- get_removed_cran_packages(lubridate::interval(
lubridate::today(),
lubridate::today() - 2
))$name
# filter windows packages # filter windows packages
# filter new packages from the last X days # filter new packages from the last X days
cran_pkgs <- unique(tools::CRAN_package_db() |> cran_pkgs <- unique(
tools::CRAN_package_db() |>
filter(`OS_type` != "windows" | is.na(`OS_type`)) |> filter(`OS_type` != "windows" | is.na(`OS_type`)) |>
filter(`Package` %nin% new_packages) |> filter(`Package` %nin% new_packages) |>
pull(Package)) pull(Package)
)
arch <- "arm64" arch <- "arm64"
platform <- "redhat-8" platform <- "redhat-8"
@ -28,13 +40,24 @@ platform <- "alpine-321"
platform <- "ubuntu-2204" platform <- "ubuntu-2204"
platform <- "ubuntu-2404" platform <- "ubuntu-2404"
data <- dbGetQuery(con, "SELECT name FROM single_builds WHERE platform = $1 AND arch = $2 and removed = FALSE;", params = list(platform, arch)) data <- dbGetQuery(
removed_pkgs = get_removed_cran_packages(lubridate::interval(lubridate::today(), lubridate::today() - 2))$name con,
"SELECT name FROM single_builds WHERE platform = $1 AND arch = $2 and removed = FALSE;",
params = list(platform, arch)
)
removed_pkgs = get_removed_cran_packages(lubridate::interval(
lubridate::today(),
lubridate::today() - 2
))$name
pkgs_db <- unique(data$name) pkgs_db <- unique(data$name)
pkgs_db = setdiff(pkgs_db, removed_pkgs) pkgs_db = setdiff(pkgs_db, removed_pkgs)
pkgs <- setdiff(cran_pkgs, pkgs_db) pkgs <- setdiff(cran_pkgs, pkgs_db)
formatted_pkgs <- gsub('"', "'", capture.output(dput(as.character(na.omit(pkgs[1:length(pkgs)]))))) formatted_pkgs <- gsub(
'"',
"'",
capture.output(dput(as.character(na.omit(pkgs[1:length(pkgs)]))))
)
formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ") formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ")
cat(formatted_pkgs_one_line, "\n", sep = "") cat(formatted_pkgs_one_line, "\n", sep = "")
@ -44,7 +67,6 @@ clipr::write_clip(formatted_pkgs_one_line)
# sapply(pkgs, function(x) which(grepl(sprintf("^%s$", x), cran_pkgs))) # sapply(pkgs, function(x) which(grepl(sprintf("^%s$", x), cran_pkgs)))
s3fs::s3_file_system( s3fs::s3_file_system(
aws_access_key_id = Sys.getenv("HETZNER_S3_ACCESS_KEY_K3S"), aws_access_key_id = Sys.getenv("HETZNER_S3_ACCESS_KEY_K3S"),
aws_secret_access_key = Sys.getenv("HETZNER_S3_SECRET_KEY_K3S"), aws_secret_access_key = Sys.getenv("HETZNER_S3_SECRET_KEY_K3S"),

View file

@ -10,17 +10,37 @@ s3fs::s3_file_system(
) )
s3fs::s3_dir_ls("s3://devxy-r-package-binaries") s3fs::s3_dir_ls("s3://devxy-r-package-binaries")
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/jammy/latest/src/contrib")) # done files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib")) # done "devxy-r-package-binaries-hel1/amd64/jammy/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib")) # done )) # done
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib")) # done files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib")) # done "devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib")) # done files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib")) # done "devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib")) # done )) # done
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib")) # done files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib")) "devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib"
)) # done
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib"
))
files_b = basename(files) files_b = basename(files)
files_b_split = sapply(strsplit(basename(files_b), "_"), function(x) x[1]) files_b_split = sapply(strsplit(basename(files_b), "_"), function(x) x[1])
@ -30,7 +50,11 @@ cran_pkgs <- tools::CRAN_package_db() |>
pull(Package) pull(Package)
pkgs = setdiff(cran_pkgs, files_b_split) pkgs = setdiff(cran_pkgs, files_b_split)
formatted_pkgs <- gsub('"', "'", capture.output(dput(as.character(na.omit(pkgs[1:900]))))) formatted_pkgs <- gsub(
'"',
"'",
capture.output(dput(as.character(na.omit(pkgs[1:900]))))
)
formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ") formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ")
cat(formatted_pkgs_one_line, "\n", sep = "") cat(formatted_pkgs_one_line, "\n", sep = "")

View file

@ -7,42 +7,109 @@ s3fs::s3_file_system(
region_name = "hel1", region_name = "hel1",
) )
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/jammy/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib")) "devxy-r-package-binaries-hel1/amd64/jammy/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib")) ))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib")) "devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib")) "devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib")) ))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib")) "devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib"
))
files_b = basename(files) files_b = basename(files)
files_b_split = sapply(strsplit(basename(files_b), "_"), function(x) x[1]) files_b_split = sapply(strsplit(basename(files_b), "_"), function(x) x[1])
files_b_split = setdiff(files_b_split, c("PACKAGES", "PACKAGES.gz", "PACKAGES.rds", "PACKAGES.db", "Archive")) files_b_split = setdiff(
files_b_split,
c("PACKAGES", "PACKAGES.gz", "PACKAGES.rds", "PACKAGES.db", "Archive")
)
# windows-only # windows-only
files_b_split = setdiff(files_b_split, c("PACKAGES", "PACKAGES.gz", "PACKAGES.rds", "PACKAGES.db", "Archive", "RInno", "KeyboardSimulator", "R2PPT", "RWinEdt", "blatr", "excel.link", "spectrino", "taskscheduleR", "MDSGUI", "BiplotGUI", "R2wd")) files_b_split = setdiff(
pkgs_index = available.packages("https://cran.devxy.io/amd64/jammy/latest/src/contrib")[, "Package"] files_b_split,
c(
"PACKAGES",
"PACKAGES.gz",
"PACKAGES.rds",
"PACKAGES.db",
"Archive",
"RInno",
"KeyboardSimulator",
"R2PPT",
"RWinEdt",
"blatr",
"excel.link",
"spectrino",
"taskscheduleR",
"MDSGUI",
"BiplotGUI",
"R2wd"
)
)
pkgs_index = available.packages(
"https://cran.devxy.io/amd64/jammy/latest/src/contrib"
)[, "Package"]
pkgs_index_b = basename(pkgs_index) pkgs_index_b = basename(pkgs_index)
if (length(files_b_split) != length(pkgs_index_b)) { if (length(files_b_split) != length(pkgs_index_b)) {
pkgs_missing = setdiff(files_b_split, pkgs_index_b) pkgs_missing = setdiff(files_b_split, pkgs_index_b)
message("Packages missing in index but present in S3:") message("Packages missing in index but present in S3:")
pkgs_missing pkgs_missing
formatted_pkgs <- gsub('"', "'", capture.output(dput(as.character(na.omit(pkgs_missing[1:900]))))) formatted_pkgs <- gsub(
'"',
"'",
capture.output(dput(as.character(na.omit(pkgs_missing[1:900]))))
)
formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ") formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ")
cat(formatted_pkgs_one_line, "\n", sep = "") cat(formatted_pkgs_one_line, "\n", sep = "")
} }
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib")) "devxy-r-package-binaries-hel1/amd64/noble/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib")) ))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel8/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/rhel9/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/amd64/alpine320/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib")) "devxy-r-package-binaries-hel1/arm64/jammy/latest/src/contrib"
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib")) ))
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib")) files <- s3fs::s3_dir_ls(sprintf(
files <- s3fs::s3_dir_ls(sprintf("devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib")) "devxy-r-package-binaries-hel1/arm64/noble/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel8/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/rhel9/latest/src/contrib"
))
files <- s3fs::s3_dir_ls(sprintf(
"devxy-r-package-binaries-hel1/arm64/alpine320/latest/src/contrib"
))

View file

@ -1,9 +1,13 @@
### Lists packages without any successful binaries, i.e. pkgs for which all builds errored (per platform & arch) ### Lists packages without any successful binaries, i.e. pkgs for which all builds errored (per platform & arch)
library(DBI) library(DBI)
library(dplyr) library(dplyr)
con <- DBI::dbConnect(RPostgres::Postgres(), con <- DBI::dbConnect(
dbname = "build_metadata", host = "r-binaries.devxy.io", RPostgres::Postgres(),
port = 15432, user = "r_binaries", password = Sys.getenv("PGPASS"), dbname = "build_metadata",
host = "r-binaries.devxy.io",
port = 15432,
user = "r_binaries",
password = Sys.getenv("PGPASS"),
sslmode = "require" sslmode = "require"
) )
@ -11,7 +15,10 @@ cran_pkgs <- tools::CRAN_package_db() |>
filter(`Date/Publication` <= "2024-12-02") |> filter(`Date/Publication` <= "2024-12-02") |>
pull(Package) pull(Package)
data <- dbGetQuery(con, "SELECT name,platform,arch,removed,error_occurred FROM single_builds;") data <- dbGetQuery(
con,
"SELECT name,platform,arch,removed,error_occurred FROM single_builds;"
)
pkgs <- data |> pkgs <- data |>
filter(platform == "alpine-320", arch == "arm64") |> filter(platform == "alpine-320", arch == "arm64") |>
@ -22,7 +29,11 @@ pkgs <- data |>
pkgs = setdiff(cran_pkgs, pkgs) pkgs = setdiff(cran_pkgs, pkgs)
# Format, remove line breaks, and print as a single line # Format, remove line breaks, and print as a single line
formatted_pkgs <- gsub('"', "'", capture.output(dput(as.character(na.omit(pkgs[1:900]))))) formatted_pkgs <- gsub(
'"',
"'",
capture.output(dput(as.character(na.omit(pkgs[1:900]))))
)
formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ") formatted_pkgs_one_line <- paste(formatted_pkgs, collapse = " ")

View file

@ -17,8 +17,12 @@ library(httr2, quietly = TRUE)
platform <- Sys.getenv("PLATFORM") platform <- Sys.getenv("PLATFORM")
arch <- Sys.getenv("ARCH") arch <- Sys.getenv("ARCH")
if (nchar(platform) == 0) stop("PLATFORM env var is not set") if (nchar(platform) == 0) {
if (nchar(arch) == 0) stop("ARCH env var is not set") stop("PLATFORM env var is not set")
}
if (nchar(arch) == 0) {
stop("ARCH env var is not set")
}
# Map platform names to S3 codenames # Map platform names to S3 codenames
s3_codename <- gsub("-", "", platform) s3_codename <- gsub("-", "", platform)
@ -37,8 +41,13 @@ os_family <- if (grepl("^ubuntu", platform)) {
platform platform
} }
cat(sprintf("Platform: %s | Arch: %s | S3 codename: %s | OS family: %s\n", cat(sprintf(
platform, arch, s3_codename, os_family)) "Platform: %s | Arch: %s | S3 codename: %s | OS family: %s\n",
platform,
arch,
s3_codename,
os_family
))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 1. Query PostgreSQL for known build failures (before s3fs init to avoid # 1. Query PostgreSQL for known build failures (before s3fs init to avoid
@ -59,7 +68,8 @@ errored_pkgs <- DBI::dbGetQuery(
con, con,
sprintf( sprintf(
"SELECT name, tag FROM single_builds WHERE error_occurred = TRUE AND platform = '%s' AND arch = '%s'", "SELECT name, tag FROM single_builds WHERE error_occurred = TRUE AND platform = '%s' AND arch = '%s'",
platform, arch platform,
arch
) )
) )
DBI::dbDisconnect(con) DBI::dbDisconnect(con)
@ -69,7 +79,12 @@ if (nrow(errored_dt) > 0) {
setnames(errored_dt, c("Package", "Version")) setnames(errored_dt, c("Package", "Version"))
setkey(errored_dt, Package, Version) setkey(errored_dt, Package, Version)
} }
cat(sprintf("Found %d known build failures for %s/%s\n", nrow(errored_dt), platform, arch)) cat(sprintf(
"Found %d known build failures for %s/%s\n",
nrow(errored_dt),
platform,
arch
))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 2. CRAN release packages (uses curl internally - must come after PG) # 2. CRAN release packages (uses curl internally - must come after PG)
@ -93,13 +108,21 @@ s3fs::s3_file_system(
refresh = TRUE refresh = TRUE
) )
s3_path <- sprintf("devxy-rpkgs-binaries/%s/%s/latest/src/contrib", arch, s3_codename) s3_path <- sprintf(
"devxy-rpkgs-binaries/%s/%s/latest/src/contrib",
arch,
s3_codename
)
cat(sprintf("Listing S3 path: %s\n", s3_path)) cat(sprintf("Listing S3 path: %s\n", s3_path))
s3_pkgs <- tryCatch( s3_pkgs <- tryCatch(
s3fs::s3_dir_ls(s3_path, recurse = FALSE), s3fs::s3_dir_ls(s3_path, recurse = FALSE),
error = function(e) { error = function(e) {
cat(sprintf("WARNING: Could not list S3 path %s: %s\n", s3_path, conditionMessage(e))) cat(sprintf(
"WARNING: Could not list S3 path %s: %s\n",
s3_path,
conditionMessage(e)
))
character(0) character(0)
} }
) )
@ -117,7 +140,12 @@ if (length(parts) == 0) {
) )
} }
cat(sprintf("S3 contains %d tarballs for %s/%s\n", nrow(s3_dt), arch, s3_codename)) cat(sprintf(
"S3 contains %d tarballs for %s/%s\n",
nrow(s3_dt),
arch,
s3_codename
))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 4. Find missing packages (CRAN release version not in S3) # 4. Find missing packages (CRAN release version not in S3)
@ -132,9 +160,15 @@ cat(sprintf("%d CRAN release packages missing from S3\n", nrow(missing_dt)))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
script_dir <- tryCatch( script_dir <- tryCatch(
dirname(normalizePath( dirname(normalizePath(
if (exists("ofile", envir = sys.frame(1), inherits = FALSE)) sys.frame(1)$ofile if (exists("ofile", envir = sys.frame(1), inherits = FALSE)) {
else commandArgs(trailingOnly = FALSE)[grepl("--file=", commandArgs(trailingOnly = FALSE))] |> sys.frame(1)$ofile
sub("--file=", "", x = _), } else {
commandArgs(trailingOnly = FALSE)[grepl(
"--file=",
commandArgs(trailingOnly = FALSE)
)] |>
sub("--file=", "", x = _)
},
mustWork = FALSE mustWork = FALSE
)), )),
error = function(e) "local" error = function(e) "local"
@ -145,11 +179,21 @@ excluded_pkgs <- data.table(package = character(0), reason = character(0))
if (file.exists(excluded_path)) { if (file.exists(excluded_path)) {
excluded_raw <- jsonlite::fromJSON(excluded_path) excluded_raw <- jsonlite::fromJSON(excluded_path)
excluded_pkgs <- as.data.table(excluded_raw) excluded_pkgs <- as.data.table(excluded_raw)
cat(sprintf("Loaded %d excluded packages from %s\n", nrow(excluded_pkgs), excluded_path)) cat(sprintf(
"Loaded %d excluded packages from %s\n",
nrow(excluded_pkgs),
excluded_path
))
missing_dt <- missing_dt[!Package %in% excluded_pkgs$package] missing_dt <- missing_dt[!Package %in% excluded_pkgs$package]
cat(sprintf("%d packages remain after removing exclusions\n", nrow(missing_dt))) cat(sprintf(
"%d packages remain after removing exclusions\n",
nrow(missing_dt)
))
} else { } else {
cat(sprintf("No excluded-packages.json found at %s -- skipping exclusion step\n", excluded_path)) cat(sprintf(
"No excluded-packages.json found at %s -- skipping exclusion step\n",
excluded_path
))
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -163,18 +207,28 @@ if (nrow(errored_dt) > 0) {
rebuildable_dt <- missing_dt rebuildable_dt <- missing_dt
} }
cat(sprintf("Rebuildable: %d | Known failures: %d\n", cat(sprintf(
nrow(rebuildable_dt), nrow(known_failures_dt))) "Rebuildable: %d | Known failures: %d\n",
nrow(rebuildable_dt),
nrow(known_failures_dt)
))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 7. Write rebuildable package names to cache RDS # 7. Write rebuildable package names to cache RDS
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
cache_dir <- "/mnt/cache/packages" cache_dir <- "/mnt/cache/packages"
cache_file <- file.path(cache_dir, sprintf("weekly_rebuild_%s_%s.rds", platform, arch)) cache_file <- file.path(
cache_dir,
sprintf("weekly_rebuild_%s_%s.rds", platform, arch)
)
if (dir.exists(cache_dir)) { if (dir.exists(cache_dir)) {
saveRDS(rebuildable_dt$Package, cache_file) saveRDS(rebuildable_dt$Package, cache_file)
cat(sprintf("Wrote %d rebuildable packages to %s\n", nrow(rebuildable_dt), cache_file)) cat(sprintf(
"Wrote %d rebuildable packages to %s\n",
nrow(rebuildable_dt),
cache_file
))
} else { } else {
cat(sprintf("Cache dir %s does not exist -- skipping RDS write\n", cache_dir)) cat(sprintf("Cache dir %s does not exist -- skipping RDS write\n", cache_dir))
} }
@ -186,29 +240,47 @@ forgejo_token <- Sys.getenv("FORGEJO_TOKEN")
if (nchar(forgejo_token) == 0) { if (nchar(forgejo_token) == 0) {
cat("FORGEJO_TOKEN not set -- skipping issue update\n") cat("FORGEJO_TOKEN not set -- skipping issue update\n")
} else { } else {
issue_title <- sprintf("Missing package binaries for latest version (%s)", os_family) issue_title <- sprintf(
"Missing package binaries for latest version (%s)",
os_family
)
n_missing <- nrow(missing_dt) n_missing <- nrow(missing_dt)
n_rebuild <- nrow(rebuildable_dt) n_rebuild <- nrow(rebuildable_dt)
arch_lines <- sprintf("### %s (%d missing, %d to rebuild)", arch, n_missing, n_rebuild) arch_lines <- sprintf(
"### %s (%d missing, %d to rebuild)",
arch,
n_missing,
n_rebuild
)
if (nrow(rebuildable_dt) > 0) { if (nrow(rebuildable_dt) > 0) {
arch_lines <- c(arch_lines, arch_lines <- c(
arch_lines,
paste0("- ", rebuildable_dt$Package, " (", rebuildable_dt$Version, ")") paste0("- ", rebuildable_dt$Package, " (", rebuildable_dt$Version, ")")
) )
} else { } else {
arch_lines <- c(arch_lines, "_None_") arch_lines <- c(arch_lines, "_None_")
} }
if (nrow(known_failures_dt) > 0) { if (nrow(known_failures_dt) > 0) {
arch_lines <- c(arch_lines, arch_lines <- c(
arch_lines,
"", "",
"#### Known build failures", "#### Known build failures",
paste0("- ", known_failures_dt$Package, " (", known_failures_dt$Version, ")") paste0(
"- ",
known_failures_dt$Package,
" (",
known_failures_dt$Version,
")"
)
) )
} }
build_excluded_footer <- function() { build_excluded_footer <- function() {
if (nrow(excluded_pkgs) == 0) return(character(0)) if (nrow(excluded_pkgs) == 0) {
return(character(0))
}
entries <- paste( entries <- paste(
paste0(excluded_pkgs$package, " (", excluded_pkgs$reason, ")"), paste0(excluded_pkgs$package, " (", excluded_pkgs$reason, ")"),
collapse = ", " collapse = ", "
@ -221,7 +293,8 @@ if (nchar(forgejo_token) == 0) {
search_url <- sprintf( search_url <- sprintf(
"%s/repos/%s/issues?type=issues&state=open&q=%s&limit=50", "%s/repos/%s/issues?type=issues&state=open&q=%s&limit=50",
forgejo_base, repo, forgejo_base,
repo,
utils::URLencode(issue_title, reserved = TRUE) utils::URLencode(issue_title, reserved = TRUE)
) )
search_resp <- httr2::request(search_url) |> search_resp <- httr2::request(search_url) |>
@ -230,7 +303,9 @@ if (nchar(forgejo_token) == 0) {
existing_issues <- httr2::resp_body_json(search_resp, simplifyVector = FALSE) existing_issues <- httr2::resp_body_json(search_resp, simplifyVector = FALSE)
match_idx <- which(sapply(existing_issues, function(x) x$title) == issue_title) match_idx <- which(
sapply(existing_issues, function(x) x$title) == issue_title
)
today_str <- format(Sys.Date(), "%Y-%m-%d") today_str <- format(Sys.Date(), "%Y-%m-%d")
@ -238,7 +313,9 @@ if (nchar(forgejo_token) == 0) {
# ---- Update existing issue ---- # ---- Update existing issue ----
issue_number <- existing_issues[[match_idx[1]]]$number issue_number <- existing_issues[[match_idx[1]]]$number
old_body <- existing_issues[[match_idx[1]]]$body old_body <- existing_issues[[match_idx[1]]]$body
if (is.null(old_body)) old_body <- "" if (is.null(old_body)) {
old_body <- ""
}
lines <- strsplit(old_body, "\n", fixed = TRUE)[[1]] lines <- strsplit(old_body, "\n", fixed = TRUE)[[1]]
@ -258,7 +335,11 @@ if (nchar(forgejo_token) == 0) {
if (length(plat_idx) == 0) { if (length(plat_idx) == 0) {
# Platform section missing -- insert before --- footer # Platform section missing -- insert before --- footer
footer_idx <- which(lines == "---") footer_idx <- which(lines == "---")
insert_at <- if (length(footer_idx) > 0) footer_idx[length(footer_idx)] else length(lines) + 1 insert_at <- if (length(footer_idx) > 0) {
footer_idx[length(footer_idx)]
} else {
length(lines) + 1
}
new_plat_block <- c(sprintf("## %s", platform), "", arch_lines, "") new_plat_block <- c(sprintf("## %s", platform), "", arch_lines, "")
lines <- c( lines <- c(
@ -271,7 +352,11 @@ if (nchar(forgejo_token) == 0) {
# End of platform section: next ## or --- at a higher level, or EOF # End of platform section: next ## or --- at a higher level, or EOF
next_section <- which(grepl("^## |^---", lines) & seq_along(lines) > pi) next_section <- which(grepl("^## |^---", lines) & seq_along(lines) > pi)
plat_end <- if (length(next_section) > 0) next_section[1] - 1 else length(lines) plat_end <- if (length(next_section) > 0) {
next_section[1] - 1
} else {
length(lines)
}
plat_lines <- lines[seq(pi, plat_end)] plat_lines <- lines[seq(pi, plat_end)]
arch_local_idx <- which(plat_lines == arch_header) arch_local_idx <- which(plat_lines == arch_header)
@ -288,7 +373,9 @@ if (nchar(forgejo_token) == 0) {
ai <- pi + arch_local_idx[1] - 1 # absolute line index ai <- pi + arch_local_idx[1] - 1 # absolute line index
# End of arch subsection # End of arch subsection
next_arch <- which(grepl("^### |^## |^---", lines) & seq_along(lines) > ai) next_arch <- which(
grepl("^### |^## |^---", lines) & seq_along(lines) > ai
)
arch_end <- if (length(next_arch) > 0) next_arch[1] - 1 else plat_end arch_end <- if (length(next_arch) > 0) next_arch[1] - 1 else plat_end
lines <- c( lines <- c(
@ -303,7 +390,11 @@ if (nchar(forgejo_token) == 0) {
excl_hdr_idx <- which(lines == "## Excluded packages") excl_hdr_idx <- which(lines == "## Excluded packages")
if (length(excl_hdr_idx) > 0) { if (length(excl_hdr_idx) > 0) {
pre_dash <- which(lines == "---" & seq_along(lines) < excl_hdr_idx[1]) pre_dash <- which(lines == "---" & seq_along(lines) < excl_hdr_idx[1])
remove_from <- if (length(pre_dash) > 0) pre_dash[length(pre_dash)] else excl_hdr_idx[1] remove_from <- if (length(pre_dash) > 0) {
pre_dash[length(pre_dash)]
} else {
excl_hdr_idx[1]
}
lines <- lines[seq_len(remove_from - 1)] lines <- lines[seq_len(remove_from - 1)]
} }
footer <- build_excluded_footer() footer <- build_excluded_footer()
@ -313,7 +404,12 @@ if (nchar(forgejo_token) == 0) {
new_body <- paste(lines, collapse = "\n") new_body <- paste(lines, collapse = "\n")
patch_url <- sprintf("%s/repos/%s/issues/%d", forgejo_base, repo, issue_number) patch_url <- sprintf(
"%s/repos/%s/issues/%d",
forgejo_base,
repo,
issue_number
)
httr2::request(patch_url) |> httr2::request(patch_url) |>
httr2::req_headers( httr2::req_headers(
Authorization = paste("token", forgejo_token), Authorization = paste("token", forgejo_token),
@ -324,7 +420,6 @@ if (nchar(forgejo_token) == 0) {
httr2::req_perform() httr2::req_perform()
cat(sprintf("Updated Forgejo issue #%d: %s\n", issue_number, issue_title)) cat(sprintf("Updated Forgejo issue #%d: %s\n", issue_number, issue_title))
} else { } else {
# ---- Create new issue ---- # ---- Create new issue ----
body_lines <- c( body_lines <- c(
@ -352,7 +447,11 @@ if (nchar(forgejo_token) == 0) {
httr2::req_perform() httr2::req_perform()
new_issue <- httr2::resp_body_json(create_resp) new_issue <- httr2::resp_body_json(create_resp)
cat(sprintf("Created Forgejo issue #%d: %s\n", new_issue$number, issue_title)) cat(sprintf(
"Created Forgejo issue #%d: %s\n",
new_issue$number,
issue_title
))
} }
} }

View file

@ -1,11 +1,7 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": ["local>devxy/renovate-config"],
"local>devxy/renovate-config" "ignorePaths": ["docker/**"],
],
"ignorePaths": [
"docker/**"
],
"customManagers": [ "customManagers": [
{ {
"customType": "regex", "customType": "regex",