Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoBouteiller authored Dec 20, 2023
0 parents commit afeae4d
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Verify

on: push

jobs:
static_checks:
name: Static Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: bash formatting
run: docker run -v "$PWD":/mnt -w /mnt mvdan/shfmt -ci -i 2 -l -d -- *.sh

- name: shellcheck
run: docker run -v "$PWD":/mnt -w /mnt koalaman/shellcheck -- *.sh test/*.bats

- name: misc files formatting
run: docker run -v "$PWD":/mnt -w /mnt tmknom/prettier --check .

tests:
name: Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Setup BATS
uses: mig4/setup-bats@v1
with:
bats-version: 1.5.0

- name: Run Tests
run: bats --trace --print-output-on-failure test/

self_check:
name: Run this Action
runs-on: ubuntu-latest
needs: [static_checks, tests]
steps:
- uses: actions/checkout@v2

- uses: ./
with:
command: greet
who: world

- uses: ./
id: error_step
continue-on-error: true
with:
command: error

- name: ensure error command failed
if: steps.error_step.outcome == 'success'
run: exit 1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
*.iml
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 rethab

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
[![Verify](https://github.com/rethab/bash-action/actions/workflows/verify.yml/badge.svg)](https://github.com/rethab/bash-action/actions/workflows/verify.yml)

# Create a Bash Action

Use this template to bootstrap the creation of a bash action.:rocket:

This template includes support for static verification, unit testing, self testing, and annotations.

## Create an action from this template

Click the `Use this Template` and provide the new repo details for your action

## What is included?

This action not only demonstrates how to write an action in pure bash, but also includes some best practices such as static verification and testing.

These checks are run as part of the workflow when pushing new code to GitHub and are defined in the file [.github/workflows/verify.yml](.github/workflows/verify.yml)

### Static Verification & Misc Checks

Static verification is the process of analyzing code and checking it for common errors without running it.
One of the most well known tools for this is [shellcheck](https://www.shellcheck.net) and

Besides code verification, this action also includes checks for formatting making sure all scripts and config files are consistently formatted.
The tools used for this task are [shfmt](https://github.com/mvdan/sh) and [prettier](https://prettier.io).

### Testing Bash Scripts

While not very common, it is possible to write unit tests for bash scripts.
A popular tool for this is [bats](https://github.com/bats-core/bats-core).

This action uses bats to run unit tests whenever code is pushed to GitHub.
The tests are in the folder [test](./test) and have the ending `.bats`.

### Testing the Action

While the previously mentioned checks test individual aspects of the action, this part is about running the action itself.

Running the action is straightforward and can be achieved with the following two steps:

```yaml
steps:
- uses: actions/checkout@v2
- uses: ./
```
The tests for this action test both a "positive" and a "negative" scenario.
In the latter case, we want to make sure the action actually fails when we expect it to fail.
Testing for the negative case can be done with the following snippet:
```yaml
steps:
- uses: actions/checkout@v2
- uses: ./
if: error_step
continue-on-error: true
- run: exit 1
if: steps.error_step.outcome == 'success'
```
There are two crucial aspects to this test:
- We make the steps continue even if the step fails by using `continue-on-error: true`
- We fail the following step if the previous step was executed successfully

## Customise this Action for your needs

Okay, so what does this action actually do and how can I extend it?

The [action.yml](action.yml) defines the metadata about this action, such as:

- what is the name of the action
- what inputs does it consume
- how does it run

The last aspect is the most interesting here:
This is a so-called [composite steps action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action).
All this action does is either directly run some commands or call a bash script like you would do from a workflow directly.

## Make it Fancy / Problem Matchers

There is a not-very-well-documented feature in GitHub Actions called [problem matchers](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md).

Let's assume you want to write an action for a tool `validate-json` that emits error message like so:

```bash
> validate-json myconfig.json
ERROR: myconfig.json: line 3, column 3: expected ',' but got ':'
WARNING: myconfig.json: line 7, column 1: unused field
```

When running this action on JSON config files, wouldn't it be nice if you could directly annotate files on GitHub in a PR to give the user nice feedback?
This is what this could look like:
![problem matcher annotation](matcher.png)

Creating these annotations is what problem matches are for:
They allow you to create a regex that matches on the output of your action and then creates the annotations based on that.
All you need to do with the regex is identifying which part of the line the file name, which part is the line number, and what is the error message.

So for the above example, you'd create a regex that says:

- the first word in the line is the severity of the error
- after a colon and a space comes the filename
- after another colon, a space and the word "line" comes the line number
- etc, you get the idea :)

Once you have pieced this regex together, you can put it into a json file install the problem matcher by issuing the following echo:
`echo: "::add-matcher::${GITHUB_ACTION_PATH}/matcher.json"`
Using the environment variable `GITHUB_ACTION_PATH` makes sure the file is correctly referenced regardless of how the action is called.
22 changes: 22 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Bash Action
description: A sample GitHub Action written in Bash

inputs:
command:
description: The command to run. Either "greet" or "error"
required: false
default: greet
who:
description: If the command is greet, the name to greet
required: false

runs:
using: composite
steps:
- run: echo "::add-matcher::${GITHUB_ACTION_PATH}/matcher.json"
shell: bash
- name: Run Script
run: ${GITHUB_ACTION_PATH}/script.sh ${{ inputs.command }} ${{ inputs.who }}
shell: bash
- run: echo "::remove-matcher owner=bash-action::"
shell: bash
16 changes: 16 additions & 0 deletions matcher.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"problemMatcher": [
{
"owner": "bash-action",
"pattern": [
{
"regexp": "^(ERROR|WARNING|INFO):\\s(.+),\\sline\\s(\\d+):\\s(.+)$",
"file": 2,
"line": 3,
"severity": 1,
"message": 4
}
]
}
]
}
Binary file added matcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

set -e

command="$1"
who="$2"

if [[ $command == "greet" ]]; then
echo "hello, $who"
elif [[ $command == "error" ]]; then
echo "ERROR: script.sh, line 11: this is not so good"

Check failure on line 11 in script.sh

View workflow job for this annotation

GitHub Actions / Run this Action

this is not so good
exit 1
fi
12 changes: 12 additions & 0 deletions test/script.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bats

@test "greet: say hello to argument" {
result=$(./script.sh greet linus)
[ "$result" == "hello, linus" ]
}

@test "error: show error and exit with 1" {
run ./script.sh error
[ "$status" -eq 1 ]
[ "$output" = "ERROR: script.sh, line 11: this is not so good" ]
}

0 comments on commit afeae4d

Please sign in to comment.