Type: Package
Title: Hosting an Independent CRAN Repository
Version: 0.9.0-1
Description: Stand-alone HTTP capable R-package repository, that fully supports R's install.packages() and available.packages(). It also contains API endpoints for end-users to add/update packages. This package can supplement 'miniCRAN', which has functions for maintaining a local (partial) copy of 'CRAN'. Current version is bare-minimum without any access-control or much security.
License: GPL-3
Depends: R (≥ 4.0.0)
Imports: rlang (≥ 1.1.0), plumber (≥ 1.2.0), assertthat (≥ 0.2.1), mime, xtable
Suggests: miniCRAN, testthat (≥ 3.0.0), covr
Encoding: UTF-8
RoxygenNote: 7.2.3
Collate: 'handlers-static.R' 'miniCRAN.R' 'packages.R' 'handlers-.R' 'api.R' 'microCRAN-package.R'
Config/testthat/edition: 3
NeedsCompilation: no
Packaged: 2023-11-03 15:28:22 UTC; smhe
Author: Stefan McKinnon Edwards ORCID iD [aut, cre, cph], Kamstrup A/S [cph]
Maintainer: Stefan McKinnon Edwards <sme@iysik.com>
Repository: CRAN
Date/Publication: 2023-11-03 22:00:02 UTC

microCRAN: Hosting an Independent CRAN Repository

Description

Stand-alone HTTP capable R-package repository, that fully supports R's install.packages() and available.packages(). It also contains API endpoints for end-users to add/update packages. This package can supplement 'miniCRAN', which has functions for maintaining a local (partial) copy of 'CRAN'. Current version is bare-minimum without any access-control or much security.

CRAN directory structure

/root
/---/src/contrib/
/---/src/contrib/PACKAGES(?.rds|.gz)
/---/src/contrib/microCRAN_v1.0.0.tar.gz
/---/src/contrib/4.0/PACKAGES
/---/src/contrib/4.1/PACKAGES
/---/src/contrib/4.2/PACKAGES
/---/src/contrib/4.3/PACKAGES
/---/bin/windows/contrib/4.0/PACKAGES
/---/bin/windows/contrib/4.0/microCRAN_v1.0.0.zip
/---/bin/windows/contrib/4.1/PACKAGES
/---/bin/windows/contrib/4.2/PACKAGES
/---/bin/windows/contrib/4.3/PACKAGES
/---/bin/macosx/contrib/4.0/PACKAGES
/---/bin/macosx/contrib/4.0/microCRAN_v1.0.0.tgz
/---/bin/macosx/contrib/4.1/PACKAGES
/---/bin/macosx/contrib/4.2/PACKAGES
/---/bin/macosx/contrib/4.3/PACKAGES

Author(s)

Maintainer: Stefan McKinnon Edwards sme@iysik.com (ORCID) [copyright holder]

Other contributors:


Adds a package-file to the repository

Description

Methods for taking a file (".tar.gz", ".zip", ".tgz") and placing it in the repository, all locally.

Usage

addPackage(
  fn,
  type = c("source", "mac.binary", "win.binary"),
  repo_dir,
  is.new = TRUE
)

Arguments

fn

Path to package

type

Type of package, see explanation in section "Binary packages" in utils::install.packages().

repo_dir

Path to local directory, where the root of the repository is. The (source) packages will be stored locally at ⁠{repo_dir}/src/contrib/⁠.

is.new

Logical, if TRUE, it causes an error if the file

Value

Invisibly returns the number of packages described in the resulting 'PACKAGES', 'PACKAGES.gz' and 'PACKAGES.rds' files. If 0, no packages were found and no files were written.

See Also

miniCRAN::addLocalPackage(), tools::write_PACKAGES()

Examples

f <- system.file('extdata/microCRAN_0.1.0.zip', package = 'microCRAN', mustWork = TRUE)
root <- tempdir()
addPackage(f, type = 'win.binary', repo_dir = root)


Build and start the microCRAN site

Description

