Getting Started with multideploy

Introduction

The {multideploy} package provides a streamlined way to deploy file changes across multiple GitHub repositories. Whether you’re managing standardized CI/CD configurations, common utility scripts, code style definitions, or any other files that should be consistent across repositories, {multideploy} helps automate this process.

This vignette will guide you through the main functionality of the package with practical examples.

Setup and Authentication

First, load the {multideploy} package:

library(multideploy)

Before using {multideploy}, you need to authenticate with GitHub. The package uses the gh package for authentication, which looks for a GitHub Personal Access Token (PAT) in the GITHUB_PAT or GITHUB_TOKEN environment variables. This token can be set directly within the R script via:

# Set GitHub PAT (or better, use .Renviron)
Sys.setenv(GITHUB_PAT = askpass::askpass("What is your GitHub Personal Access Token (PAT) ?"))

For regular use, it’s recommended to add your PAT to the git credential system through the gitcreds package:

gitcreds::gitcreds_set()

Your PAT needs appropriate permissions to access and modify repositories. At a minimum, you’ll need:

Managing Repositories

Listing Repositories

The repos() function allows you to list repositories for a user or organization:

# List repositories for a user
user_repos <- repos("username")

# List repositories for an organization
org_repos <- repos("orgname", type = "public")

# Filter repositories by name pattern
api_repos <- repos("orgname", filter_regex = "^api-")

# View the repositories
head(api_repos)

Listing Organizations

If you’re a member of multiple organizations, you can list them with:

my_orgs <- orgs()
print(my_orgs)

Working with Files

Getting File Content

You can retrieve the content of a file from a GitHub repository:

# Get content of a file
workflow_file <- file_content("username/repo", ".github/workflows/ci.yml")

# View the content
cat(workflow_file$content)

Creating or Updating Files

To create or update a file in a repository:

# Update a file
result <- file_update(
  repo = "username/repo",
  path = ".github/workflows/ci.yml",
  content = "updated workflow content...",
  message = "Update CI workflow"
)

Deploying Files Across Repositories

The core functionality of {multideploy} is deploying files across multiple repositories. This can be done in two ways:

Direct File Deployment

Deploy a single file to multiple repositories:

# Get repositories
repos <- repos("orgname", filter_regex = "^api-")

# Deploy a file to all repositories
results <- file_deploy(
  source_file = "templates/ci.yml",
  target_path = ".github/workflows/ci.yml",
  repos = repos,
  commit_message = "Standardize CI workflow"
)

# View deployment results
print(results)

Creating Pull Requests

For changes that require review, you can create pull requests:

# Create a mapping of files to deploy
mapping <- file_mapping(
  "templates/ci.yml" = ".github/workflows/ci.yml",
  "templates/lint.R" = ".lintr",
  "templates/codeowners" = ".github/CODEOWNERS"
)

# Create pull requests with these changes
pr_results <- pr_create(
  repos = repos,
  branch_name = "feature/standardize-configs",
  title = "Standardize repository configurations",
  body = "This PR updates CI workflows, linting settings, and CODEOWNERS file to match organization standards.",
  file_mapping = mapping
)

# View PR creation results
print(pr_results)

Advanced Use Cases

Deploying Files with Directory Structure

You can deploy all files from a directory while preserving their structure:

# Create mapping from a directory
workflow_mapping <- file_mapping(
  dir = "templates/workflows",
  pattern = "\\.ya?ml$",
  target_prefix = ".github/workflows/",
  preserve_structure = TRUE
)

# Use this mapping to create PRs
pr_create(
  repos = repos,
  branch_name = "feature/update-workflows",
  title = "Update all workflow files",
  body = "Standardize all GitHub Actions workflow files",
  file_mapping = workflow_mapping
)

Dry Run Mode

Before making actual changes, you can preview them using dry run mode:

# Preview file deployment without making changes
dry_results <- file_deploy(
  source_file = "templates/ci.yml",
  target_path = ".github/workflows/ci.yml",
  repos = repos,
  dry_run = TRUE
)

# View what would happen
print(dry_results)

Filtering and Targeting Repositories

You can combine repository filtering with deployment to target specific subsets of repositories:

# Get all organization repositories
all_repos <- repos("orgname")

# Filter to only Java repositories
r_repos <- all_repos[grepl("r", all_repos$name), ]

# Deploy R-specific configuration
file_deploy(
  source_file = "templates/R/.Rbuildignore",
  target_path = ".Rbuildignore",
  repos = r_repos
)

# Filter to only Python repositories
python_repos <- all_repos[grepl("python", all_repos$name), ]

# Deploy Python-specific configuration
file_deploy(
  source_file = "templates/python/pylintrc",
  target_path = ".pylintrc",
  repos = python_repos
)

Recommendations

If you’re deploying files across multiple repositories, we recommend taking into consideration the following:

  1. Start with dry runs: Always use dry_run = TRUE first to preview changes.

  2. Use meaningful commit messages: Include context about why the change is being made.

  3. Consider PR approach for significant changes: Use pr_create() instead of direct commits for changes that might need review.

  4. Store templates in version control: Keep your template files in their own repository.

  5. Create a deployment script: For regular deployments, create an R script that can be run repeatedly.

Deployment Script Example

Regarding the last recommendation, you can create a deployment script that automates the process of updating files across multiple repositories. Here’s an example script that updates CI/CD workflows and deploys configuration files to all repositories in an organization:

library(multideploy)

# Get repositories
api_repos <- repos("my-organization", filter_regex = "^api-")
service_repos <- repos("my-organization", filter_regex = "^service-")
all_repos <- rbind(api_repos, service_repos)

# Create file mappings
workflow_mapping <- file_mapping(
  dir = "templates/workflows",
  pattern = "\\.ya?ml$",
  target_prefix = ".github/workflows/",
  preserve_structure = TRUE
)

config_mapping <- file_mapping(
  "templates/.lintr" = ".lintr",
  "templates/.editorconfig" = ".editorconfig",
  "templates/CONTRIBUTING.md" = "CONTRIBUTING.md"
)

# Create PRs for workflow changes
pr_create(
  repos = all_repos,
  branch_name = "chore/update-workflows",
  title = "Update CI/CD workflows",
  body = "Update workflows to organization standards",
  file_mapping = workflow_mapping,
  dry_run = FALSE
)

If you want to deploy the files directly without creating pull requests, you can use the following snippet to directly deploy the configuration files:

# Directly deploy config files
for (local_file in names(config_mapping)) {
  target_path <- config_mapping[[local_file]]
  
  file_deploy(
    source_file = local_file,
    target_path = target_path,
    repos = all_repos,
    commit_message = paste("Update", basename(target_path), "to organization standards"),
    dry_run = FALSE
  )
}

Fin

The {multideploy} package streamlines the process of maintaining consistent files across multiple GitHub repositories. By automating deployment, you can ensure standardization while saving significant time and effort.