Skip to content

sxzz/actionspack

actionspack

Open on npmx npm downloads Unit Test

actionspack is a lockfile-first GitHub Actions workflow packer. It lets you author workflows in .github/workflows/src/, lock every remote workflow/action dependency in .github/workflow.lock.yml, and generate pinned workflows in .github/workflows/.

It currently supports inlining composite actions and safely transformable reusable workflows. JavaScript and Docker actions are pinned as external dependencies instead of being bundled.

Why actionspack?

GitHub Actions workflows often depend on reusable workflows and actions from other repositories. You may want to author those dependencies with convenient floating refs like @main in .github/workflows/src/, but generated workflows should be reproducible and reviewable.

actionspack gives workflows a lockfile mechanism similar to pnpm. It locks remote workflows and actions in .github/workflow.lock.yml, inlines everything that can be transformed safely into the local repository, and pins anything that cannot be inlined to a fixed SHA.

To update workflow and action dependencies, run actionspack update periodically. The updated lockfile and generated workflows are normal repository files, so git diff shows exactly which dependencies changed and what generated workflow output changed.

Install

npm i actionspack

Usage

Put authored workflows under .github/workflows/src/:

# .github/workflows/src/ci.yml
name: CI

on:
  push:

jobs:
  test:
    uses: owner/repo/.github/workflows/test.yml@main

Then run:

npx actionspack

actionspack defaults to actionspack pack. It writes:

  • .github/workflow.lock.yml
  • .github/workflows/ci.yml

Generated workflows are safe to commit. Existing lockfile SHAs are reused until you explicitly run actionspack update.

When you want to refresh workflow/action dependencies:

npx actionspack update
git diff

Review the dependency SHA changes in .github/workflow.lock.yml and the resulting generated workflow changes before committing.

VS Code

Generated workflows should not be edited by hand. Consider marking them as read-only in your workspace settings:

{
  "files.readonlyInclude": {
    ".github/workflows/*.yml": true
  }
}

Commands

actionspack pack

Scan source workflows, resolve missing dependencies, update the lockfile, and write generated workflows.

actionspack scan

Update the lockfile graph shape only. This adds newly discovered dependencies and removes unreachable ones without refreshing existing SHAs.

actionspack update [package]

Refresh all locked dependencies, or only the selected package. By default this also packs workflows. Use --lockfile-only to update only .github/workflow.lock.yml.

actionspack verify

Check that generated workflows are current and contain no unsupported unpinned remote references.

actionspack tree
actionspack why <package>
actionspack diff
actionspack diff --json

Inspect the lockfile dependency tree, explain why a package is present, or compare the current lockfile with HEAD.

Configuration

actionspack.yml is optional. Without it, actionspack discovers .github/workflows/src/*.yml and .github/workflows/src/*.yaml, then writes matching generated workflows to .github/workflows/*.yml.

Use explicit entries when you need custom paths:

$schema: ./actionspack.schema.json

entries:
  - source: .github/workflows/src/ci.yml
    output: .github/workflows/ci.yml

Use external to pin a workflow or action without bundling it:

external:
  - actions/checkout
  - owner/repo/path

The same configuration can be supplied through CLI flags:

actionspack pack \
  --entry .github/workflows/src/ci.yml:.github/workflows/ci.yml \
  --external actions/checkout

Packing Rules

Composite actions are recursively inlined when runs.using is composite. Inputs are substituted from caller with values or action defaults. Missing required inputs fail closed.

Reusable workflows are inlined only when they use workflow_call and can be transformed into local jobs deterministically. Unsupported cases, unresolved refs, unsafe reusable workflows, and leftover remote uses fail closed.

JavaScript actions, Docker actions, and docker:// references are not bundled yet. They are pinned to locked SHAs as external dependencies.

API

import { diff, pack, scan, tree, update, verify, why } from 'actionspack'

await pack()
await update({ packageName: 'owner/repo', lockfileOnly: true })
await verify()

Sponsors

License

MIT License © 2026-PRESENT Kevin Deng

About

Lockfile-first GitHub Actions workflow packer

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors