Skip to content

Commit

Permalink
Merge pull request #131 from rstudio/gha
Browse files Browse the repository at this point in the history
Add GitHub Actions workflow to test R builds
  • Loading branch information
glin authored Aug 17, 2022
2 parents b088d31 + b863122 commit 38b7be9
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 5 deletions.
131 changes: 131 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: R builds

on:
push:
paths:
- 'builder/**'
- 'test/**'
- 'Makefile'
- '.github/workflows/test.yml'
workflow_dispatch:
inputs:
platforms:
description: |
Comma-separated list of platforms. Specify "all" to use all platforms (the default).
required: false
default: 'all'
type: string
r_versions:
description: |
Comma-separated list of R versions. Specify "last-N" to use the
last N minor R versions, or "all" to use all minor R versions since R 3.1.
Defaults to "last-5".
required: false
default: 'last-5'
type: string

permissions:
contents: read

jobs:
setup-matrix:
runs-on: ubuntu-latest
outputs:
platforms: ${{ steps.setup-matrix.outputs.platforms }}
r_versions: ${{ steps.setup-matrix.outputs.r_versions }}
steps:
- uses: actions/checkout@v3

- name: Set up matrix of platforms and R versions
id: setup-matrix
run: |
platforms=$(python test/get_platforms.py ${{ github.event.inputs.platforms }})
echo "::set-output name=platforms::$platforms"
r_versions=$(python test/get_r_versions.py ${{ github.event.inputs.r_versions }})
echo "::set-output name=r_versions::$r_versions"
docker-images:
needs: setup-matrix
strategy:
matrix:
platform: ${{ fromJson(needs.setup-matrix.outputs.platforms) }}
runs-on: ubuntu-latest
name: Docker image (${{ matrix.platform }})
steps:
- uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
install: true

# Enable Docker layer caching without having to push to a registry.
# https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
# This may eventually be migrated to the GitHub Actions cache backend,
# which is still considered experimental.
# https://github.com/moby/buildkit#github-actions-cache-experimental
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ matrix.platform }}-buildx-${{ github.sha }}
restore-keys: ${{ matrix.platform }}-buildx-

# Use docker buildx instead of docker-compose here because cache exporting
# does not seem to work as of docker-compose v2.6.0 and buildx v0.8.2, even
# though it works with buildx individually.
- name: Build image
run: |
docker buildx build -t r-builds:${{ matrix.platform }} \
--file builder/Dockerfile.${{ matrix.platform }} \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache-new,mode=max" \
builder
# Temporary workaround for unbounded GHA cache growth with the local cache mode.
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
test:
needs: [setup-matrix, docker-images]
strategy:
fail-fast: false
matrix:
platform: ${{ fromJson(needs.setup-matrix.outputs.platforms) }}
r_version: ${{ fromJson(needs.setup-matrix.outputs.r_versions) }}
runs-on: ubuntu-latest
name: ${{ matrix.platform }} (R ${{ matrix.r_version }})
steps:
- uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
install: true

- name: Restore cached Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ matrix.platform }}-buildx-${{ github.sha }}
restore-keys: ${{ matrix.platform }}-buildx-

- name: Load cached Docker image
run: |
docker buildx build -t r-builds:${{ matrix.platform }} \
--file builder/Dockerfile.${{ matrix.platform }} \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--load \
builder
- name: Build R
run: |
R_VERSION=${{ matrix.r_version }} make build-r-${{ matrix.platform }}
- name: Test R
run: |
R_VERSION=${{ matrix.r_version }} make test-r-${{ matrix.platform }}
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ $(foreach platform,$(PLATFORMS), \
$(eval $(GEN_TARGETS)) \
)

print-platforms:
@echo $(PLATFORMS)

# Helper for launching a bash session on a docker image of your choice. Defaults
# to "ubuntu:xenial".
TARGET_IMAGE?=ubuntu:xenial
Expand All @@ -68,4 +71,4 @@ bash:
-w /r-builds \
${TARGET_IMAGE} /bin/bash

.PHONY: deps docker-build docker-push docker-down docker-build-package docker-shell-package-env ecr-login fetch-serverless-custom-file serverless-deploy
.PHONY: deps docker-build docker-push docker-down docker-build-package docker-shell-package-env ecr-login fetch-serverless-custom-file print-platforms serverless-deploy
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,21 @@ environment:

