commit b07ee5cce868cabaf642ac6046f648db1b2753c8 Author: awalsh128 Date: Wed Oct 13 21:11:27 2021 -0700 Initial commit. diff --git a/.github/workflows/pub_staging_pr.yml b/.github/workflows/pub_staging_pr.yml new file mode 100644 index 0000000..47458e2 --- /dev/null +++ b/.github/workflows/pub_staging_pr.yml @@ -0,0 +1,19 @@ +name: Publish Staging Pull Request +on: + workflow_dispatch: + pull_request: + branches: + - staging + +jobs: + publish_event: + runs-on: ubuntu-latest + name: Publish staging pull request. + steps: + - run: | + curl -i \ + -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${{ secrets.TRIGGER_PUBLISH_STAGING_PR_TOKEN }}" \ + https://api.github.com/repos/awalsh128/cache-apt-pkgs-action-ci/dispatches \ + -d '{"event_type":"staging_pull_request"}' \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a10a977 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021 Andrew Walsh + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f7296c --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# cache-apt-pkgs-action + +[![License: Apache2](https://shields.io/badge/license-apache2-blue.svg)](https://github.com/awalsh128/fluentcpp/blob/master/LICENSE) +[![GitHub Actions status](https://github.com/awalsh128/cache-apt-pkgs-action/workflows/Tests/badge.svg?branch=main&event=push)](https://github.com/awalsh128/cache-apt-pkgs-action/actions?query=workflow%3ATests) + +This action allows caching of Advanced Package Tool (APT) package dependencies to improve workflow execution time. + +## Documentation + +This action is a composition of [actions/cache](https://github.com/actions/cache/README.md) and the `apt` utility. Some actions require additional APT based packages to be installed in order for other steps to be executed. Packages can be installed when ran but can consume much of the execution workflow time. + +## Usage + +### Pre-requisites + +Create a workflow `.yml` file in your repositories `.github/workflows` directory. An [example workflow](#example-workflow) is available below. For more information, reference the GitHub Help Documentation for [Creating a workflow file](https://help.github.com/en/articles/configuring-a-workflow#creating-a-workflow-file). + +### Inputs + +* `key` - Unique key representing the cache being used. +* `packages` - Space delimited list of packages to install. + +### Outputs + +* `cache-hit` - A boolean value to indicate a cache was found for the packages requested. + +### Cache scopes + +The cache is scoped to the key and branch. The default branch cache is available to other branches. + +See [Matching a cache key](https://help.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key) for more info. + +### Example workflow + +This was a motivating use case for creating this action. + +```yaml +name: Documentation + +on: push + +jobs: + + build_and_deploy_docs: + runs-on: ubuntu-latest + name: Build Doxygen documentation and deploy + steps: + - uses: actions/checkout@v2 + - uses: awalsh128/cache-apt-pkgs-action-action@v1 + with: + cache_key: doxygen_env + packages: dia doxygen doxygen-doc doxygen-gui doxygen-latex graphviz mscgen + + - name: Build + run: | + cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@4.1.5 + with: + branch: gh-pages + folder: ${{github.workspace}}/build/website +``` + +## Creating a cache key + +A cache key can include any of the contexts, functions, literals, and operators supported by GitHub Actions. + +For example, using the [`hashFiles`](https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#hashfiles) function allows you to create a new cache when dependencies change. + +```yaml + - uses: awalsh128/cache-apt-pkgs-action@v1 + with: + cache_key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + packages: dot +``` + +Additionally, you can use arbitrary command output in a cache key, such as a date or software version: + +```yaml + # http://man7.org/linux/man-pages/man1/date.1.html + - name: Get Epoch Seconds + id: get-epoch-sec + run: | + echo "::set-output name=epoch_sec::$(/bin/date +%s)" + shell: bash + + - uses: awalsh128/cache-apt-pkgs-action@v1 + with: + cache_key: ${{ runner.os }}-${{ steps.get-epoch-sec.outputs.epoch_sec }}-${{ hashFiles('**/lockfiles') }} + packages: dot +``` + +See [Using contexts to create cache keys](https://help.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows#using-contexts-to-create-cache-keys) + +## Cache Limits + +A repository can have up to 5GB of caches. Once the 5GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted. diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..9e60c7c --- /dev/null +++ b/action.yml @@ -0,0 +1,41 @@ +name: 'Cache APT Packages' +description: 'Install APT based packages and cache them for future runs.' +author: awalsh128 + +inputs: + packages: + description: 'Space delimited list of packages to install.' + required: true + default: '' + +outputs: + cache-hit: + description: 'A boolean value to indicate a cache was found for the packages requested.' + value: ${{ steps.load-pkg-cache.outputs.cache-hit }} + +runs: + using: "composite" + steps: + - name: Validate Packages + run: ${{ github.action_path }}/validate_pkgs.sh ${{ inputs.packages }} + + - name: Create Cache Key + run: echo ${{ inputs.packages }} | sed 's/[\s,]+/\n/g' | sort > /tmp/package_list.txt + shell: bash + + - name: Load Package Cache + id: load-pkg-cache + uses: actions/cache@v2 + with: + path: ~/cache-apt-pkgs + key: cache-apt-pkgs_${{ hashFiles('/tmp/package_list.txt') }} + + - name: Install and Cache Packages + run: ${{ github.action_path }}/install_and_cache.sh ~/cache-apt-pkgs ${{ inputs.packages }} + shell: bash + if: steps.cache-primes.outputs.cache-hit != 'true' + + - name: Restore Cached Packages + run: ${{ github.action_path }}/run.sh ~/cache-apt-pkgs / + shell: bash + if: steps.cache-primes.outputs.cache-hit != 'true' diff --git a/install_and_cache_pkgs.sh b/install_and_cache_pkgs.sh new file mode 100755 index 0000000..e73b978 --- /dev/null +++ b/install_and_cache_pkgs.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Directory that holds the cached packages. +cache_dir=$1 +# List of the packages to use. +packages="${@:2}" + +mkdir -p $cache_dir +for package in $packages; do + cache_filepath=$cache_dir/$package.tar.gz + + echo "* Clean installing $package... " + sudo apt-get --yes install $package + + echo "* Caching $package to $cache_filepath..." + # Pipe all package files (no folders) to Tar. + dpkg -L $package | + while IFS= read -r f; do + if test -f $f; then echo $f; fi; + done | + xargs tar -czf $cache_filepath -C / +fi + +echo "Action complete. ${#packages[@]} package(s) installed and cached." diff --git a/restore_pkgs.sh b/restore_pkgs.sh new file mode 100755 index 0000000..c83c6ea --- /dev/null +++ b/restore_pkgs.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Directory that holds the cached packages. +cache_dir=$1 +# Root directory to untar the cached packages to. +# Typically filesystem root '/' but can be changed for testing. +cache_restore_root=$2 + +for cache_filepath in $(ls $cache_dir); do + echo "* Restoring $package from cache $cache_filepath... " + sudo tar -xf $cache_filepath -C $cache_restore_root + sudo apt-get --yes --only-upgrade install $package +fi + +echo "Action complete. ${#packages[@]} package(s) restored." diff --git a/validate_pkgs.sh b/validate_pkgs.sh new file mode 100755 index 0000000..09e3e52 --- /dev/null +++ b/validate_pkgs.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +packages=$1 + +echo -n "* Validating action arguments... "; +if [ "$packages" == "" ]; then + echo "aborted." + echo "* Packages argument cannot be empty." >&2 + exit 1 +fi +for package in $packages; do + apt-cache search ^$package$ | grep $package > /dev/null + if [ $? -ne 0 ]; then + echo "aborted." + echo "* Package '$package' not found." >&2 + exit 2 + fi +done +echo "done."