Skip to content

refactor: replace adaptivecards-templating and @actions/github with native code#130

Open
ahanoff wants to merge 4 commits into
scope-c-full-template-alignmentfrom
scope-c-drop-deps
Open

refactor: replace adaptivecards-templating and @actions/github with native code#130
ahanoff wants to merge 4 commits into
scope-c-full-template-alignmentfrom
scope-c-drop-deps

Conversation

@ahanoff

@ahanoff ahanoff commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

What

Eliminate the two heaviest dependency chains from the action. Stacked on top of PR #129 (Scope C).

Chain Deps removed Bundle saved
adaptivecards-templating → adaptive-expressions + antlr4ts + xpath + xmldom 4 packages ~900 KB
@actions/github → undici + @octokit/* + @actions/http-client 3 packages (+transitives) ~400 KB
cockatiel (unused — never imported) 1 package

Production dependencies: 6 → 1 (just @actions/core).

Bundle size progression

Stage dist/index.js Build time
Scope B (ncc CJS) 1.7 MB 4.0s
Scope C (rollup ESM minified) 1.5 MB 2.1s
This PR 411 KB 0.67s

73% smaller than Scope C. 76% smaller than Scope B.

How

adaptivecards-templating → manual JS object builder

The library's ${$root.x} template syntax was only used for simple property access. Replaced with JS template literals:

// Before: 2.2MB of transitive deps for this one call
const template = new Template(templateData)
const content = template.expand({$root: data})

// After: zero deps
const content = buildCard(data)  // plain JS object with template literals

An s() helper converts undefined/null'' to match adaptivecards-templating's expansion behavior. All 28 existing tests pass unchanged (they validate output shape, not implementation).

Side benefit: eliminates the JSON injection bug class (PR #121) entirely — no template expansion engine to misuse.

@actions/github → native fetch + env vars

github.context was just GITHUB_* env var reads + GITHUB_EVENT_PATH JSON parse. github.getOctokit(token) was used for exactly 2 GET requests:

// Before
const o = github.getOctokit(token)
const jobs = await o.rest.actions.listJobsForWorkflowRun({owner, repo, run_id})

// After
const jobs = await fetch(`${apiBase}/repos/${owner}/${repo}/actions/runs/${runId}/jobs`, {
  headers: {Authorization: `Bearer ${token}`, ...}
})

No change for consumers — token still comes from github-token: ${{ github.token }}. GITHUB_API_URL respected for GHE compatibility.

Verification

Check Result
npm run lint ✅ clean
npm run build (tsc --noEmit) ✅ clean
npm test (28 tests) ✅ all pass in 0.20s
npm run package ✅ 411 KB in 670ms

Remaining 411 KB

Mostly @actions/core's transitive chain (@actions/http-client, @actions/exec, tunnel, undici). Removing @actions/core itself is possible (replace 5 API calls with process.env + process.exitCode) but diminishing returns — the action would become dependency-free at ~20KB.

Risk

Medium. The card output shape is identical (28 tests confirm). The GitHub API calls match the same endpoints Octokit used. The main risk is edge cases in env var parsing or API response shapes that Octokit's types previously caught at compile time — mitigated by runtime type assertions on fetch responses.

ahanoff added 4 commits June 20, 2026 02:50
…t builder

The adaptivecards-templating library (plus its transitive deps adaptive-expressions and antlr4ts) accounted for ~60% of the bundle size (~900KB minified) but was only used for simple ${$root.x} property interpolation.

Replaced with a plain JS object builder using template literals. The `s()` helper converts undefined/null to empty string, matching adaptivecards-templating's expansion behavior.

Output shape is identical — all 28 existing tests pass unchanged.

Side benefit: eliminates the entire class of JSON injection bugs (PR #121) since there is no longer a template expansion engine to misuse.
… parsing

@actions/github (plus undici, @octokit/*, @actions/http-client — ~400KB minified) was used for only two things:

1. github.context — convenience wrapper around GITHUB_* env vars and GITHUB_EVENT_PATH JSON file. Replaced with direct process.env reads + readFileSync.

2. github.getOctokit(token).rest.actions.{listJobsForWorkflowRun,getWorkflowRun} — two GET requests. Replaced with native fetch() using Authorization: Bearer header.

The token still comes from the github-token action input — no change for consumers. GITHUB_API_URL respected for GHE compatibility.
Production dependencies: 6 -> 1 (@actions/core only).

Removed:

- @actions/github (replaced by native fetch in main.ts)

- adaptivecards-templating (replaced by manual builder in card.ts)

- adaptivecards (transitive of adaptivecards-templating, never directly imported)

- adaptive-expressions (transitive of adaptivecards-templating, peer dep)

- cockatiel (unused — no imports in any source file)

rollup.config.mjs: removed @rollup/plugin-json (no longer needed without adaptivecards-templating's require('./../package.json')). Kept @rollup/plugin-commonjs (still needed for the tunnel CJS package transitively pulled by @actions/core -> @actions/http-client).
Bundle size comparison:

- ncc CJS (Scope B): 1.7 MB

- rollup ESM minified (Scope C): 1.5 MB

- rollup ESM minified (this PR): 411 KB (-73%)

Build time: 2.1s -> 0.67s. Test time: 0.43s -> 0.20s.
@ahanoff ahanoff changed the title Drop adaptivecards-templating + @actions/github (bundle: 1.5MB → 411KB) refactor: replace adaptivecards-templating and @actions/github with native code Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant