
Crossposting a post to other repositories
crossposting-a-post.Rmd
library(blogscapes)Once a post exists as a folder (its own index.qmd plus
any images it references – see the vignette("writing-a-post-from-google-doc")
vignette for how to produce one from a Google Doc), crossposting it to
other Openscapes-affiliated repositories is a three-step pipeline:
verify its images are self-contained, copy the folder into one or more
destination repos, then open a pull request in each.
Step 1: verify the post’s images
verify_post_images("website-new/blog/2026-09-09-openscapes_esip_summer_2026")This scans the post’s .qmd for every image reference –
the YAML image: field and body ![]() figures –
and checks that each one resolves to a file that actually exists
inside the post’s own folder. crosspost_blog()
copies the folder as-is, so a reference pointing outside of it (or to a
missing file) would silently produce a broken image in the destination
repo. verify_post_images() errors with the specific
offending reference(s) instead.
crosspost_blog() calls this automatically before
copying, so running it by hand first is optional – useful if you want to
check a post without copying it anywhere yet.
Step 2: copy the post into destination repos
targets <- crosspost_blog(
post = "website-new/blog/2026-09-09-openscapes_esip_summer_2026",
root = "~/Documents/repos/github/openscapes/crossposting",
destinations = c(
nasa = "NASA-Openscapes.github.io/news",
nmfs = "nmfs-openscapes.github.io/blog"
)
)destinations is a named vector of target directories –
one per destination repo – that the post folder gets copied
into, keeping its original folder name. Relative entries (like
the two above) are resolved against root, a common parent
folder all the repos live under as siblings; a destination can also be
given as an absolute path if it’s cloned somewhere else entirely:
crosspost_blog(
post = "website-new/blog/2026-09-09-openscapes_esip_summer_2026",
root = "~/Documents/repos/github/openscapes/crossposting",
destinations = c(
nasa = "NASA-Openscapes.github.io/news",
nmfs = "/some/other/place/nmfs-openscapes.github.io/blog"
)
)By default, crosspost_blog() errors if a destination
already has a folder for this post, to avoid silently clobbering an
existing crosspost. Pass overwrite = TRUE to replace it
instead.
The return value, targets, is the vector of full paths
the post was copied to – you’ll pass this straight into Step 3.
Step 3: open a pull request in each destination repo
open_crosspost_prs(targets, branch = "crosspost-2026-09-09-openscapes_esip_summer_2026")For each path in targets, this creates a branch, commits
the copied post, pushes it, and opens a pull request via the GitHub API
(using the gh package, so it relies on your existing GitHub
CLI authentication). It prints the opened PR’s URL for each repo as it
goes, and returns all of them (invisibly) as a character vector.
base (default "main") and
remote (default "origin") can be overridden if
a destination repo uses a different base branch or remote name.
Putting it together
post <- "website-new/blog/2026-09-09-openscapes_esip_summer_2026"
targets <- crosspost_blog(
post = post,
root = "~/Documents/repos/github/openscapes/crossposting",
destinations = c(
nasa = "NASA-Openscapes.github.io/news",
nmfs = "nmfs-openscapes.github.io/blog"
)
)
open_crosspost_prs(targets, branch = "crosspost-2026-09-09-openscapes_esip_summer_2026")