-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from wayofdev/feat/cache
- Loading branch information
Showing
3 changed files
with
273 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Custom S3 cache | ||
|
||
This action allows caching dependencies and saving them in an AWS S3 bucket to reuse in other jobs and workflows to improve workflow execution time. | ||
|
||
|
||
## Inputs | ||
|
||
* `cache_action` | ||
|
||
Specify what to do with the cache: save to an s3 bucket or restore from the s3 bucket into `cache_path`. | ||
|
||
- Type: string | ||
- Required | ||
- Possible values: save, restore | ||
|
||
* `cache_path` | ||
|
||
Absolute or relative path to a folder with cache. When cache_action is **save** the path itself will not be saved, only the contents of the directory (including all subdirectories). When cache_action is **restore** all folders in `cache_path` will be created first and cache will be restored from the S3 bucket into this folder. | ||
|
||
- Type: string | ||
- Required | ||
- Default: . | ||
|
||
* `s3_bucket_name` | ||
|
||
AWS S3 bucket name which will be used to save cache to and restore it from. | ||
|
||
- Type: string | ||
- Required | ||
|
||
* `cache_key` | ||
|
||
A cache key which is used only to save cache to S3 bucket | ||
|
||
- Type: string | ||
- Required only when `cache_action` is **save** | ||
|
||
* `restore_keys` | ||
|
||
An ordered list of keys to use for restoring cache from the s3 bucket | ||
|
||
- Type: list of strings | ||
- Required only when `cache_action` is **restore** | ||
|
||
You can specify multiple keys by putting each key on its own line: | ||
```yaml | ||
restore_keys: |- | ||
${{ runner.os }}-cache-${{ hashfiles('**/.package-lock.json') }} | ||
${{ runner.os }}-cache | ||
``` | ||
The first matching key will be restored. | ||
## Environment Variables | ||
- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`(Required) - credential with access to provided AWS S3 bucket name | ||
- `AWS_REGION`(Required) - AWS region. | ||
|
||
## Example Cache Workflow | ||
|
||
### Save Cache | ||
|
||
```yaml | ||
name: Create cache | ||
on: push | ||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Create cache | ||
uses: wayofdev/gh-actions/actions/s3-cache@v1 | ||
with: | ||
cache_action: save | ||
cache_path: ${GITHUB_WORKSPACE}/.cache | ||
s3_bucket_name: my_s3_bucket | ||
cache_key: ${{ runner.os }}-cache-${{ hashfiles('**/.package-lock.json') }} | ||
env: | ||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
AWS_REGION: ${{ secrets.AWS_REGION }} | ||
``` | ||
|
||
### Restore Cache | ||
|
||
```yaml | ||
name: Restore cache | ||
on: push | ||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Create cache | ||
uses: wayofdev/gh-actions/actions/s3-cache@v1 | ||
with: | ||
cache_action: restore | ||
cache_path: ${GITHUB_WORKSPACE}/.cache | ||
s3_bucket_name: my_s3_bucket | ||
restore_keys: | | ||
${{ runner.os }}-cache-${{ hashfiles('**/.package-lock.json') }} | ||
${{ runner.os }}-cache | ||
env: | ||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
AWS_REGION: ${{ secrets.AWS_REGION }} | ||
``` | ||
|
||
### 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` function allows you to create a new cache when dependencies change. The `hashFiles` function is specific to GitHub Actions. | ||
|
||
```yaml | ||
cache_key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} | ||
``` | ||
|
||
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 Date | ||
id: get-date | ||
run: | | ||
echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT | ||
shell: bash | ||
- uses: wayofdev/gh-actions/actions/s3-cache@v1 | ||
with: | ||
cache_action: save | ||
cache_path: ${GITHUB_WORKSPACE}/.cache | ||
s3_bucket_name: my_s3_bucket | ||
cache_key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/lockfiles') }} | ||
``` | ||
|
||
See [GitHub Contexts and Expressions](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context) for more cache key examples. | ||
|
||
## Limitations | ||
|
||
This action has not been tested on self-hosted runners or when running inside a container. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
name: 'Custom S3 cache' | ||
description: 'Save and restore cache artifacts from AWS s3 bucket' | ||
author: Alina Freydina | ||
|
||
inputs: | ||
cache_action: | ||
description: "An action to do with cache: save or restore" | ||
required: true | ||
cache_path: | ||
description: Absolute or relative path where cache will be restored to or saved from | ||
required: true | ||
default: . | ||
s3_bucket_name: | ||
description: AWS S3 bucket name to save cache to or restore cache from | ||
required: true | ||
cache_key: | ||
description: A cache key wich used only to save cache to s3 bucket | ||
required: false | ||
restore_keys: | ||
description: 'An ordered list of keys to use for restoring cache from s3 bucket' | ||
required: false | ||
|
||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Run action script | ||
run: $GITHUB_ACTION_PATH/cache.sh | ||
shell: bash | ||
env: | ||
INPUT_CACHE_ACTION: "${{ inputs.cache_action }}" | ||
INPUT_CACHE_PATH: "${{ inputs.cache_path }}" | ||
INPUT_S3_BUCKET_NAME: "${{ inputs.s3_bucket_name }}" | ||
INPUT_CACHE_KEY: "${{ inputs.cache_key }}" | ||
INPUT_RESTORE_KEYS: "${{ inputs.restore_keys }}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -euo pipefail | ||
|
||
function save_cache() { | ||
|
||
if [[ $(aws s3 ls s3://${S3_BUCKET}/${CACHE_KEY}/ --region $AWS_REGION | head) ]]; then | ||
echo "Cache is already existed for key: ${CACHE_KEY}" | ||
else | ||
echo "Saving cache for key ${CACHE_KEY}" | ||
|
||
tmp_dir="$(mktemp -d)" | ||
(cd $CACHE_PATH && tar czf "${tmp_dir}/archive.tgz" ./*) | ||
size="$(ls -lh "${tmp_dir}/archive.tgz" | cut -d ' ' -f 5 )" | ||
|
||
aws s3 cp "${tmp_dir}/archive.tgz" "s3://${S3_BUCKET}/${CACHE_KEY}/archive.tgz" --region $AWS_REGION > /dev/null | ||
copy_exit_code=$? | ||
rm -rf "${tmp_dir}" | ||
echo "Cache size: ${size}" | ||
|
||
if [[ "${copy_exit_code}" == 0 ]]; then | ||
echo "Cache saved successfully for key: ${CACHE_KEY}" | ||
fi | ||
fi | ||
} | ||
|
||
function restore_cache() { | ||
|
||
for key in ${RESTORE_KEYS}; do | ||
if [[ $(aws s3 ls s3://${S3_BUCKET}/ --region $AWS_REGION | grep $key | head) ]]; then | ||
k=$(aws s3 ls s3://${S3_BUCKET}/ --region $AWS_REGION | grep $key | head -n 1 | awk '{print $2}') | ||
tmp_dir="$(mktemp -d)" | ||
mkdir -p $CACHE_PATH | ||
|
||
aws s3 cp s3://${S3_BUCKET}/${k//\//}/archive.tgz $tmp_dir/archive.tgz --region $AWS_REGION > /dev/null | ||
tar xzf "${tmp_dir}/archive.tgz" -C $CACHE_PATH | ||
|
||
echo "Restoring cache for key ${key}" | ||
du -sm ${CACHE_PATH}/* | ||
exit 0 | ||
else | ||
echo "Cache with key $key not found." | ||
fi | ||
done | ||
} | ||
|
||
# Check if all necessary variables are set | ||
|
||
if [[ -z "$INPUT_CACHE_ACTION" && -z "$INPUT_S3_BUCKET_NAME" ]]; then | ||
echo "::error::Required inputs are missing: cache_action, s3_bucket_name and either cache_key (if cache_action is save) or restore_keys (if cache_action is restore) must be set." | ||
exit 1 | ||
|
||
fi | ||
|
||
if [[ "$INPUT_CACHE_ACTION" != 'save' ]] && [[ "$INPUT_CACHE_ACTION" != 'restore' ]]; then | ||
echo "::error::Incorrect cache_action. Must be 'save' or 'restore'." | ||
exit 1 | ||
fi | ||
|
||
if [[ "$INPUT_CACHE_ACTION" == "save" && -z "$INPUT_CACHE_KEY" ]]; then | ||
echo "::error::Required inputs are missing: cache_action, s3_bucket_name and either cache_key (if cache_action is save) or restore_keys (if cache_action is restore) must be set." | ||
exit 1 | ||
fi | ||
|
||
if [[ "$INPUT_CACHE_ACTION" == "restore" && -z "$INPUT_RESTORE_KEYS" ]]; then | ||
echo "::error::Required inputs are missing: cache_action, s3_bucket_name and either cache_key (if cache_action is save) or restore_keys (if cache_action is restore) must be set." | ||
exit 1 | ||
fi | ||
|
||
if [[ ! -v AWS_ACCESS_KEY_ID || ! -v AWS_SECRET_ACCESS_KEY || ! -v AWS_REGION ]]; then | ||
echo "::error::AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION must be set" | ||
exit 1 | ||
fi | ||
|
||
# Main logic | ||
|
||
if [[ -v INPUT_CACHE_PATH ]]; then | ||
CACHE_PATH=$INPUT_CACHE_PATH | ||
fi | ||
S3_BUCKET=$INPUT_S3_BUCKET_NAME | ||
|
||
if [[ "$INPUT_CACHE_ACTION" == "save" ]]; then | ||
CACHE_KEY=$INPUT_CACHE_KEY | ||
save_cache | ||
else | ||
RESTORE_KEYS=$INPUT_RESTORE_KEYS | ||
restore_cache | ||
fi |