https://github.com/integralist/actions-testing
Testing GitHub Actions
https://github.com/integralist/actions-testing
Last synced: 4 months ago
JSON representation
Testing GitHub Actions
- Host: GitHub
- URL: https://github.com/integralist/actions-testing
- Owner: Integralist
- Created: 2021-12-02T14:29:06.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-05-21T10:10:17.000Z (about 1 year ago)
- Last Synced: 2025-08-17T17:43:38.932Z (10 months ago)
- Language: JavaScript
- Size: 249 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
## Gotchas
- Use single quotes when using operators like `==` (double quotes are invalid).
## Scheduled Jobs (cron)
```yaml
on:
push:
schedule:
- cron: '0 0 1 * *' # https://crontab.guru/every-month
- cron: '*/5 * * * *' # https://crontab.guru/every-5-minutes
```
> **NOTE**: We don't specify a value for `push` key which means run whenever a push is triggered on any branch.
## Automatically cancel in-flight workflows
The `concurrency` setting allows you to restrict a workflow to one 'group' at a time.
Now if you're working on a PR which executes a workflow, then the workflow is likely getting run on every single push to the PR.
An example of why this is a problem is if you have just pushed a _broken_ commit and now you're pushing a new commit on top to fix it, then the workflow from the previous commit (the broken one) is still going to run, it's still going to use up resources but ultimately it's going to fail.
What you want to have happen is when the second commit is pushed, it cancels running the workflow on the previous commit which we know to be broken any way. Hell, it's nice to have this behaviour if you're pushing commits at just a regular pace! Why have a bunch of in-flight workflows runnings on old code.
To get this to work you set both the `concurrency` group _and_ `cancel-in-progress`:
```yaml
concurrency:
group: ${{ github.ref_name }}
cancel-in-progress: true
```
## Persisting Data
GitHub says you can use 'caching' or 'artifacts' to persist data between jobs. But you can also persist data by using the JSON output of one job as the `strategy.matrix` input for another job:
> **NOTE**: Not restricted to using `strategy.matrix`, you can just use the output data however you like. See [later in the README](#using-shared-job-data-to-determine-if-subsequent-job-should-run) for an example.
```yaml
name: example
on: push
jobs:
job1:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: echo "::set-output name=matrix::{\"FOO\":["abc"],\"BAR\":["xyz"]}"
job2:
needs: job1
runs-on: ubuntu-latest
strategy:
matrix: ${{fromJSON(needs.job1.outputs.matrix)}}
env:
FOO: ${{ matrix.FOO }}
BAR: ${{ matrix.BAR }}
steps:
- run: echo ${{ matrix.FOO }} # abc
- run: echo ${{ matrix.BAR }} # xyz
```
> **NOTE**: Be sure to set the value of each matrix field to a list type containing a single entry, otherwise the strategy.matrix will fail to parse the JSON format. This is because a strategy.matrix is typically used to generate multiple 'variants' of a job. By setting a single value inside a list, we ensure there is only ever one job variant generated (i.e. only one job is created), and that single job can simply reference the fields within the matrix as data points of interest.
> **UPDATE**: `::set-output` is deprecated in favour of sending `key=value` to `$GITHUB_OUTPUT` (e.g. `echo "nvmrc-version=$(cat .nvmrc)" >> $GITHUB_OUTPUT`).
### Clarify the cache action
The `actions/cache@v2` works like so...
```yaml
- uses: actions/cache@v2
with:
path: path/to/be/cached
key: ${{ runner.os }}-my-cache-key
```
When the step that implements the action is executed (see above snippet), the cache action simply looks up the cache key (e.g. `Linux-my-cache-key`) and if it finds something in the cache, then it restores the cache to the path you specified (e.g. `path/to/be/cached`).
If the cache action doesn't find anything in the cache, then nothing happens.
Now the important bit: the cache action has a 'post run' event that executes once your job has finished successfully. The cache action will be run again and this time it stores whatever was in your given path into the cache using the key you said it should be stored under.
This means, when it comes to running another job, you need to ensure you define the cache action again (the same as you defined it in your first job). This is so all of what I've just explained will happen again. The only difference is that in the 'post run' event for your next job, when the action gets run again, you'll now see something like...
> Cache hit occurred on the primary key `Linux-my-cache-key`, not saving cache.
Meaning there was nothing else to do. I imagine if there were changes to the files in the given path then it would indicate the cache was updated with the latest files.
## Reusable Workflows
If you have a bunch of setup configuration that is the same between jobs, then you can move that configuration into a separate workflow file that can then be imported and used by each job in your main workflow file.
The following example, demonstrates how to _call_ (i.e. import) a reusable workflow:
```yaml
jobs:
build:
...
deploy:
...
validate-foo:
uses: integralist/actions-testing/.github/workflows/resuable-setup@main # install node, rust, setup env vars etc
steps:
- ...
validate-bar:
uses: integralist/actions-testing/.github/workflows/resuable-setup@main # install node, rust, setup env vars etc
steps:
- ...
```
An example implementation of a reusable workflow would be:
```yaml
name: Reusable workflow for validation scripts
on:
workflow_call:
inputs:
install_node:
type: bool
name:
required: true
type: string
description:
required: true
type: string
script:
required: true
type: string
jobs:
validation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- if: ${{ github.event_name != 'schedule' }}
run: exit 1
- if: ${{ inputs.install_node }}
name: Environment
run: |
echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
- if: ${{ inputs.install_node }}
uses: actions/setup-node@v2
id: node-yarn
with:
node-version: "${{ env.NODE_VERSION }}"
cache: yarn
- name: status update
uses: ouzi-dev/commit-status-updater@v1.1.2
with:
name: ${{ inputs.name }}
description: ${{ inputs.description }}
status: pending
- id: validator
run: ${{ inputs.script }}
- if: ${{ success() }}
name: status update
uses: ouzi-dev/commit-status-updater@v1.1.2
with:
name: ${{ inputs.name }}
description: ${{ steps.validator.outputs.description }}
status: ${{ steps.validator.outputs.status }}
```
## Using shared job data to determine if subsequent job should run
In the following example the `bar` job will not run if the required fields `foo` and `baz` aren't set to `true`.
```yaml
foo:
runs-on: ubuntu-latest
outputs:
data: ${{ steps.footest.outputs.data }}
steps:
- run: |
echo "FOO=true" >> $GITHUB_ENV
echo "BAR=false" >> $GITHUB_ENV
echo "BAZ=true" >> $GITHUB_ENV
- id: footest
run: echo ::set-output name=data::[\"foo=$FOO\", \"bar=$BAR\", \"baz=$BAZ\"]
bar:
needs: foo
if: ${{ contains(needs.foo.outputs.data, 'foo=true') && contains(needs.foo.outputs.data, 'baz=true') }}
runs-on: ubuntu-latest
steps:
- run: echo 'yay! we ran because the fields were set to true'
- run: exit 1
build:
needs: bar
runs-on: ubuntu-latest
steps:
- run: echo 'yay! this job ran as the bar job was successful'
```
## Committing changes directly or opening a PR
```yaml
name: Update Zone Data
permissions:
contents: write
pull-requests: read
id-token: write
on:
# Schedule to run at 9am UTC every day (2pm PST)
schedule:
- cron: '0 09 * * *'
# Allow manual trigger via GitHub UI
workflow_dispatch:
env:
GOPRIVATE: "github.com/fastly"
jobs:
update-dependencies:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Set up private Go modules
run: |
echo "machine github.com login ${{ secrets.GH_PAT_CI }}" > ~/.netrc
- name: Update Dependencies
run: make update-dependencies
# - name: Commit changes
# if: success()
# run: |
# git config --global user.name "github-actions[bot]"
# git config --global user.email "github-actions[bot]@users.noreply.github.com"
# git add .
# git diff-index --quiet HEAD -- || git commit -m "build: update api dependencies [skip ci]" && git push
- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "feat(partnerzones): updates"
committer: GitHub
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
title: "feat(partnerzones): updates"
body: "My PR description."
assignees: Integralist
- name: Slack ping if failure
if: failure()
run: |
curl -Ss -X POST -H 'Content-type: application/json' --data '{"text":"❌ Example API app dependencies update failed"}' ${{ secrets.SLACK_WEBHOOK_URL }}
```