© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
C. Chandrasekara, P. HerathHands-on GitHub Actionshttps://doi.org/10.1007/978-1-4842-6464-5_5

5. Artifacts and Caching Dependencies

Chaminda Chandrasekara1   and Pushpa Herath2
(1)
Dedigamuwa, Sri Lanka
(2)
Hanguranketha, Sri Lanka
 

Artifacts in GitHub Actions pass data to a subsequent job or store data or compiled binaries once the workflow is completed. Persisted data in one job can be passed to another subsequent job, which may be running on a different operating system. This is an advantage of using artifacts. The retention period of artifacts in GitHub Actions workflows in 90 days by default; however, you have the option to change these settings, which is discussed later in this chapter.

Reusable files can be cached, which considerably reduces the execution time of a GitHub Actions workflow. However, any secrets or files containing secrets should not be added to the cache because the cache can be pulled from a forked repo.

This chapters explains how to use artifacts and caches.

Storing Content in Artifacts

When you execute a build or test run in a GitHub Actions workflow, it generates binaries and test results as the output of the workflow. These items may be stored for the next jobs in the same workflow. GitHub storage is utilized to store artifacts. Usage is free for public repos and self-hosted runners (discussed in Chapter 6). Private repos have limitations on storage and the number of minutes to run actions.

You can download artifacts from a workflow once it is completed (see Figure 5-1).
../images/502534_1_En_5_Chapter/502534_1_En_5_Fig1_HTML.jpg
Figure 5-1

Artifacts

Using artifacts from another workflow is ideal for implementing a better CI/CD experience. However, sharing artifacts between workflows is not a built-in feature (as of writing this book). One of the GitHub Actions issues (in the community where GitHub issues are discussed) mentioned that sharing artifacts between workflows would be implemented sooner, and if such sharing of artifacts between workflows is implmented that would be ideal for implementing proper CI CD workflows in GitHub Actions.

To upload an artifact, use the “Upload a Build Artifact” action in GitHub. You can also download artifacts and delete artifact tasks in a workflow (see Figure 5-2).
../images/502534_1_En_5_Chapter/502534_1_En_5_Fig2_HTML.jpg
Figure 5-2

Artifact actions

The code for uploading an artifact action is shown in the following example. Artifacts and log files can remain in a workflow for a maximum of 90 days and a minimum of one day. The default retention period is 90 days.
    - name: Upload a Build Artifact
      uses: actions/upload-artifact@v2.2.0
      with:
        # Artifact name
        name: myartifact2 # optional, default is artifact
        # A file, directory or wildcard pattern that describes what to upload
        path: "**/bin/Debug/com.companyname.AwesomeApp.api"
        # The desired behavior if no files are found using the provided path.
        #Available Options:
        #  warn: Output a warning but do not fail the action
        #  error: Fail the action with an error message
        #  ignore: Do not output any warnings or errors, the action does not fail
        if-no-files-found: error # optional, default is warn
        # Duration after which artifact will expire in days. 0 means using default retention.
        # Minimum 1 day. Maximum 90 days unless changed from the repository settings page.
        retention-days: 90 # optional

If you want to change the retention period to more than 90 days for private, internal or GitHub enterprise you can set the value to maximum of 400 days.

Let’s look at an example scenario where artifacts must be passed to another job in the workflow. Android build steps are done on a macOS runner. The build APK is deployed to the Microsoft App Center using a Windows runner for distribution purposes. Once you complete the build, you can upload the APK as an artifact in the workflow, and then download it to the Windows runner job, and deploy it to the app center. Note the following example pipeline.
on: [push, pull_request]
jobs:
  Android:
    runs-on: macos-latest
    steps:
    - uses: actions/checkout@v1
    - name: Android
      run: |
        cd AwesomeApp
        nuget restore
        cd AwesomeApp.Android
        msbuild AwesomeApp.Android.csproj /verbosity:normal /t:PackageForAndroid /p:Configuration=Debug
    - uses: actions/upload-artifact@v2
      with:
        name: my-artifact
        path: "**/bin/Debug/com.companyname.AwesomeApp.apk"
  AppCenterDistibuteDroid:
    runs-on: ubuntu-latest
    needs: Android
    steps:
    - uses: actions/download-artifact@v2
      with:
        name: my-artifact
    - name: App Center
      uses: wzieba/AppCenter-Github-Action@v1.0.0
      with:
        # App name followed by username
        appName: Ch-DemoOrg/SLDevOpsDroidDemo
        # Upload token - you can get one from appcenter.ms/settings
        token: ${{ secrets.AppCenterAPIToken }}
        # Distribution group
        group: alphatesters
        # Artefact to upload (.apk or .ipa)
        file: AwesomeApp/AwesomeApp.Android/bin/Debug/com.companyname.AwesomeApp.apk
        # Release notes visible on release page
        releaseNotes: "demo test"
