Generated GitHub Actions¶
Hollywood generates ordinary GitHub Actions files. There is no custom runtime inside the workflow YAML.
Action metadata¶
generateActionFile(publishImage, {
sourcePath: ".github/actions/containers/publish-image/src/action.ts",
actionsDir: ".github/actions",
});
This produces:
When the source already lives under .github/actions/<name>/src, Hollywood
keeps the generated files in that action directory.
The file contains a normal JavaScript action contract:
name: publish-container-image
description: Build and publish a container image without embedding shell in workflow YAML.
runs:
using: node24
main: dist/index.js
Entrypoint¶
generateActionEntrypointFile(publishImage, {
sourcePath: ".github/actions/containers/publish-image/src/action.ts",
actionsDir: ".github/actions",
exportName: "publishImage",
});
This produces:
import { runGitHubAction } from "@dedalus-labs/hollywood/action-runtime";
import { publishImage } from "./action.ts";
void runGitHubAction(publishImage);
runGitHubAction uses GitHub's official TypeScript packages. Inputs and
outputs go through @actions/core. Commands go through @actions/exec.
Child output streams unchanged. Hollywood adds a compact command group, elapsed
status, and failure annotation without replaying the child output or printing a
runtime stack trace.
Action composition¶
Use call inside a parent action when one public GitHub Action should compose
smaller typed Hollywood actions.
export const release = action({
name: "release",
description: "Compose a release contract.",
inputs,
outputs,
run: async ({ call, input }) => {
const artifacts = await call(resolveArtifacts, {
version: input.version,
});
const metadata = await call(readBuildMetadata, {
uri: artifacts.buildMetadataUri,
});
return assembleRelease(input, artifacts, metadata);
},
});
call does not create nested workflow steps. It invokes the child action in the
same runtime with the same exec, fs, log, runner, and summary services.
Command log color¶
runGitHubAction colors command status lines in auto mode in GitHub Actions
and interactive terminals. NO_COLOR, NODE_DISABLE_COLORS,
FORCE_COLOR=0, and FORCE_COLOR=false disable automatic color. Other
FORCE_COLOR values enable it. Set logColor: "never" when another log
collector renders terminal color codes literally, or logColor: "always" when
testing colored output.
GitHub step summaries do not use terminal color codes. They are rendered as escaped HTML.
Step summaries¶
Use summary.table for GitHub step summaries. Titles and labels are escaped
plain text. Values must be explicitly formatted with summaryText or
summaryCode. There is no raw HTML or Markdown cell format.
import { action, summaryCode, summaryText } from "@dedalus-labs/hollywood/action-runtime";
export const integrationTest = action({
name: "integration-test",
description: "Run a live integration test.",
inputs,
outputs: {},
run: async ({ input, summary }) => {
await summary.table("Integration test", [
{ label: "Environment", value: summaryCode(input.environment) },
{ label: "API base", value: summaryCode(input.apiBase) },
{ label: "Result", value: summaryText("PASS") },
]);
return {};
},
});
Workflow files¶
Set localActionPath on actions you want to call from generated workflows.
Then uses(action, ...) derives ./.github/actions/<path> and preserves the
action's typed inputs.
import { generateWorkflowFile, job, uses, workflow } from "@dedalus-labs/hollywood";
import { defineMatrix, format, gh } from "@dedalus-labs/hollywood/expr";
const build = defineMatrix({
runner: ["ubuntu-latest"],
} as const);
generateWorkflowFile({
sourcePath: "gha/containers/release.ts",
sourceRoot: "gha",
workflowsDir: ".github/workflows",
workflow: workflow({
name: "Container Release",
on: { workflow_dispatch: {} },
concurrency: {
group: format("{0}-{1}", gh.github.workflow, gh.github.ref),
queue: "max",
},
jobs: {
publish_image: job({
"runs-on": build.runner,
strategy: { matrix: build, "max-parallel": 2 },
steps: [
uses(publishImage, {
name: "Publish container image",
with: {
image: "ghcr.io/acme/api",
tag: gh.github.sha,
provenance: "false",
},
}),
],
}),
},
}),
});
The source path is flattened:
becomes:
GitHub gets the flat shape it requires. The source tree keeps the nested shape humans want.
Validation¶
Generated workflow YAML and action metadata pass through upstream GitHub Actions parsers before Hollywood writes files. Invalid generated content fails closed.
CLI¶
Let the CLI discover source files that export Hollywood actions or workflows:
Hollywood discovers exports by shape:
| Export shape | Generated files |
|---|---|
action({ name: "s3-cache" }) |
.github/actions/s3-cache/action.yml and entrypoint |
workflow({ name: "Container Release" }) |
.github/workflows/<flattened-source-path>.yml |
For example, this source tree:
can generate:
Bundle generated actions before GitHub runs them:
Commit dist/index.js with the generated action, or build an ignored bundle in
an earlier workflow step before calling the local action. The workflow YAML can
be committed as-is.
The CLI prints one line per generated file: