
Create custom R metapackages from local packages.
bigbang builds tidyverse-style metapackages from
local package archives. Every metapackage ends in
-verse—tidyverse, 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.
create_metapackage() scaffolds a complete, documented,
CRAN-checkable metapackage from your own package archives..tar.gz,
.tar and .zip, carry no version in the
filename, be listed in a manifest file, or be source directories that
bigbang builds for you. Identity and version are read from each
archive’s DESCRIPTION, not guessed from its name.<meta>_install() works with no arguments and no path
has to be agreed on between machines.cran_deps = "skip" is the default. This is what makes
bigbang work behind an institutional firewall, but it is just as useful
for shipping a versioned bundle anywhere.The generated package has two separate jobs:
library(<meta>) attaches components that are
already installed and reports missing ones.<meta>_install() explicitly installs local
archives once, in topological dependency order, and then attaches
them.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")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
)
resultBuild 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 orderAttached 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")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().
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.
plan <- create_metapackage(..., dry_run = TRUE) # resolve, validate, write nothing
plan$order # installation order
plan$files # what would be written
plan$findings # every validation findingdry_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.
on_component_error = "skip" generates from the
components that are valid instead of aborting, and reports the ones it
left out. The exclusion is transitive: a component that depends on an
excluded one is excluded too, and the chain is reported. When an invalid
archive still has a readable DESCRIPTION, its declared
package name drives this propagation; otherwise bigbang falls back to
the filename and reports that limitation. Excluding everything is an
error. During an update, a failed input never authorizes deletion of an
archive already shipped by the project. If the old component cannot be
identified unambiguously, archive reconciliation waits for a clean
update.update = TRUE regenerates in place. Generation records
a manifest of the files it wrote together with their content hashes;
update rewrites only those, and refuses to run if the
manifest is missing or if a generated file was modified or removed by
hand. Files bigbang did not write are never touched. Before changing an
existing project, bigbang backs up every generated file and its
manifest. A failed update restores that state so the same update can be
retried. Both dry runs and real results list removed paths in
removed_files. Removing a component removes its shipped
archive, which may be the last available copy. A real result also lists
partial documentation files created and cleaned up after a failed
roxygen run; a dry run cannot predict those failure-dependent cleanups.
Updates also refuse to write through a symbolic project root or symbolic
links inside the generated project, including links in parent
directories of generated files.install_upgrade fixes the default upgrade policy of the
installer that gets emitted, so you decide when generating whether
recipients stay pinned to the versions you ship ("always")
or keep anything newer they already have ("newer", the
default).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.
create_metapackage() creates a complete metapackage
source tree.install_local_pkg() installs one local archive and its
dependencies.diagnose_dependencies() reports possible implicit
dependencies.scan_bigbang_artifact() scans old source trees,
archives, or installed packages for historical deletion signatures
without loading them.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 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.2On 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.
<meta>_packages() design in
bigbang-generated metapackages draw on tidyverse and on metaverse
(Westgate and colleagues).| 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.
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)
scanIf 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.
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.
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("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/},
}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.