bigbang bigbang logo

Create custom R metapackages from local packages.

R-CMD-check CRAN status r-universe License: GPL v3 Lifecycle: stable docs: español

bigbang builds tidyverse-style metapackages from local package archives. Every metapackage ends in -versetidyverse, teamverse, yours. This package creates them: one function call, and a new -verse exists.

Its reason to exist is distributing a set of packages as a single unit.

Say you maintain four packages of your own that are used together and depend on each other. Someone joins the team, or another office asks for them. They are not on CRAN, so the way to hand them over is a folder of .tar.gz files.

At best you add instructions: install this one first, then that one, this version goes with that one. But instructions are manual work for whoever receives them, and one more document to keep current every time a version changes or a package joins the set.

bigbang puts that knowledge inside the package instead. The order comes from the real dependency graph and the versions are recorded in the generated metapackage, so there is nothing to follow by hand and nothing that can drift out of step with what the folder actually contains. The set you curated is the set they install, in a single line.

✨ At a glance

The generated package has two separate jobs:

🚀 Installation

The stable version is on CRAN:

install.packages("bigbang")

The development version is served as a binary from r-universe, so it needs no compilation:

install.packages("bigbang", repos = c("https://sebollin.r-universe.dev",
                                      "https://cloud.r-project.org"))

Or from the sources on GitHub:

# install.packages("pak")
pak::pak("sebollin/bigbang")

# or
remotes::install_github("sebollin/bigbang")

And true to the package’s offline spirit, a local source checkout installs without any network at all:

install.packages("path/to/bigbang", repos = NULL, type = "source")

⚡ Quick start

Suppose archives/ contains:

archives/
├── datahelpers_1.2.0.tar.gz
└── reports_0.9.1.tar.gz

Create the metapackage in a new directory:

library(bigbang)

result <- create_metapackage(
  name = "teamverse",
  packages = c("datahelpers_1.2.0", "reports_0.9.1"),
  pkg_dir = "archives",
  dest_dir = tempdir(),
  document = TRUE
)
result

Build and install teamverse by the usual R package workflow. Component installation remains explicit:

library(teamverse)                 # attaches what is already installed
teamverse_install()                # installs them, in dependency order

Attached component exports are available directly (for example, report()) or through their own namespace (reports::report()). They are not copied into the metapackage namespace, so teamverse::report() is not supported.

To expose explicit component exports through read-only runtime bindings, use reexport = TRUE when generating. Components remain outside Imports and Depends, so the metapackage can be installed and loaded offline before they exist. Before installation, reading a binding returns a placeholder function; its clear missing-component error appears only when that function is called. For non-function exports, access returns the placeholder instead of the object until installation. The binding then resolves the real function or object without reloading the metapackage. Only explicit export() directives become bindings, including non-syntactic names, which are quoted safely in NAMESPACE. S4 classes and methods remain available by loading the component. An object restored with readRDS() does not load a component by itself, so base R cannot dispatch that component’s S3 method until the component has been loaded.

teamverse carries its components, so the call takes no arguments and that is all anyone who receives it has to do. Hand over the built teamverse_0.1.0.tar.gz and nothing else: no folder of archives alongside it, and no path to agree on beforehand. If the archives should stay in a shared location instead, generate with include_archives = FALSE; then teamverse_install() requires an explicit pkg_dir.

cran_deps = "skip" is the default and never accesses the network. Use "error" to fail immediately when a non-local dependency is missing, or "install" with an explicitly configured repos value to allow repository installation.

Generated installers also accept upgrade = "newer" (the default), "always", or "never"; force = TRUE is the concise form of upgrade = "always". Generated metapackages use an optional cli two-column attachment message and fall back to their ASCII banner when cli is unavailable. Set options(teamverse.quiet = TRUE) to silence startup messages, or call teamverse_conflicts() to inspect masking conflicts.

For an ordered pipeline guide, supply every component once in a named workflow:

workflow = c("Import" = "datahelpers", "Report" = "reports")

Validation and explicit tolerances

During generation, bigbang validates everything that protects the recipient of a generated metapackage. Unsafe or malformed archives, invalid component metadata, duplicate components, unsatisfied local version constraints, and dependency cycles are always hard errors and cannot be disabled. Installation is more tolerant: an already installed component can be kept when an archive it will not use cannot be read, and the reason is reported.

Checks about project tidiness can be relaxed individually and explicitly:

create_metapackage(
  # ...,
  tolerate = c("filename_mismatch", "unincluded_local_dep")
)

"filename_mismatch" silences warnings when an archive filename differs from its DESCRIPTION identity. "unincluded_local_dep" changes the error for a local dependency available in the supplied sources but omitted from packages into a warning. The generated metapackage will not ship that dependency, so the recipient must provide it through pkg_dir or a repository with cran_deps = "install". Applied relaxations are recorded in result$tolerated; unknown names are errors. There is deliberately no switch that disables all validation.

bigbang does not run R CMD check on component packages. A component with check warnings or notes can be included; validation is limited to whether the distributed metapackage can identify and safely install its components.

See vignette("getting-started", package = "bigbang") for a reproducible toy project created entirely under tempdir().

🎛️ Where components come from

Any element of packages that is an existing file is used as a path; anything else is resolved as a stem in pkg_dir, which accepts more than one directory. So all of these work, including mixed together in one call:

create_metapackage(
  "teamverse",
  packages = c(
    "/srv/archives/first_1.2.0.tar.gz",  # a path, any directory
    "~/builds/second.zip",               # another directory, another format
    "third_0.4.0",                       # a stem resolved in pkg_dir
    "~/src/fourth"                       # a source directory, built for you
  ),
  pkg_dir = c("/srv/archives", "~/builds"),
  dest_dir = "~/projects"
)

A filename without a version is fine: Package and Version come from the archive’s DESCRIPTION. If the filename disagrees, bigbang warns and trusts the DESCRIPTION.

A bare package name such as "geomides" also works when exactly one archive in pkg_dir declares Package: geomides. Matching uses the declared package identity, so "geo" never selects geomides; if several versions or sources match, bigbang lists the candidates and asks for an explicit stem or path. Unreadable archives encountered during that search are excluded with a warning that names each affected file.

Source directories are built with the optional pkgbuild package, in a temporary directory, and require include_archives = TRUE, because the archive built for them does not outlive the call.

packages can also be the path to a manifest: one component per line, # for comments. Relative paths in it resolve against the manifest’s own directory; absolute paths and ~ paths are used as written; and bare filenames are also looked up in pkg_dir, so the list can live under version control while the archives do not.

🎚️ Generation options

plan <- create_metapackage(..., dry_run = TRUE)  # resolve, validate, write nothing
plan$order                                       # installation order
plan$files                                       # what would be written
plan$findings                                    # every validation finding

dry_run = TRUE does not create dest_dir and does not touch the destination at all, so it is a safe way to see what a call would do before it does it.

The generated installer also takes only to install a subset — local dependencies of the selection are added automatically — and lib to choose the library it installs into.

🧰 Main API

🗜️ ZIP files and portability

ZIP archives are classified by content. A ZIP containing Meta/package.rds is a Windows binary and is installed with type = "win.binary" on Windows only. Other ZIPs containing DESCRIPTION are unpacked into an owned temporary directory and installed as source packages.

All generated text is written explicitly as UTF-8. CI is prepared for R release on Windows and macOS and for release, devel, and oldrel on Ubuntu. The declared minimum is R 3.6.0, following the minimum of the imported brio release.

🌎 English and Spanish

English is the source language for code, help, and runtime messages. A complete Spanish runtime catalog is included through R’s gettext mechanism:

Sys.setLanguage("es")  # R >= 4.2

On earlier R versions, set LANGUAGE=es before starting R. A complete Spanish guide is available in vignette("bigbang-es", package = "bigbang") and as README.es.md. Rd help remains English because R has no stable native mechanism for translated help; a separate bigbang.es module can be considered if rhelpi18n becomes production-ready and reaches CRAN.

🧭 Choosing the right tool

Need Best fit
Distribute one curated, version-pinned set of local archives as a single installable unit bigbang
A conventional local repository with indexes, multiple packages, and repository semantics miniCRAN or drat
A small metapackage around packages already available from repositories pkgverse

bigbang deliberately does not replace a repository manager. If a team needs version retention, repository indexes, or dependency distribution to many projects, miniCRAN/drat is the stronger abstraction. bigbang is useful when the distributed unit is a curated metapackage plus a directory of archives.

🛡️ Data-safety history and old artifacts

An unreleased predecessor generated cleanup code that could remove directories named after components from the user’s working directory. The startup installer and all cwd-relative cleanup paths were removed before this CRAN submission and are covered by destructive regression tests that run only in disposable trees.

Do not load or document an old generated artifact before classifying it:

scan <- scan_bigbang_artifact("path/to/artifact", dry_run = TRUE)
scan

If scan$vulnerable is true, quarantine the artifact and generate a new version in a new, empty destination. Never regenerate an unclassified source tree in place. The full remediation procedure is documented in the Spanish guide and in RELEASE.md.

🙏 Acknowledgments

bigbang started from a suggestion by Richard Detomasi, who proposed building a metapackage tool and pointed to pegeler/metapackage as an antecedent. The design and implementation—including the graph-based dependency resolution—are by Sebastián Lucas. The hex logo was created with hexSticker.

🤝 Contributing

Contributions are welcome: bug reports and feature ideas through issues, and pull requests following CONTRIBUTING.md (spelling, lint, and test expectations are documented there). The package aims to stay small and focused — see Choosing the right tool above for what deliberately stays out of scope.

📖 Citation

citation("bigbang")
@Manual{bigbang2026,
  title  = {bigbang: Build 'Tidyverse'-Style Meta-Packages from Local Package Files},
  author = {Sebastián Lucas},
  note   = {R package version 0.3.0},
  year   = {2026},
  url    = {https://sebollin.github.io/bigbang/},
}

🔬 Development status

The complete test suite includes unit, portability, i18n, scanner, installation, and data-loss regression tests. R CMD check --as-cran runs with the PDF manual enabled for both bigbang and a generated metapackage, on every push, across Ubuntu (release, devel, oldrel), Windows, and macOS. win-builder and rhub::rhub_check() results are reviewed before each CRAN submission.