I created a custom Github action that deploys a static website to s3, and I want to point out a few details of my process of building a reusable action.

For detailed reference refer to https://docs.github.com/en/actions/creating-actions/about-custom-actions.

There are 3 types of custom actions – docker, javascript and composite.

For deploying to S3 I decided to go with a composite action. There’s already an existing action that I can reuse for aws cli.

Starting point

action.yaml is the main file for a custom Github action.

Refer to https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions for other supported metadata.


name: "Deploy static website to S3"
description: "Deploys static website to S3 without html extension. Works for NextJS."
author: "Ana Bujan - Eisberg Labs"
  using: "composite"
    - uses: unfor19/install-aws-cli-action@v1
    - run: ${{ github.action_path }}/deploy-doc-to-s3.sh "${{inputs.target}}" "${{inputs.exclusions}}" "${{inputs.bucket}}" "${{inputs.dest}}"
      shell: bash
    description: 'Location of static website'
    required: false
    default: ''
    description: 'Regex pattern for files to be excluded from extension removal'
    required: false
    default: ''
    description: 'Bucket name'
    required: true
    description: 'Destination directory path'
    required: true
  icon: "upload-cloud"
  color: "blue"

This composite action combines 2 workflow steps into a single step:

  • installing aws cli that I can use in the next step
  • running the bash script that takes target, exclusions, bucket and dest inputs.

Invoking bash scripts

If invoking bash scripts on your action, do not start with ./script.sh! It will only work on your action repository. Other repositories will report that the script is missing.

My first draft had a ./deploy-doc-to-s3.sh instead of ${{ github.action_path }}/deploy-doc-to-s3.sh.


Branding defines a badge icon that shows up next to your custom action. Supported icons are https://feathericons.com/.


Arguments that action expects in the runtime are defined in inputs field. In a composite action they are only accessible with ${{inputs.<variable_name>}}. All other actions can access input variables also through environment variable INPUT_<VARIABLE_NAME>.

When using this kind of action, inputs are defined in with map of the input parameters:

- name: Deploy to s3
  uses: eisberg-labs/static-website-to-s3@main
    target: nextjs-testapp/out
    dest: next-to-s3/nextjs-testapp
    exclusions: ^nextjs-testapp\/out\/(index).html$
    bucket: ${{ secrets.BUCKET }}

Test the workflow

Before syncing the action to Github, I like to test it locally and I’m able to do so with https://github.com/nektos/act.

Install act

curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash

My usual commands

As for the usual commands, I mostly use act -l for listing of the available jobs, act -j <job_name> -s <your_secret>.

Typing just act will default to jobs that are triggered on push.

run github actions locally

When first working on a repo that will be open sourced, I like to work on a private repository first.

Reason being, I don’t want you to see the mess I’m making on my repo, force-pushing to main, rebasing etc. And when I’m ready, private repo becomes public.

To test private Github actions on nektos act, you have to authenticate. Otherwise this happens:

Unable to clone https://github.com/...  refs/heads/main: authentication required

Create a personal Github access token and use like act -s GITHUB_TOKEN=<your_token>.

Publish to Github marketplace

When you’re ready to publish your action:

  • Go to releases
  • Draft a new release
  • Click on checkbox Publish this Action to the GitHub Marketplace
  • Tag your release, document changelog and publish release.
release custom github action

Notify of
Inline Feedbacks
View all comments