Skip to content

Latest commit

 

History

History
310 lines (233 loc) · 9.58 KB

README.md

File metadata and controls

310 lines (233 loc) · 9.58 KB

typescript-action status

TypeScript action + SLSA generation template

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.

Getting Started

You may be starting from different starting points depending on your situation.

  1. 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.
  2. 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.

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

Code in Main

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)

...

Change action.yml

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

Change the Code

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.

Publish to a distribution branch

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

Validate

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! 🚀

Usage:

After testing you can create a v1 tag to reference the stable and latest V1 action

SLSA Reusable Workflow

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.

Adding SLSA Provenance to an Existing GitHub Action

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 the Reusable Workflow

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 Reusable Workflow inputs

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

Pass Secrets to the Delegator Workflow

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 the Internal Action

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"
          }
        }
      ]
    }
  ]
}