build creates the Plumber-router and run starts the service.

Usage

build(
  repo_dir,
  url_path,
  redirect_url,
  title,
  description,
  contact,
  license,
  tos
)

run(pr, host = "127.0.0.1", port = 1881, url_path, ...)

Arguments

repo_dir

Path to local directory, where the root of the repository is. The (source) packages will be stored locally at ⁠{repo_dir}/src/contrib/⁠.

url_path

Optional prefix to endpoint. The CRAN repository will be available at e.g. ⁠http://127.0.0.1:port/path_prefix/⁠ with the "contrib.url" as ⁠http://127.0.0.1:port/path_prefix/src/contrib/⁠.

redirect_url

Url, if supplied, requests to static assets (package files, etc.) are redirected to another service instead of being handled by cran_static_path_handler. It can be beneficial to let e.g. an Apache httpd service handle those.

title, description, contact, license, tos

Descriptions of the API. Some defaults are used, see section below or https://www.rplumber.io/articles/annotations.html.

pr

A Plumber-router, e.g. as returned from build.

host

A string that is a valid IPv4 or IPv6 address that is owned by this server, which the application will listen on. "0.0.0.0" represents all IPv4 addresses and "::/0" represents all IPv6 addresses.

port

A number or integer that indicates the server port that should be listened on. Note that on most Unix-like systems including Linux and Mac OS X, port numbers smaller than 1025 require root privileges.

...

Additional arguments passed on to plumber::pr_run().

run

Logical, should the method run the site immediately?

Details

Point repo_dir to you local filesystem. If the (sub-)directory does not exist, it will be created when an R-package is added through the corresponding endpoint.

Value

A new Plumber-router object.

API Descriptions

The fields title, description, contact, license, and tos are used for describing the API in the resulting Swagger-documents. These follow the OpenAPI type descriptions, see https://spec.openapis.org/oas/v3.0.3#info-object.

Field name Type Description
title string The title of the API.
description string A short description of the API.
tos string A URL to the Terms of Service for the API.
contact list A "Contact Object", i.e., list-object with fields "name", "url" and "email".
license list A "License Object", i.e., list-object with fields "name" and "url".
version string The version of the API.

Creates and handle a (error) condition

Description

Creates and handle a (error) condition

Usage

cran_error_handler(req, res, e)

http_condition(status_code, message, ..., type = NULL, call = sys.call(-1))

Arguments

req, res

A "request"- and "response"-object, respectively

e

The error/[condition][base::conditions] that was thrown by a handler, preferable a [http_condition].

status_code

Integer HTTP Status Code

message

Optional message to display (text).

...

Other things to include in exception.

type

Type of condition to generate. Must be one of 'error', 'warning' or 'message'.

call

The call stored in the condition object.

Value

'NULL' invisibly; called to modify the response.


Handler for package repository files

Description

Creates handler for handling access to static files in the repository, i.e., the src/contrib, bin/windows/contrib, and bin/macosx/contrib subdirectories.

Use cran_static_redirect_handler to let another process (e.g. an Apache httpd or nginx server) handle the request, by redirecting it.

Usage

cran_static_path_handler(repo_dir, path_prefix = NULL)

cran_static_redirect_handler(dest_url)

Arguments

repo_dir

Path to local directory, where the root of the repository is. The (source) packages will be stored locally at ⁠{repo_dir}/src/contrib/⁠.

path_prefix

