diff --git a/.crow/build-alpine-321-arm64-single.yaml b/.crow/build-alpine-321-arm64-single.yaml index c9bce04..61db0db 100644 --- a/.crow/build-alpine-321-arm64-single.yaml +++ b/.crow/build-alpine-321-arm64-single.yaml @@ -1,12 +1,8 @@ when: - event: manual - evaluate: 'build == "all"' - # - event: manual - # evaluate: 'build == "alpine-321-arm64"' - - event: manual - evaluate: 'build == "alpine-321-arm64-0"' - - event: manual - evaluate: 'build == "all-arm64"' + evaluate: 'task == "build-single-alpine321-arm64"' + # package: + # tag: # set NULL to build all versions skip_clone: true @@ -34,74 +30,29 @@ steps: R_LIBS_USER: /mnt/cache/R-pkgs CCACHE_DIR: /mnt/cache/ccache PLATFORM: alpine-321 - BLOCK_START: 17601 - BLOCK_END: 19800 ARCH: arm64 - NCPUS: 2 + NCPUS: 4 STRATEGY: sequential - # volumes: - # - arm64-binaries-r-dep-cache-alpine-321:/mnt/cache commands: - git clone -q https://pat-s:$$GIT_RO_TOKEN@git.devxy.io/devxy/build-binaries.git . + - rm -rf /mnt/cache/R-pkgs/00LOCK-r-pkg-binaries /mnt/cache/R-pkgs/00LOCK-bincraftR /mnt/cache/R-pkgs/00LOCK-pak /mnt/cache/pkgcache/R/pkgcache /mnt/cache/pkgcache/R/pkgcache /mnt/cache/R-pkgs/pkgcache + - R -q -e 'pak::pak("git::https://gitlab.com/devxy/r-package-binaries/bincraftr.git")' - R -q -e 'packageVersion("bincraftR")' - mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages - # Windows-only: 'RInno', 'KeyboardSimulator', 'R2PPT', 'RWinEdt', 'blatr', 'excel.link', 'spectrino', 'taskscheduleR', 'MDSGUI', 'BiplotGUI', 'R2wd', 'rFUSION', 'MediaNews' - - xvfb-run R -q -e "options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL); pkgs = c('A3', 'abmR', 'abtest', 'AccelStab', 'Achilles', 'actel', 'actxps', 'AdhereRViz', 'aIc', 'ajv', 'AlleleShift', 'almanac', 'alphashape3d', 'amadeus', 'Anthropometry', 'AntMAN', 'APCalign', 'apcf', 'aPCoA', 'apisensr', 'Apollonius', 'appeears', 'AQEval', 'AquaBEHER', 'arakno', 'arcgisgeocode', 'arcgislayers', 'archeoViz', 'archive', 'arcpbf', 'areal', 'arealDB', 'aroma.affymetrix', 'aroma.cn', 'aroma.core', 'Arothron', 'ARPALData', 'arrangements', 'artfima', 'ARUtools', 'asciicast', 'atakrig', 'atom4R', 'atpolR', 'audubon', 'AurieLSHGaussian', 'autostats', 'av', 'AVGAS', 'aws', 'aws.wrfsmn', 'AZIAD', 'babelmixr2', 'badgen', 'baguette', 'bamm', 'bangladesh', 'base64', 'basemaps', 'basf', 'basifoR', 'bate', 'BAwiR', 'BayesfMRI', 'BayesOrdDesign', 'BayesVarSel', 'BayesXsrc', 'BBEST', 'bbl', 'bcROCsurface', 'bdl', 'BeeBDC', 'behaviorchange', 'bfboinet', 'bfsMaps', 'BIEN', 'bigDM', 'bigGP', 'bigrquerystorage', 'bioclim', 'biocompute', 'BIOMASS', 'biomod2', 'biopixR', 'biosensors.usc', 'BioTIMEr', 'biplotbootGUI', 'BiplotGUI', 'birdring', 'birdscanR', 'bispdep', 'bivgeom', 'bivpois', 'blackbox', 'blackmarbler', 'blatr', 'blockCV', 'bluebike', 'bmstdr', 'BNSP', 'BoolFilter', 'brickster', 'brsim', 'BRugs', 'C50', 'CAinterprTools', 'calmate', 'CaseBasedReasoning', 'cbl', 'ClickHouseHTTP', 'clrng', 'CTRing', 'dartR', 'dartR.base', 'dartR.captive', 'dartR.popgen', 'dartR.sexlinked', 'dartR.sim', 'dartR.spatial', 'DGP4LCF', 'diffdfs', 'diffeRenTES', 'disk.frame', 'DominoDataR', 'doMPI', 'DoubleML', 'dpkg', 'ebmc', 'ecdata', 'EFAfactors', 'enerscape', 'epidm', 'EthSEQ', 'excel.link', 'facmodTS', 'fangs', 'fingerPro', 'fmf', 'foundry'); pkgs = setdiff(pkgs, c('RInno', 'KeyboardSimulator', 'R2PPT', 'RWinEdt', 'blatr', 'excel.link', 'spectrino', 'taskscheduleR', 'MDSGUI', 'BiplotGUI', 'R2wd', 'rFUSION', 'MediaNews', 'DoE.base', 'DoE.wrapper', 'FrF2', 'FrF2.catlg128', 'eha', 'gRain', 'gRbase', 'doBy', 'IDPmisc', 'RVAideMemoire', 'pbkrtest', 'seewave', 'compareGroups', 'pls', 'SNPassoc', 'spdep', 'surveillance')); progressr::handlers('cli'); progressr::handlers(global = TRUE); foo = lapply(pkgs, function(x) bincraftR::build_binary_package(x, debug = FALSE, force = TRUE, s3_endpoint = 'https://fsn1.your-objectstorage.com', s3_region = 'fsn1', s3_bucket = 'devxy-r-package-binaries', s3_access_key_id = Sys.getenv('HETZNER_S3_ACCESS_KEY_K3S'), s3_secret_access_key = Sys.getenv('HETZNER_S3_SECRET_KEY_K3S'), metadata_db_host = 'r-binaries.devxy.io', metadata_db_name = 'build_metadata', metadata_db_table = 'single_builds', metadata_db_user = 'r_binaries', metadata_db_password = Sys.getenv('PGPASS'), metadata_db_sslmode = 'require', metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE, platform = 'alpine-321'))" - ### Manual pkg builds - # webshot2, tabledown, TestAnaAPP, trekcolors, Spectran, SimNPH, RPushbullet, SimDesign, simCAT, SIAmodules, shinytest2, ShinyItemAnalysis, shinyIRT, shiny.benchmark, scDIFtest, RSP, qtkit, PROsetta, prettifyAddins, PerFit, outlierensembles, nomnoml, mxfda, MultBiplotR, mstDIF, mdsr, makepipe, lordif, kequate, jstager, jrt, irtreliability, irtQ, irtpwr, irtGUI, irtawsi, GRShiny, GPCMlasso, googletraffic, giacR, flps, flow, faoutlier, equateIRT, EnrichIntersect, EFA.dimensions, DFIT, D3mirt, ctgdist, CoTiMA, ConvertPar, bscui, BifactorIndicesCalculator, autoFC, airt: wait for https://github.com/rstudio/r-system-requirements/pull/178 - # RInno: Windows-only - # skedastic: waiting for ROI.plugin.qpoases (https://gitlab.com/roigrp/solver/ROI.plugin.qpoases/-/issues/1) - # rswipl: ccache issues: https://github.com/mgondan/rswipl/issues/3 -> solved - # Rpoppler: https://github.com/rstudio/r-system-requirements/pull/179/ - # ROracle: sysdep missing -> create rule - # ROI.plugin.qpoases: https://gitlab.com/roigrp/solver/ROI.plugin.qpoases/-/issues/1 - # ROI.plugin.cplex: checking for cplex... no -> requires propriatary software IBM Cplex - # RmecabKo: fatal error: mecab.h: No such file or directory - # rmarchingcubes: error: narrowing conversion of '-1' from 'int' to 'char' [-Wnarrowing] -> https://github.com/shwilks/rmarchingcubes/issues/3 - # RcppMeCab: /bin/bash: line 2: mecab-config: command not found -> https://github.com/rstudio/r-system-requirements/pull/180 - # Rcplex: checking for cplex... no -> requires propriatary software IBM Cplex - # Rblpapi: Unknown architecture: aarch64. Exiting. -> https://github.com/Rblp/Rblpapi/issues/399 - # Rbeast: aarch64 issue -> https://github.com/zhaokg/Rbeast/issues/29 - # ravetools: checking for FFTW... configure: error: Package requirements (fftw3) were not met: -> fftw3 rule needed - # Racmacs -> rmarchingcubes - # minic: /usr/include/c++/13/bits/stl_algo.h:5805:5: note: template argument deduction/substitution failed: - # KSgeneral -> aligned_mem.h:6:10: fatal error: mm_malloc.h: No such file or directory -> https://github.com/d-dimitrova/KSgeneral/issues/1 - # gpuR -> /usr/bin/ld: cannot find -lOpenCL: No such file or directory -> no GPU avail - # clrng -> gpuR - # BRugs: configure: error: OpenBUGS 3.2.2 or later not found. as it is only one package, not doing anything - # gamstransfer -> gdx/src/gdlib/gmsstrm.cpp:85:70: error: narrowing conversion of '-1' from 'std::char_traits::int_type' {aka 'int'} to 'char' [-Wnarrowing] -> https://github.com/GAMS-dev/transfer-r/issues/2 - # filters: -> email sent - # magickGUI, biplotbootGUI - # ymd (alpine, voi) - # tfrmtbuilder -> V8 (alpine) - # Rcmdr (alpine) - # readstata13 (alpine) - # - xvfb-run R -q -e 'options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL); pkgs = c( "RcmdrPlugin.orloca", "RcmdrPlugin.PcaRobust", "RcmdrPlugin.RiskDemo", "RcmdrPlugin.RMTCJags", "RcmdrPlugin.ROC", "RcmdrPlugin.sos" , "RcmdrPlugin.survival", "RcmdrPlugin.TeachingDemos", "RcmdrPlugin.TeachStat", "RcmdrPlugin.temis", "RcmdrPlugin.UCA", "RcmdrPlugin.WorldFlora", "streamDAG", "stan4bart", "snplinkage", "saeHB.panel.beta", "rTwig", "rswipl", "RPointCloud", "rPDBapi", "rolog", "rmcfs", "rjags", "ResultModelManager", "RcppCWB", "RcmdrPlugin.WorldFlora", "RcmdrPlugin.UCA", "RcmdrPlugin.temis", "RcmdrPlugin.TeachStat", "RcmdrPlugin.TeachingDemos", "RcmdrPlugin.survival", "RcmdrPlugin.sos", "RcmdrPlugin.ROC", "RcmdrPlugin.RMTCJags", "RcmdrPlugin.RiskDemo", "RcmdrPlugin.PcaRobust", "RclusTool", "Rbeast", "rayrender", "quanteda.textmodels", "polmineR", "plotBart", "NlinTS", "ndtv", "overtureR", "mathml", "magickGUI", "lgpr", "KappaGUI", "historicalborrow", "GSODR", "GRIDCOPULA", "googleAnalyticsR", "fluidsynth", "EventDetectGUI", "evalITR", "EBcoBART", "deeptrafo", "dbarts", "CRE", "countSTAR", "bartMan", "bartCause", "altmeta", "EthSEQ", "dartR.spatial", "dartR.sim", "gwid", "Mega2R", "priorCON", "prior3D", "MCARtest", "designmatch", "highs", "ROI.plugin.highs", "GPCMlasso"); library(bincraftR); options(progressr.enable = TRUE); foo = lapply(pkgs, function(x) build_binary_package(x, debug = FALSE, force = TRUE, s3_endpoint = 'https://fsn1.your-objectstorage.com', s3_region = 'fsn1', s3_bucket = 'devxy-r-package-binaries', s3_access_key_id = Sys.getenv('HETZNER_S3_ACCESS_KEY_K3S'), s3_secret_access_key = Sys.getenv('HETZNER_S3_SECRET_KEY_K3S'), metadata_db_host = 'r-binaries.devxy.io', metadata_db_name = 'build_metadata', metadata_db_table = 'single_builds', metadata_db_user = 'r_binaries', metadata_db_password = Sys.getenv('PGPASS'), metadata_db_sslmode = 'require', metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE, platform = "arm64"))' + - xvfb-run R -q -e "options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL, repos = structure(c(CRAN = 'https://cran.devxy.io/$ARCH/alpine321/latest'))); progressr::handlers('cli'); progressr::handlers(global = TRUE); bincraftR::build_binary_package(\"$$package\", tag = \"$$tag\", debug = FALSE, force = TRUE, s3_endpoint = 'https://fsn1.your-objectstorage.com', s3_region = 'fsn1', s3_bucket = 'devxy-r-package-binaries', s3_access_key_id = Sys.getenv('HETZNER_S3_ACCESS_KEY_K3S'), s3_secret_access_key = Sys.getenv('HETZNER_S3_SECRET_KEY_K3S'), metadata_db_host = 'r-binaries.devxy.io', metadata_db_name = 'build_metadata', metadata_db_table = 'single_builds', metadata_db_user = 'r_binaries', metadata_db_password = Sys.getenv('PGPASS'), metadata_db_sslmode = 'require', metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE, platform = 'alpine-321')" backend_options: kubernetes: resources: requests: memory: 5Gi - cpu: 1000m + cpu: 2000m limits: memory: 20Gi - cpu: 2000m + cpu: 3000m nodeSelector: kubernetes.io/arch: arm64 - node.kubernetes.io/instance-type: cax41 tolerations: - key: 'CI' operator: 'Equal' value: 'true' effect: 'NoSchedule' - - - name: 'ntfy' - image: binwiederhier/ntfy:v2.11.0 - when: - - status: [failure] - environment: - NTFY_TOKEN: - from_secret: ntfy_token - commands: | - BUILD_INDEX=$(cat build_index.txt) - ntfy publish --tags=no_entry_sign --token $NTFY_TOKEN -t "Run '${CI_REPO}' failed at $BUILD_INDEX" --click="${CI_PIPELINE_URL}" --actions="view, Open build, ${CI_PIPELINE_URL}, clear=true;" ntfy.devxy.io/ci-builds Workflow: ${CI_WORKFLOW_NAME} Commit message: ${CI_COMMIT_MESSAGE} Commit Author: ${CI_COMMIT_AUTHOR} diff --git a/.crow/build-redhat-9-amd64-single.yaml b/.crow/build-redhat-9-amd64-single.yaml index 6443d29..560e714 100644 --- a/.crow/build-redhat-9-amd64-single.yaml +++ b/.crow/build-redhat-9-amd64-single.yaml @@ -42,7 +42,7 @@ steps: - R -q -e 'packageVersion("bincraftR")' - mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages # Windows-only: 'RInno', 'KeyboardSimulator', 'R2PPT', 'RWinEdt', 'blatr', 'excel.link', 'spectrino', 'taskscheduleR', 'MDSGUI', 'BiplotGUI', 'R2wd', 'rFUSION', 'MediaNews' - - xvfb-run R -q -e "options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL, repos = structure(c(CRAN = 'https://cran.devxy.io/$ARCH/rhel9/latest'))); progressr::handlers('cli'); progressr::handlers(global = TRUE); bincraftR::build_binary_package(\"$$PACKAGE\", tag = \"$$TAG\", debug = FALSE, force = TRUE, s3_endpoint = 'https://fsn1.your-objectstorage.com', s3_region = 'fsn1', s3_bucket = 'devxy-r-package-binaries', s3_access_key_id = Sys.getenv('HETZNER_S3_ACCESS_KEY_K3S'), s3_secret_access_key = Sys.getenv('HETZNER_S3_SECRET_KEY_K3S'), metadata_db_host = 'r-binaries.devxy.io', metadata_db_name = 'build_metadata', metadata_db_table = 'single_builds', metadata_db_user = 'r_binaries', metadata_db_password = Sys.getenv('PGPASS'), metadata_db_sslmode = 'require', metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE, platform = 'redhat-9')" + - xvfb-run R -q -e "options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL, repos = structure(c(CRAN = 'https://cran.devxy.io/$ARCH/rhel9/latest'))); progressr::handlers('cli'); progressr::handlers(global = TRUE); bincraftR::build_binary_package(\"$$package\", tag = \"$$tag\", debug = FALSE, force = TRUE, s3_endpoint = 'https://fsn1.your-objectstorage.com', s3_region = 'fsn1', s3_bucket = 'devxy-r-package-binaries', s3_access_key_id = Sys.getenv('HETZNER_S3_ACCESS_KEY_K3S'), s3_secret_access_key = Sys.getenv('HETZNER_S3_SECRET_KEY_K3S'), metadata_db_host = 'r-binaries.devxy.io', metadata_db_name = 'build_metadata', metadata_db_table = 'single_builds', metadata_db_user = 'r_binaries', metadata_db_password = Sys.getenv('PGPASS'), metadata_db_sslmode = 'require', metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE, platform = 'redhat-9')" backend_options: kubernetes: resources: diff --git a/.crow/update-package-index-alpine-321-arm64.yaml b/.crow/update-package-index-alpine-321-arm64.yaml index a984f2a..365f013 100644 --- a/.crow/update-package-index-alpine-321-arm64.yaml +++ b/.crow/update-package-index-alpine-321-arm64.yaml @@ -6,8 +6,6 @@ when: - event: manual evaluate: 'update_package_index == "alpine-321-arm64"' -skip_clone: true - steps: - name: Upload PACKAGES files image: docker.io/devxygmbh/arm64-binaries-r-ubuntu-2404 @@ -31,7 +29,6 @@ steps: R_PKG_CACHE_DIR: /mnt/cache/pkgcache R_LIBS_USER: /mnt/cache/R-pkgs commands: - - git clone -q https://pat-s:$$GIT_RO_TOKEN@git.devxy.io/devxy/build-binaries.git . - rm -rf /mnt/cache/packages/* - R -q -e 'pak::pak("git::https://gitlab.com/devxy/r-package-binaries/bincraftr.git")' - R -q -e 'pak::pak("pat-s/cranlike@s3")' @@ -57,17 +54,17 @@ steps: effect: 'NoSchedule' - name: Purge CDN cache - image: curlimages/curl + image: alpine:3.21 environment: BUNNY_API_KEY: from_secret: BUNNY_API_KEY - commands: | - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" https://api.bunny.net/purge?url=https%3A%2F%2Fcran.devxy.io%2Farm64%2Falpine321%2Flatest%2Fsrc%2Fcontrib%2FPACKAGES&async=false - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" https://api.bunny.net/purge?url=https%3A%2F%2Fcran.devxy.io%2Farm64%2Falpine321%2Flatest%2Fsrc%2Fcontrib%2FPACKAGES.gz&async=false - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" https://api.bunny.net/purge?url=https%3A%2F%2Fcran.devxy.io%2Farm64%2Falpine321%2Flatest%2Fsrc%2Fcontrib%2FPACKAGES.rds&async=false - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" https://api.bunny.net/purge?url=https%3A%2F%2Fcran.devxy.io%2Farm64%2Falpine321%2Flatest%2Fsrc%2Fcontrib%2FPACKAGES.db&async=false - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" https://api.bunny.net/purge?url=https%3A%2F%2Fcran.devxy.io%2Farm64%2Falpine321%2Flatest%2Fsrc%2Fcontrib%2FPMeta%2FParchive.rds&async=false - sleep 20 # need to wait a bit to successfully deliver API calls + SUBDOMAIN1: "cran.devxy.io" + SUBDOMAIN2: "cran.allianceswisspass.devxy.io" + OS_ID: alpine321 + ARCH: arm64 + commands: + - apk add --no-cache -q bash curl + - bash scripts/purge_cdn_cache.sh "$BUNNY_API_KEY" "$ARCH" "$OS_ID" "$SUBDOMAIN1" "$SUBDOMAIN2" - name: 'ntfy' image: binwiederhier/ntfy:v2.11.0 diff --git a/.crow/update-package-index-redhat-9-amd64.yaml b/.crow/update-package-index-redhat-9-amd64.yaml index eea445b..d143ea5 100644 --- a/.crow/update-package-index-redhat-9-amd64.yaml +++ b/.crow/update-package-index-redhat-9-amd64.yaml @@ -61,56 +61,13 @@ steps: environment: BUNNY_API_KEY: from_secret: BUNNY_API_KEY - commands: | - #!/bin/bash - - # Variables for subdomains, OS identifier and architecture - SUBDOMAIN1="cran.devxy.io" - SUBDOMAIN2="cran.allianceswisspass.devxy.io" - OS_ID="rhel9" - ARCH="amd64" - - # Array of resource paths - resources=( - "src/contrib/PACKAGES" - "src/contrib/PACKAGES.gz" - "src/contrib/PACKAGES.rds" - "src/contrib/PACKAGES.db" - ) - - # Function to URL-encode a string using Python - urlencode() { - python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$$1" - } - - # Function to send the purge request - purge() { - curl -sL -X POST -H "AccessKey: $BUNNY_API_KEY" "$$1" - } - - # Function to generate the purge URLs for a given subdomain - generate_urls() { - local subdomain=$$1 - local urls=() - for resource in "${resources[@]}"; do - # Build the unencoded URL based on variables - local target_url="https://$${subdomain}/$${ARCH}/$${OS_ID}/latest/$${resource}" - # URL-encode the target URL - local encoded=$(urlencode "$$target_url") - # Construct the full API call URL - urls+=( "https://api.bunny.net/purge?url=$${encoded}&async=false" ) - done - echo "$${urls[@]}" - } - - # Generate URLs for both subdomains - urls_combined=( $$(generate_urls "$$SUBDOMAIN1") ) - allianceswisspass_urls=( $$(generate_urls "$$SUBDOMAIN2") ) - - # Purge for first subdomain and then wait - for url in "$${urls_combined[@]}"; do - purge "$$url" - done + SUBDOMAIN1: "cran.devxy.io" + SUBDOMAIN2: "cran.allianceswisspass.devxy.io" + OS_ID: rhel9 + ARCH: amd64 + commands: + - apk add --no-cache -q bash curl + - bash scripts/purge_cdn_cache.sh "$BUNNY_API_KEY" "$ARCH" "$OS_ID" "$SUBDOMAIN1" "$SUBDOMAIN2" - name: 'ntfy' image: binwiederhier/ntfy:v2.11.0 diff --git a/scripts/purge_cdn_cache.sh b/scripts/purge_cdn_cache.sh new file mode 100644 index 0000000..e6ca48f --- /dev/null +++ b/scripts/purge_cdn_cache.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -o pipefail +set -x + +# Function to URL-encode a string using Python +urlencode() { + python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$1" +} + +# Function to send the purge request using the provided API key +purge() { + local api_key="$1" + local url="$2" + curl -sL -X POST -H "AccessKey: $api_key" "$url" +} + +# Function to generate the purge URLs for a given domain, arch, and OS ID +generate_urls() { + local domain="$1" + local arch="$2" + local os_id="$3" + + # Array of resource paths (can be modified or extended as needed) + local resources=( + "src/contrib/PACKAGES" + "src/contrib/PACKAGES.gz" + "src/contrib/PACKAGES.rds" + "src/contrib/PACKAGES.db" + ) + + local urls=() + for resource in "${resources[@]}"; do + local target_url="https://${domain}/${arch}/${os_id}/latest/${resource}" + local encoded + encoded=$(urlencode "$target_url") + urls+=( "https://api.bunny.net/purge?url=${encoded}&async=false" ) + done + + echo "${urls[@]}" +} + +# Function to process multiple domains. +# Usage: process_domains domain1 [domain2 ... domainN] +purge_cdn_cache() { + local api_key="$1" + local arch="$2" + local os_id="$3" + shift 3 # Remove the first three arguments, leaving the domains + + for domain in "$@"; do + # Generate the list of URLs for the current domain + local urls + urls=($(generate_urls "$domain" "$arch" "$os_id")) + + # Purge each URL + for url in "${urls[@]}"; do + purge "$api_key" "$url" + done + done +} + +# Example usage: +# purge_cdn_cache "$BUNNY_API_KEY" "$ARCH" "$OS_ID" "domain1.com" "domain2.com" \ No newline at end of file