|
| 1 | +--- |
| 2 | +title: "Getting Started with multideploy" |
| 3 | +output: rmarkdown::html_vignette |
| 4 | +vignette: > |
| 5 | + %\VignetteIndexEntry{Getting Started with multideploy} |
| 6 | + %\VignetteEngine{knitr::rmarkdown} |
| 7 | + %\VignetteEncoding{UTF-8} |
| 8 | +--- |
| 9 | + |
| 10 | +```{r} |
| 11 | +#| include: FALSE |
| 12 | +knitr::opts_chunk$set( |
| 13 | + collapse = TRUE, |
| 14 | + comment = "#>" |
| 15 | +) |
| 16 | +``` |
| 17 | + |
| 18 | +## Introduction |
| 19 | + |
| 20 | +The `multideploy` package provides a streamlined way to deploy file changes |
| 21 | +across multiple GitHub repositories. Whether you're managing standardized CI/CD |
| 22 | +configurations, common utility scripts, code style definitions, or any other |
| 23 | +files that should be consistent across repositories, `multideploy` helps |
| 24 | +automate this process. |
| 25 | + |
| 26 | +This vignette will guide you through the main functionality of the package with |
| 27 | +practical examples. |
| 28 | + |
| 29 | +## Setup and Authentication |
| 30 | + |
| 31 | +First, load the multideploy package: |
| 32 | + |
| 33 | +```{r} |
| 34 | +#| label: setup |
| 35 | +#| eval: FALSE |
| 36 | +library(multideploy) |
| 37 | +``` |
| 38 | + |
| 39 | +Before using `multideploy`, you need to authenticate with GitHub. The package |
| 40 | +uses the [`gh`](https://github.com/r-lib/gh) package for authentication, which |
| 41 | +looks for a [GitHub Personal Access Token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) in the `GITHUB_PAT` or |
| 42 | +`GITHUB_TOKEN` environment variables. This token can be set directly within the |
| 43 | +R script via: |
| 44 | + |
| 45 | +```{r} |
| 46 | +#| label: auth |
| 47 | +#| eval: FALSE |
| 48 | +# Set GitHub PAT (or better, use .Renviron) |
| 49 | +Sys.setenv(GITHUB_PAT = askpass::askpass("What is your GitHub Personal Access Token (PAT) ?")) |
| 50 | +``` |
| 51 | + |
| 52 | +For regular use, it's recommended to add your PAT to the git credential system |
| 53 | +through the `gitcreds` package: |
| 54 | + |
| 55 | +```r |
| 56 | +gitcreds::gitcreds_set() |
| 57 | +``` |
| 58 | + |
| 59 | +Alternatively, you can set the PAT directly in your `.Renviron` file by running: |
| 60 | + |
| 61 | +```r |
| 62 | +file.edit("~/.Renviron") |
| 63 | +``` |
| 64 | + |
| 65 | +Then, type: |
| 66 | + |
| 67 | +```bash |
| 68 | +GITHUB_PAT=your_github_pat_here |
| 69 | +``` |
| 70 | + |
| 71 | +Your PAT needs appropriate permissions to access and modify repositories. |
| 72 | +At a minimum, you'll need: |
| 73 | + |
| 74 | +- `repo` scope for accessing private repositories |
| 75 | +- `workflow` scope if you're modifying GitHub Actions workflows |
| 76 | + |
| 77 | +## Managing Repositories |
| 78 | + |
| 79 | +### Listing Repositories |
| 80 | + |
| 81 | +The `repos()` function allows you to list repositories for a user or organization: |
| 82 | + |
| 83 | +```{r} |
| 84 | +#| label: repos |
| 85 | +#| eval: FALSE |
| 86 | +# List repositories for a user |
| 87 | +user_repos <- repos("username") |
| 88 | +
|
| 89 | +# List repositories for an organization |
| 90 | +org_repos <- repos("orgname", type = "public") |
| 91 | +
|
| 92 | +# Filter repositories by name pattern |
| 93 | +api_repos <- repos("orgname", filter_regex = "^api-") |
| 94 | +
|
| 95 | +# View the repositories |
| 96 | +head(api_repos) |
| 97 | +``` |
| 98 | + |
| 99 | +### Listing Organizations |
| 100 | + |
| 101 | +If you're a member of multiple organizations, you can list them with: |
| 102 | + |
| 103 | +```{r orgs} |
| 104 | +#| label: orgs |
| 105 | +#| eval: FALSE |
| 106 | +my_orgs <- orgs() |
| 107 | +print(my_orgs) |
| 108 | +``` |
| 109 | + |
| 110 | +## Working with Files |
| 111 | + |
| 112 | +### Getting File Content |
| 113 | + |
| 114 | +You can retrieve the content of a file from a GitHub repository: |
| 115 | + |
| 116 | +```{r} |
| 117 | +#| label: file-content |
| 118 | +#| eval: FALSE |
| 119 | +# Get content of a file |
| 120 | +workflow_file <- file_content("username/repo", ".github/workflows/ci.yml") |
| 121 | +
|
| 122 | +# View the content |
| 123 | +cat(workflow_file$content) |
| 124 | +``` |
| 125 | + |
| 126 | +### Creating or Updating Files |
| 127 | + |
| 128 | +To create or update a file in a repository: |
| 129 | + |
| 130 | +```{r} |
| 131 | +#| label: file-update |
| 132 | +#| eval: FALSE |
| 133 | +# Update a file |
| 134 | +result <- file_update( |
| 135 | + repo = "username/repo", |
| 136 | + path = ".github/workflows/ci.yml", |
| 137 | + content = "updated workflow content...", |
| 138 | + message = "Update CI workflow" |
| 139 | +) |
| 140 | +``` |
| 141 | + |
| 142 | +## Deploying Files Across Repositories |
| 143 | + |
| 144 | +The core functionality of `multideploy` is deploying files across multiple repositories. This can be done in two ways: |
| 145 | + |
| 146 | +### Direct File Deployment |
| 147 | + |
| 148 | +Deploy a single file to multiple repositories: |
| 149 | + |
| 150 | +```{r} |
| 151 | +#| label: file-deploy |
| 152 | +#| eval: FALSE |
| 153 | +# Get repositories |
| 154 | +repos <- repos("orgname", filter_regex = "^api-") |
| 155 | +
|
| 156 | +# Deploy a file to all repositories |
| 157 | +results <- file_deploy( |
| 158 | + source_file = "templates/ci.yml", |
| 159 | + target_path = ".github/workflows/ci.yml", |
| 160 | + repos = repos, |
| 161 | + commit_message = "Standardize CI workflow" |
| 162 | +) |
| 163 | +
|
| 164 | +# View deployment results |
| 165 | +print(results) |
| 166 | +``` |
| 167 | + |
| 168 | +### Creating Pull Requests |
| 169 | + |
| 170 | +For changes that require review, you can create pull requests: |
| 171 | + |
| 172 | +```{r} |
| 173 | +#| label: file-mapping-and-pull-requests |
| 174 | +#| eval: FALSE |
| 175 | +# Create a mapping of files to deploy |
| 176 | +mapping <- file_mapping( |
| 177 | + "templates/ci.yml" = ".github/workflows/ci.yml", |
| 178 | + "templates/lint.R" = ".lintr", |
| 179 | + "templates/codeowners" = ".github/CODEOWNERS" |
| 180 | +) |
| 181 | +
|
| 182 | +# Create pull requests with these changes |
| 183 | +pr_results <- pr_create( |
| 184 | + repos = repos, |
| 185 | + branch_name = "feature/standardize-configs", |
| 186 | + title = "Standardize repository configurations", |
| 187 | + body = "This PR updates CI workflows, linting settings, and CODEOWNERS file to match organization standards.", |
| 188 | + file_mapping = mapping |
| 189 | +) |
| 190 | +
|
| 191 | +# View PR creation results |
| 192 | +print(pr_results) |
| 193 | +``` |
| 194 | + |
| 195 | +## Advanced Use Cases |
| 196 | + |
| 197 | +### Deploying Files with Directory Structure |
| 198 | + |
| 199 | +You can deploy all files from a directory while preserving their structure: |
| 200 | + |
| 201 | +```{r} |
| 202 | +#| label: dir-mapping |
| 203 | +#| eval: FALSE |
| 204 | +# Create mapping from a directory |
| 205 | +workflow_mapping <- file_mapping( |
| 206 | + dir = "templates/workflows", |
| 207 | + pattern = "\\.ya?ml$", |
| 208 | + target_prefix = ".github/workflows/", |
| 209 | + preserve_structure = TRUE |
| 210 | +) |
| 211 | +
|
| 212 | +# Use this mapping to create PRs |
| 213 | +pr_create( |
| 214 | + repos = repos, |
| 215 | + branch_name = "feature/update-workflows", |
| 216 | + title = "Update all workflow files", |
| 217 | + body = "Standardize all GitHub Actions workflow files", |
| 218 | + file_mapping = workflow_mapping |
| 219 | +) |
| 220 | +``` |
| 221 | + |
| 222 | +### Dry Run Mode |
| 223 | + |
| 224 | +Before making actual changes, you can preview them using dry run mode: |
| 225 | + |
| 226 | +```{r} |
| 227 | +#| label: dry-run |
| 228 | +#| eval: FALSE |
| 229 | +# Preview file deployment without making changes |
| 230 | +dry_results <- file_deploy( |
| 231 | + source_file = "templates/ci.yml", |
| 232 | + target_path = ".github/workflows/ci.yml", |
| 233 | + repos = repos, |
| 234 | + dry_run = TRUE |
| 235 | +) |
| 236 | +
|
| 237 | +# View what would happen |
| 238 | +print(dry_results) |
| 239 | +``` |
| 240 | + |
| 241 | +### Filtering and Targeting Repositories |
| 242 | + |
| 243 | +You can combine repository filtering with deployment to target specific subsets of repositories: |
| 244 | + |
| 245 | +```{r targeting} |
| 246 | +#| label: target-repos |
| 247 | +#| eval: FALSE |
| 248 | +# Get all organization repositories |
| 249 | +all_repos <- repos("orgname") |
| 250 | +
|
| 251 | +# Filter to only Java repositories |
| 252 | +r_repos <- all_repos[grepl("r", all_repos$name), ] |
| 253 | +
|
| 254 | +# Deploy R-specific configuration |
| 255 | +file_deploy( |
| 256 | + source_file = "templates/R/.Rbuildignore", |
| 257 | + target_path = ".Rbuildignore", |
| 258 | + repos = r_repos |
| 259 | +) |
| 260 | +
|
| 261 | +# Filter to only Python repositories |
| 262 | +python_repos <- all_repos[grepl("python", all_repos$name), ] |
| 263 | +
|
| 264 | +# Deploy Python-specific configuration |
| 265 | +file_deploy( |
| 266 | + source_file = "templates/python/pylintrc", |
| 267 | + target_path = ".pylintrc", |
| 268 | + repos = python_repos |
| 269 | +) |
| 270 | +``` |
| 271 | + |
| 272 | +## Recommendations |
| 273 | + |
| 274 | +If you're deploying files across multiple repositories, we recommend taking |
| 275 | +into consideration the following: |
| 276 | + |
| 277 | +1. **Start with dry runs**: Always use `dry_run = TRUE` first to preview changes. |
| 278 | + |
| 279 | +2. **Use meaningful commit messages**: Include context about why the change is |
| 280 | + being made. |
| 281 | + |
| 282 | +3. **Consider PR approach for significant changes**: Use `pr_create()` instead |
| 283 | + of direct commits for changes that might need review. |
| 284 | + |
| 285 | +4. **Store templates in version control**: Keep your template files in their own |
| 286 | + repository. |
| 287 | + |
| 288 | +5. **Create a deployment script**: For regular deployments, create an R script |
| 289 | + that can be run repeatedly. |
| 290 | + |
| 291 | +## Deployment Script Example |
| 292 | + |
| 293 | +Regarding the last recommendation, you can create a deployment script that |
| 294 | +automates the process of updating files across multiple repositories. Here's an |
| 295 | +example script that updates CI/CD workflows and deploys configuration files to |
| 296 | +all repositories in an organization: |
| 297 | + |
| 298 | +```{r} |
| 299 | +#| label: deployment-script |
| 300 | +#| eval: FALSE |
| 301 | +library(multideploy) |
| 302 | +
|
| 303 | +# Get repositories |
| 304 | +api_repos <- repos("my-organization", filter_regex = "^api-") |
| 305 | +service_repos <- repos("my-organization", filter_regex = "^service-") |
| 306 | +all_repos <- rbind(api_repos, service_repos) |
| 307 | +
|
| 308 | +# Create file mappings |
| 309 | +workflow_mapping <- file_mapping( |
| 310 | + dir = "templates/workflows", |
| 311 | + pattern = "\\.ya?ml$", |
| 312 | + target_prefix = ".github/workflows/", |
| 313 | + preserve_structure = TRUE |
| 314 | +) |
| 315 | +
|
| 316 | +config_mapping <- file_mapping( |
| 317 | + "templates/.lintr" = ".lintr", |
| 318 | + "templates/.editorconfig" = ".editorconfig", |
| 319 | + "templates/CONTRIBUTING.md" = "CONTRIBUTING.md" |
| 320 | +) |
| 321 | +
|
| 322 | +# Create PRs for workflow changes |
| 323 | +pr_create( |
| 324 | + repos = all_repos, |
| 325 | + branch_name = "chore/update-workflows", |
| 326 | + title = "Update CI/CD workflows", |
| 327 | + body = "Update workflows to organization standards", |
| 328 | + file_mapping = workflow_mapping, |
| 329 | + dry_run = FALSE |
| 330 | +) |
| 331 | +``` |
| 332 | + |
| 333 | +If you want to deploy the files directly without creating pull requests, you can |
| 334 | +use the following snippet to directly deploy the configuration files: |
| 335 | + |
| 336 | +```{r} |
| 337 | +#| label: deployment-script-custom |
| 338 | +#| eval: FALSE |
| 339 | +# Directly deploy config files |
| 340 | +for (local_file in names(config_mapping)) { |
| 341 | + target_path <- config_mapping[[local_file]] |
| 342 | + |
| 343 | + file_deploy( |
| 344 | + source_file = local_file, |
| 345 | + target_path = target_path, |
| 346 | + repos = all_repos, |
| 347 | + commit_message = paste("Update", basename(target_path), "to organization standards"), |
| 348 | + dry_run = FALSE |
| 349 | + ) |
| 350 | +} |
| 351 | +``` |
| 352 | + |
| 353 | +# Fin |
| 354 | + |
| 355 | +The `multideploy` package streamlines the process of maintaining consistent |
| 356 | +files across multiple GitHub repositories. By automating deployment, you can |
| 357 | +ensure standardization while saving significant time and effort. |
0 commit comments