In order for the makefile to push these new platforms to ECR, add them to the PLATFORMS variable near the top of the Makefile

### test/docker-compose.yml

A new service in the `test/docker-compose.yml` file named according to the `platform-version` and containing the proper entries:

```yaml
ubuntu-2204:
image: ubuntu:jammy
command: /r-builds/test/test-apt.sh
environment:
- OS_IDENTIFIER=ubuntu-2204
- R_VERSION=${R_VERSION}
volumes:
- ../:/r-builds
```
### Submit a Pull Request
Once you've followed the steps above, submit a pull request. On successful merge, builds for this platform will begin to be available from the CDN.
Expand All @@ -244,6 +259,16 @@ serverless invoke stepf -n rBuilds -d '{"force": true, "versions": ["3.6.3", "4.

## Testing

Tests are automatically run on each push that changes a file in `builder/`, `test/`, or the `Makefile`.
These tests validate that R was correctly configured, built, and packaged. By default, the tests run
for the last 5 minor R versions on each platform.

To run the tests manually, you can navigate to the [GitHub Actions workflow page](https://github.com/rstudio/r-builds/actions/workflows/test.yml)
and use "Run workflow" to run the tests from a custom branch, list of platforms, and list of R versions.

To skip the tests, add `[skip ci]` to your commit message. See [Skipping workflow runs](https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs)
for more information.

To test the R builds locally, you can use the `build-r-$PLATFORM` and `test-r-$PLATFORM`
targets to build R and run the tests. The tests use the quick install script to install R,
using a locally built R if present, or otherwise a build from the CDN.
Expand Down
2 changes: 0 additions & 2 deletions builder/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '2.0'

services:
ubuntu-1804:
command: ./build.sh
Expand Down
2 changes: 0 additions & 2 deletions test/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '2.0'

services:
ubuntu-1804:
image: ubuntu:bionic
Expand Down
31 changes: 31 additions & 0 deletions test/get_platforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import argparse
import json
import subprocess


def main():
parser = argparse.ArgumentParser(description="Print R-builds platforms as JSON.")
parser.add_argument(
'platforms',
type=str,
nargs='?',
default='all',
help='Comma-separated list of platforms. Specify "all" to use all platforms (the default).'
)
args = parser.parse_args()
platforms = _get_platforms(which=args.platforms)
print(json.dumps(platforms))


def _get_platforms(which='all'):
supported_platforms = subprocess.check_output(['make', 'print-platforms'], text=True)
supported_platforms = supported_platforms.split()
if which == 'all':
return supported_platforms
platforms = which.split(',')
platforms = [p for p in platforms if p in supported_platforms]
return platforms


if __name__ == '__main__':
main()
67 changes: 67 additions & 0 deletions test/get_r_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import argparse
import json
import re
import urllib.request

VERSIONS_URL = 'https://cdn.rstudio.com/r/versions.json'

# Minimum R version for "all"
MIN_ALL_VERSION = '3.1.0'


def main():
parser = argparse.ArgumentParser(description="Print R-builds R versions as JSON.")
parser.add_argument(
'versions',
type=str,
nargs='?',
default='last-5',
help="""Comma-separated list of R versions. Specify "last-N" to use the
last N minor R versions, or "all" to use all minor R versions since R 3.1.
Defaults to "last-5".
"""
)
args = parser.parse_args()
versions = _get_versions(which=args.versions)
print(json.dumps(versions))


def _get_versions(which='all'):
supported_versions = sorted(_get_supported_versions(), reverse=True)

last_n_versions = None
if which.startswith('last-'):
last_n_versions = int(which.replace('last-', ''))
elif which != 'all':
versions = which.split(',')
versions = [v for v in versions if v in supported_versions]
return versions

versions = {}
for ver in supported_versions:
# Skip unreleased versions (e.g., devel, next)
if not re.match(r'[\d.]', ver):
continue
if ver < MIN_ALL_VERSION:
continue
minor_ver = tuple(ver.split('.')[0:2])
if minor_ver not in versions:
versions[minor_ver] = ver
versions = sorted(list(versions.values()), reverse=True)

if last_n_versions:
return versions[0:last_n_versions]

return versions


def _get_supported_versions():
request = urllib.request.Request(VERSIONS_URL)
response = urllib.request.urlopen(request)
data = response.read()
result = json.loads(data)
return result['r_versions']


if __name__ == '__main__':
main()

0 comments on commit 38b7be9

Please sign in to comment.