Use this template to bootstrap the creation of a TypeScript action and SLSA generation reusable workflow. 🚀
Click the Use this Template
and provide the new repo details for your action.
This repo also can serve as an example of how to add SLSA generation reusable workflow to your existing action. It includes a sample GitHub action that can be used as a standalone GitHub action, as well as a reusable workflow that wraps the action and provides SLSA provenance generation.
You may be starting from different starting points depending on your situation.
- If would like to provide SLSA provenance generation for your existing tool but do not have an existing GitHub Action, start from the GitHub Action section. This will guide you through developing a GitHub Action for your tool. You can then proceed to the SLSA Reusable Workflow section to add SLSA provenance generation.
- If you already have a GitHub action and would like to add support for SLSA provenace generation you can start at the SLSA Reusable Workflow section. Be sure to read the section on Adding SLSA Provenance to an Existing GitHub Action.
The GitHub action in this template includes compilation support, tests, a validation workflow, publishing, and versioning guidance.
If you are new, there's also a simpler introduction. See the Hello World JavaScript Action
First, you'll need to have a reasonably modern version of
node
handy. This won't work with versions older than 9, for instance.
Install the dependencies
$ npm install
Build the typescript and package it for distribution
$ npm run build && npm run package
Run the tests ✔️
$ npm test
PASS ./index.test.js
✓ throws invalid number (3ms)
✓ wait 500 ms (504ms)
✓ test runs (95ms)
...
The action.yml defines the inputs and output for your action.
Update the action.yml with your name, description, inputs and outputs for your action.
See the documentation
Most toolkit and CI/CD operations involve async operations so the action is run in an async function.
import * as core from '@actions/core';
...
async function run() {
try {
...
}
catch (error) {
core.setFailed(error.message);
}
}
run()
See the toolkit documentation for the various packages.
Actions are run from GitHub repos so we will checkin the packed dist folder.
Then run ncc and push the results:
$ npm run package
$ git add dist
$ git commit -a -m "prod dependencies"
$ git push origin releases/v1
Note: We recommend using the --license
option for ncc, which will create a license file for all of the production node modules used in your project.
Your action is now published! 🚀
See the versioning documentation
You can now validate the action by referencing ./
in a workflow in your repo (see test.yml)
uses: ./
with:
milliseconds: 1000
See the actions tab for runs of this action! 🚀
After testing you can create a v1 tag to reference the stable and latest V1 action
This repository template includes a reusable workflow that wraps the GitHub Action and generates SLSA provenance for the resulting artifacts using the slsa-github-generator project.
The reusable workflow wraps a GitHub action to provide support for generating non-forgeable SLSA provenance on GitHub that meets the build and provenance requirements for SLSA level 3 and above.
This section covers adding SLSA provenance generation to an existing GitHub Action. This section assumes that you already have a repository set up that contains the code for your GitHub Action. You can skip this section if you creating a new GitHub Action.
For an existing GitHub Action, rather than creating a new repository using this template repository, we will copy some of the code from the template repository into your GitHub Action's repository in order to add support for SLSA provenance.
Set the path to your GitHub Action's repository.
$ GITHUB_ACTION=/path/to/github-action-repo
Copy the reusable workflow to your GitHub Action's repository.
$ mkdir -p ${GITHUB_ACTION}/.github/workflows/
$ cp .github/workflows/slsa3.yml ${GITHUB_ACTION}/.github/workflows/
Copy the internal Action to your GitHub Action's repository.
$ mkdir -p ${GITHUB_ACTION}/internal/action-wrapper
$ cp -r internal/action-wrapper ${GITHUB_ACTION}/internal/
You can now follow the rest of the instructions in your own GitHub Action's repo.
Update .github/workflows/slsa3.yml
and replace the default code. The file
contains a number of TODO
comments that highlight areas that you will need to
update.
Update the workflow inputs and secrets. These should reflect the inputs to your
Action. As GitHub Actions do not differentiate between inputs and secrets, you
should pay extra attention to use secrects
for secret values. You can learn
more about inputs and secrets for reusable workflows in the official
documentation
on:
workflow_call:
inputs:
config-path:
required: true
type: string
secrets:
envPAT:
required: true
Since secrets will not be included in provenance and require extra care you must pass them to the delegator workflow individually.
slsa-run:
needs: [slsa-setup]
permissions:
id-token: write # For signing.
contents: write # For asset uploads.
# actions: read # For the entrypoint.
packages: write # For publishing to GitHub packages.
uses: slsa-framework/slsa-github-generator/.github/workflows/delegator_generic_slsa3.yml@main
with:
slsa-token: ${{ needs.slsa-setup.outputs.slsa-token }}
secrets:
# ** Add your secrets here **
secret1: ${{ secrets.github-token }}
secret2: ${{ secrets.mysecret }}
# ...
Update internal/action-wrapper/action.yml
to pass the right inputs to your
action. You will need to first define the right number of secret inputs (all
other inputs are passed as a JSON map).
inputs:
slsa-workflow-inputs:
description: 'All the inputs formatted as a JSON map'
type: string
required: true
slsa-layout-file:
description: 'Location to store the layout content'
type: string
required: true
slsa-workflow-secret1:
description: 'The GITHUB_TOKEN'
type: string
required: false
slsa-workflow-secret2:
description: 'My Secret'
type: string
required: false
# ...
Next, set the right inputs to the call to your action. You will also need to update the path to your Action if it is not the repository's root directory.
runs:
using: 'composite'
steps:
# NOTE: the repository is already cloned by the caller, so there's no need to
# checkout ourselves.
# This calls the main action, e.g.,
# ./../__TOOL_CHECKOUT_DIR__/<path/to/action> if path is left empty, the
# action's action.yml is located at the root of the repository.
- name: Run main Action
# *** Update this path if it's not the root of the repository ***
uses: ./../__TOOL_CHECKOUT_DIR__
with:
# *** Set these inputs! ***
message: ${{ fromJson(inputs.slsa-workflow-inputs).message }}
file: ${{ fromJson(inputs.slsa-workflow-inputs).file }}
version: ${{ fromJson(inputs.slsa-workflow-inputs).version }}
Finally, you the internal action will need to create an attestation layout
file. This determines the SLSA provenance attestations that are created for the
results of your action. This layout file allows you create attestations
flexibly if you have a large number of outputs for your action. A sample script
that generates an attestation layout file is located at
internal/action-wrapper/generate-layout.sh
.
An example attestation layout file that generates a single attestation for a single output artifact looks like the following:
{
"version": 1,
"attestations": [
{
"name": "attestation-name.intoto",
"subjects": [
{
"name": "myartifact",
"digest": {
"sha256": "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"
}
}
]
}
]
}