Packages
Import what you need. There’s no manifest file to maintain.
import { differenceInBusinessDays } from "date-fns";import { parsePhoneNumber } from "libphonenumber-js";import { z } from "zod";import httpximport pandas as pdfrom dateutil.relativedelta import relativedeltaTurbyn parses your imports when you publish and installs what they resolve to. require() is detected as well as import.
Pinning
Section titled “Pinning”Pin inline when you need a specific version:
import axios from "axios@1.6.0";Without a pin, the latest version at publish time is resolved and then frozen. Publishing writes a lockfile for that version of your action, and every subsequent execution of it uses exactly that dependency tree.
This matters more than it might appear. A published action keeps running the tree it was tested against, so a package publishing a broken, or compromised. Release upstream doesn’t silently change what’s already running in your workflows. Given the frequency of npm supply-chain attacks, “production runs what you tested” is a property worth having deliberately.
Publishing again re-resolves. So updating dependencies is an explicit act with a version number attached to it, and rolling back rolls back the dependency tree too.
Pre-warmed packages
Section titled “Pre-warmed packages”Common packages are baked into the base sandbox image and cost nothing to load:
JavaScript. @hubspot/api-client, axios, lodash, date-fns, luxon, dayjs, moment, zod, jsonwebtoken, nanoid, csv-parse, googleapis
Python. requests, httpx, pydantic, python-dateutil, pyjwt
Anything else installs on first use and is cached for later runs. The first execution after publishing pays that cost; the rest don’t.
Supply-chain checks
Section titled “Supply-chain checks”At install time, dependencies are screened for known vulnerabilities and typosquatting patterns. Unrecognised packages. Very new, very low download counts, names a character away from a popular package. Are flagged in the editor before you publish.
The check informs, it doesn’t block. You know your dependencies better than a heuristic does.
What doesn’t work
Section titled “What doesn’t work”Native binaries needing compilation. Pure-JS and pure-Python packages, plus common precompiled wheels, work. A package needing a C toolchain at install time generally won’t.
Private registries. Only the public npm and PyPI registries are reachable. A private package needs to be vendored into your action.
Filesystem persistence. Each run gets a fresh sandbox. Writing to disk works during the run; nothing survives it. Use ctx.store for state that needs to persist.
Very large dependency trees. They work, but they lengthen cold starts. If a run feels slow on the first execution after publishing, an oversized tree is the usual reason.