Introduction

{tic} uses the CI client packages {travis} and {circle} for setting up deployment on the CI systems.

Travis CI

  1. When calling travis::use_travis_deploy() (either directly or indirectly by using use_tic()), a public key is added to GitHub repository under the “Deploy keys” section and the user receives an e-mail:

  2. A private key is encoded in base64 and stored in an environment variable on Travis. During the CI run, {tic} installs it in the ~/.ssh directory so that it can be used for authentication:

    Initiating the deployment via a public-private key pair has the advantage that rights are granted to a single repo only and the setup process can be automated.

Circle CI

On Circle CI, setting up deployment is easier than on Travis as there is no need to create a SSH key pair for deployment.

When calling circle::use_circle_deploy() directly (or indirectly via tic::use_tic()), a so called “user-key” is created and stored in the Circle CI repo. This key makes it possible to deploy from Circle CI builds to your GitHub repo. No extra deploy key need to be stored in the GitHub repo.

Appveyor CI

Appveyor follows the same philosophy as Travis CI when it comes to deployment. A bunch of predefined deployment providers are available but deploying to the GitHub repo of the project is not possible. Unfortunately, there is no R client package for Appveyor yet. This means one needs to manually set up an SSH key pair which is used for deployment.

Usually we suggest to simply deploy on one of the other CI providers. However, when it comes to deployment to a drat repository (see ?do_drat()), one usually also wants to build and deploy Windows binaries.

Follow these steps to add deployment capabilities via {tic} to Appveyor builds:

  1. Generate a SSH key pair

    key <- openssl::rsa_keygen()
    
    pub_key <- tic:::get_public_key(key)$ssh
    private_key <- tic:::encode_private_key(key)
  2. Copy the public key and add it as an deploy key to Settings -> Deploy Key of the drat repo you want to deploy to (not the one of the package which should be deployed).

  3. Copy the private key and log into to your Appveyor account. Go to Account -> Encrypt YAML and encrypt the private key.

  4. Copy the resulting snipped into the appveyor.yml file of the repo. Name the environment variable “id_rsa”.

Now the Appveyor builds should be able to deploy to the drat repo (where you added the public key). If you are building a drat repo, make sure to also add USE_RTOOLS: true to appveyor.yml. Otherwise R cannot build binaries on Windows.

On a side note, we have an implicit R version requirement of >= 3.4 when building on Appveyor. This is due to the fact that we are only allowing binary installs on Appveyor with {tic} due to some nasty issues when building packages from source on Appveyor in the past. The requirement is caused by the {cli} >= 2.0.0 requirement of {tic}. This package version is not available as a binary on R =< 3.5.

GitHub Actions

GitHub Actions offers multiple options related to deployment:

  • Deployment related actions on the GitHub marketplace
  • Supplying an DEPLOY_PAT secret as r-lib/actions suggests
  • Using an SSH key pair with the private key stored as a “secret” in the repo and the public key as a “Deploy key” ({tic} default)

To reflect a successful deployment in the repo checks, actions from the GitHub marketplace help. For the actual deployment we recommend to use a SSH key pair. A SSH key can be easily created, is safe to use and when browsing at the “Deploy key” section of a repo, one can directly see if deployment was granted to a CI service for the repository.

To simplify the creation of a SSH key pair for deployment and adding the keys to the appropriate places, {tic} comes with a little helper function use_ghactions_deploy(). To use this function, you need a GITHUB_PAT with public repo scope defined in your .Renviron. usethis::browse_github_pat() helps setting one up if you haven’t done so already.

Updating the deployment status

To update the deployment status in the “environments” menu (next to “release” and “branches”) conditionally on the outcome of the “Deployment” stage, one can use the chrnorm/deployment-status.

Add the following before the “Before Deploy” stage:

yml - uses: chrnorm/[email protected] name: Create GitHub deployment id: deployment with: token: "${{ github.token }}" environment: productionyml

and then this part after the “After Deploy” stage:

{pkgdown} deployment

{pkgdown} is an R package which builds a documentation wrapper-site of an R package. It collects all the vignettes, function references and metadata information of a package and presents it in an eye-appealing HTML version.

It has become a quasi-standard to provide a {pkgdown} site for an R package. However, it is tedious to update the {pkgdown} site manually on every commit, check whether something has changed and commit the changes. {tic} comes with the ability to automate this process.

The following example shows how {tic} deploys a {pkgdown} site on Travis CI. Remember that you can freely choose your favorite provider for this task. In .travis.yml file the “before_deploy” and “deploy” stages are redirected to {tic}.

In the “before_deploy” stage, {tic} will do the following:

if (ci_on_travis()) {
  get_stage("before_deploy") %>%
    add_step(step_setup_ssh())
}

step_build_pkgdown() will build the {pkgdown} site and afterwards (note the pipe operator chaining the commands), step_push_deploy() takes care pushing the results to the repo. By default the site will be pushed to the gh-pages branch of your repo, keeping the history.

Deploying to docs/ (default branch) or gh-pages branch

By default deployment is pushed to the gh-pages branch. This has the following advantages:

  • No cluttering of the commit history in the default branch
  • Everything “just works” silently in the background

Default branch deployment

Deploying to the docs/ directory of the default branch has the following advantages:

  • Per-branch versions of the {pkgdown} site (if desired)
  • Per-branch versions enable the possibility to have preview for pull requests via https://www.netlify.com/

The disadvantage is that the default branch will be cluttered by automatic commits triggered by Travis CI that push the changes of the {pkgdown} site to the default branch.

Orphaning the gh-pages branch

By default, changes to the {pkgdown} site will be added as incremental commits to the gh-pages branch. This is useful to keep a history of past versions and to enable a release and dev version of the site. To have this feature, set

in your _pkgdown.yml file. See ?pkgdown::build_site() for more information.

If you only want to have one version of your {pkgdown} site and not fill your repo with many commits in the gh-pages branch, you can use do_pkgdown(orphan = TRUE). This will wipe all commits of this branch on every CI run so that there is only one commit corresponding to the latest version of your pkgdown site.

Conditional deployment

If you are running a build matrix or build stages on Travis CI, you want to run certain tasks only once during a build. The creation of a {pkgdown} site is a common task which applies to this.

Another situation in which conditioning comes in handy is when you want to deploy multiple files in different stages. You can restrict on which stage/job a task is executed with the help of environment variables.

{tic} can make use of implicit set environment variables of the build and additional ones added by the user. In the following we show a “build stage” example on Travis CI using the custom env variable "BUILD_PKGDOWN":

.travis.yml:

tic.R:

if (ci_has_env("TIC_DEPLOY_KEY")) {
  get_stage("before_deploy") %>%
    add_step(step_setup_ssh())
}

if (isTRUE("BUILD_PKGDOWN")) {
  get_stage("deploy") %>%
    add_step(step_build_pkgdown()) %>%
    add_step(step_push_deploy())
}

Committing single files

The step_push_deploy() step has the ability to restrict the files that are committed and pushed. This can be very useful for conditionally pushing documentation files like NEWS or man/ and NAMESPACE if these are automatically created during the CI run.

In the following example, these files are created/updated by calling devtools::document(). The commit_paths argument in step_push_deploy() decides which files are committed and pushed:

get_stage("before_deploy") %>%
  add_step(step_setup_ssh())

get_stage("deploy") %>%
  add_code_step(devtools::document(roclets = c("rd", "collate", "namespace"))) %>%
  add_step(step_push_deploy(commit_paths = c("NAMESPACE", "man/*")))

Applying this idea depends on your overall R package development strategy: Commit files like /man/ and NAMESPACE directly or let them be created during the CI run?