Build a GitHub Actions Marketplace using the Github CLI, Jekyll and Tailwind CSS

4 minute read

TLDR; see the repo or the demo website for more details.

When you work with GitHub, you most likely use GitHub Actions as well. A great way to execute all kinds of tasks, like interacting with Git, performing a build, running tests, or deploying to an environment. There are a lot of different actions available since it is so easy to create one. With some metadata, you can add your actions to the GitHub marketplace for others to find.

That all looks good and nice, but that flexibility also comes with a price; security. As everybody can create actions, they can also add (not always on purpose) vulnerabilities to that action. You should check the actions source code to see if it is okay to use. And when you are using it, make sure to stick it to a version (like a sha) so that even if the code is changed, you still point to the version you vetted. Although this provides some protection, it might not be enough. My colleague Rob Bos wrote an excellent blog about this, highlighting all the risks and solutions. So if you follow his advice, you will end up with your own organization with GitHub Actions that you allow others to use. Discoverability comes into play now; how do people find which actions are available?

Rob has created a GitHub Marketplace for this, and I took his idea to experiment with some other tools as well. I wanted a static site, so server-generated, using Jekyll, which works great with GitHub pages. I also wanted to use Tailwind CSS to style it. A very flexible CSS framework. Next, the collection of actions needed to be simple, so I wanted to use the GitHub CLI tool. This CLI allows you to perform all kinds of operations against the GitHub API.

Collection GitHub actions

GitHub actions are nothing more than repos with an action YAML file inside. Simply put, we need to iterate through all the repositories in an organization and find the action file.

There is a cli command called gh repo list that lists all the repositories in an organization. It retrieves only 30 repositories at a time, so you need to use the --limit option to get all the repositories. I ended up using the direct API call, which supports pagination.

So to fetch all repositories and loop through them, we can use this command and only return the name property.

repos=$(gh api /orgs/$org/repos --paginate --jq '.[].name')
for repo in $repos; do
  echo "Collecting $repo"
done

We now need to check if the file called action.yml actually exists by downloading it. The API command helps here again:

actionfilename="action.yml"
action=$(gh api -H 'accept: application/vnd.github.v3.raw' "repos/$org/$repo/contents/$actionfilename") || false

This command will directly read the raw contents of the file from Git. We need to handle the case when this file does not exist. It might be available under the yaml file extension, so we need to check for that.

if [ $? -ne 0 ]; then       
  actionfilename="action.yaml"
  action=$(gh api -H 'accept: application/vnd.github.v3.raw' "repos/$org/$repo/contents/$actionfilename") || false
fi

If the content is available, we can parse the YAML using the shyaml library.

name=$(echo "$action" | shyaml -q get-value name)
description=$(echo "$action" | shyaml -q get-value description)
author=$(echo "$action" | shyaml -q get-value author "")

We also want to have the readme file, so we can show that on the detail page. There is another API endpoint that retrieves this for us.

readme=$(gh api -H 'accept: application/vnd.github.v3.raw' "repos/$org/$repo/readme") || false

When we fork an existing repository, we do not get the releases, but do get the tags. They represent versions, so we also fetch those:

tags=$(gh api /repos/$org/$repo/git/refs/tags --paginate --jq .[].ref)
versions=""
for tag in $tags; do
  tag=$(echo $tag | cut -d/ -f3)
  versions+=" - $tag\n"     
done

The tags are in the form of refs/tags/v1.0.0, so we need to cut the refs/tags/ part, and add them to a formatted string.

The last step is to save this to a markdown file, which Jekyll will render to a static page.

echo -e "---\nname: $name\ndescription: $description\nrepo: $org/$repo\nauthor: $author\nmetadata: $actionfilename\nversions:\n$versions---\n\n$readme" > $folder/$repo.md

You can find the complete file in the GitHub repo.

Showing the results

When the bash script has run, we will have a collection of markdown files inside the _actions folder. The workflow file (or when building locally) will convert those into html so they can be directly viewed in the browser. Since it is a Jekyll site, we can use templating and liquid to place the content elements in the right place. A demo of the result can be found here.

Conclusion

Using the GitHub CLI it is relatively simple to gather the data needed. The GitHub Actions workflow provides the credentials, and the CLI is already installed on the GitHub Build Agent. Hosting a static website on GitHub pages with Jekyll is also a simple task and offers a lot of flexibility. Feel free to fork this project and use it to your own needs. The readme.md file and the _config.yml files are the ones to look at to get started. Configure the workflow file to point to your organization and push the code back to GitHub and configure GitHub pages to have your own marketplace website.

Leave a comment