The pipeline artifact upload task uploads the build apk.
- uses: actions/upload-artifact@v2
      with:
        name: my-artifact
        path: "**/bin/Debug/com.companyname.AwesomeApp.apk"
Then, the artifact is downloaded in the next job, using the artifact name.
- uses: actions/download-artifact@v2
      with:
        name: my-artifact
The download artifact action has the following options. You can provide the name of the artifact and optionally a path to download artifacts. Artifact content is extracted from the specified path.
- name: Download a Build Artifact
  uses: actions/download-artifact@v2.0.5
  with:
    # Artifact name
    name: myartifact # optional
    # Destination path
    path: artifacts # optional

5.02: Cashing Workflow Dependencies

When jobs are executed in GitHub-hosted runners, they always run in a fresh and clean virtual environment. A clean environment demands downloading all required dependencies in each job run, causing longer runtimes for jobs, higher utilization of network bandwidth, and increased costs. Dependencies may include files utilized by package and dependency management tools such as npm, Gradle, yarn.

As a solution, you can use GitHub’s capabilities to cache dependencies. However, you should avoid caching sensitive values in public repositories because forked repos can obtain cached information.

File storing is a common capability of both artifacts and caches; however, each purpose is different, and the use of artifacts and caches are not interchangeable. Caching should store files when they do not change jobs or when the next workflow runs. Artifacts should share files between jobs and when you want to view files after a job run.

The following is a template for the latest version of a cache action.
- name: Cache
  uses: actions/cache@v2.1.3
  with:
    # A list of files, directories, and wildcard patterns to cache and restore
    path:
    # An explicit key for restoring and saving the cache
    key:
    # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
    restore-keys: # optional
    # The chunk size used to split up large files during upload, in bytes
    upload-chunk-size: # optional

You can define a list of files, directories or wild card patterns in the cache action which are used to put in cache or restore from cache. Explicit key can be specified to use as the key for restoring or saving the cache. Additionally, a list of keys can be specified to use for restoration of cache items in a case where the cache items cannot be found with the explicit key. The chunk size can be used to define the size of chunks to use, when breaking down a large file to chunks, which is uploading to cache.

An example of caching a node module is shown next.
    - name: Cache node modules
      uses: actions/cache@v2
      env:
        cache-name: cache-node-modules
      with:
        # npm cache files are stored in `~/.npm` on Linux/macOS
        path: ~/.npm
        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.os }}-build-${{ env.cache-name }}-
          ${{ runner.os }}-build-
          ${{ runner.os }}-
The path is ~/.npm. It is the path for Linux and macOS npm cache files. If you use this in a pipeline implemented to build a node project, the build steps with caching are similar to the following.
name: Node.js CI
on: [workflow_dispatch]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Cache node modules
      uses: actions/cache@v2
      env:
        cache-name: cache-node-modules
      with:
        # npm cache files are stored in `~/.npm` on Linux/macOS
        path: ~/.npm
        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.os }}-build-${{ env.cache-name }}-
          ${{ runner.os }}-build-
          ${{ runner.os }}-
    - name: Install Dependencies
      run: npm install
    - name: Build
      run: npm build
    - name: Test
      run: npm test
When you execute the workflow for the first time, there is no cache available in the repo, so the files are stored in the cache (see Figure 5-3).
../images/502534_1_En_5_Chapter/502534_1_En_5_Fig3_HTML.jpg
Figure 5-3

Cache node modules

In subsequent runs, the cached files are used, and since the cache is available, the pipeline does not save the cache again (see Figure 5-4).
../images/502534_1_En_5_Chapter/502534_1_En_5_Fig4_HTML.jpg
Figure 5-4

Using cache

GitHub’s policy is to remove cached files not accessed for seven days. You can create many caches; however, there is a 5 GB size limit for all caches in the repository. If you add more than 5 GB, GitHub removes caches to bring down the cached file size to under 5 GB.

Summary

This chapter discussed using artifacts in GitHub Actions to share files between workflow jobs and to view or download file output in a workflow. It also explored caching files for workflow execution.

The next chapter discusses self-hosted runner setups in GitHub Actions so to execute workflows on your machines or virtual machines.