Optional URL-component, if the repository exists at a subdirectory of the host (see run's path). Here, it is only for decorative purposes.

dest_url

The url requests are forwarded to, after being appened with the request; it should contain all path-components up to the '/src' or '/bin' parts.

Value

A handler (function) for use in a Plumber router "filter" .

Examples

require(plumber)
pr() |>
 pr_filter('static',
  cran_static_path_handler(repo_dir, path_prefix = 'cran'))
pr() |>
  pr_filter('redirect',
    cran_static_redirect_handler("http://my.local.cran:80/cran"))

Directory listing

Description

Creates a simple HTML page with table of all files and subdirectories. May throw a 403 or 404 code.

Usage

directory_listing(req, res, path, path_prefix)

directory_listing_html(path, url_path, add_dir = TRUE)

Arguments

req, res

A "request"- and "response"-object, respectively

path

Path to directory to list

path_prefix

Optional URL-component, if the repository exists at a subdirectory of the host (see run's path). Here, it is only for decorative purposes.

url_path

The entire, relative path, that is displayed to end user in the URL.

add_dir

Logical, if the requested URL does not end with a '/', set this to TRUE, else all links will point to the parent directory.

Value

directory_listing returns the response-object. directory_listing_html returns a HTML-string with the entire page.


Running microCRAN as a Docker container

Description

A Dockerfile with an init-script is installed with this package, which can be used for running a Docker container with this package.

The path to the directory with the file can be found by running

system.file('docker/', package = 'microCRAN', mustWork = TRUE)

Running

To run, place microCRAN's source-tarball in the build directory with the Dockerfile.

Enable BuildKit and build the image:

DOCKER_BUILDKIT=1 docker build .

The build-process will automatically install any source-tarballed R-package that is located in the build-context (i.e. directory with the Dockerfile).

Start the container. Remember to map the port and the directory for the repository, else the repository is lost if the container is restarted. See the list below for variables to use.

docker run -d -v /data/my_local_cran:/var/cran -p 1881:1881 -e CRAN_DIR=/var/cran <microcran-image>
CRAN_HOST

Host for the service to listen to. See also plumber::pr_run.

CRAN_PORT

Port to listen on. Defaults to 1881.

CRAN_DIR

Path to root of local directory where the repository's files are stored.

CRAN_URL_PATH

Absolute path to the repository, as seen from the client.

CRAN_REDIRECT_URL

If static files should be served by something else.

See Also

build(), plumber::pr_run


Splits repository "contrib"-paths into a directory path and filename.

Description

Splits the requested path into directory-part and filename-part, while ensuring some security to where it may point to.

Usage

extract_cran_path_parts(s)

Arguments

s

The requested path, e.g. src/contrib or bin/windows/contrib/4.0/my_package_v0.1.0.zip.

Value

A character vector of length 2, with directory path and filename. If s was a directory (e.g., "src/contrib", "src/contrib/", "bin/windows/contrib/4.0", etc.) the second element is "". Can return NULL if s does not fit the repository directory structure.


Test if path matches repository directory structure

Description

Tests if the requested resource maps to a safe path in the respository, i.e., that it points to '/src/contrib', '/bin/windows/contrib' or '/bin/macosx/contrib', and does not attempt to weasle back out with '..'.

Usage

path_is_safe_for_cran(s)

Arguments

s

The path to check, typically 'req$PATH_INFO' in a request handler.

Value

Logical


Plumber route for adding package to repository

Description

Creates a Plumber route that handles an incoming R-package. Use rather build() to build the entire API.

Usage

pr_add_package(pr, path = "/add", repo_dir)

Arguments

pr

A Plumber router-object

path

The path to the endpoint

repo_dir

Path to local directory, where the root of the repository is. The (source) packages will be stored locally at ⁠{repo_dir}/src/contrib/⁠.

Value

NULL invisibly; called to modify the response.

See Also

build(), plumber::pr_post


Matches package file extension to package type

Description

Matches package file extension to package type

Usage

r_package_type(fn)

Arguments

fn

Filename or path

Value

"source", "mac.binary" or "win.binary", depending on file extension, or throws a http_condition.


Read DESCRIPTION file from package

Description

Read DESCRIPTION file from package

Usage

read_DESCRIPTION_zip(fn)

read_DESCRIPTION_tar(fn)

Arguments

fn

Path to either zip, tar.gz or tgz file.

Value

List-object with contents of DESCRIPTION file.

Examples

package <- system.file('extdata/microCRAN_0.1.0.zip',
  package='microCRAN', mustWork=TRUE)
read_DESCRIPTION_zip(package)