Skip to content

feat: add GHES-compatible artifact action versions via feature flag#31664

Merged
pelikhan merged 6 commits into
mainfrom
salmanmkc/ghes-artifact-compat
May 12, 2026
Merged

feat: add GHES-compatible artifact action versions via feature flag#31664
pelikhan merged 6 commits into
mainfrom
salmanmkc/ghes-artifact-compat

Conversation

@salmanmkc
Copy link
Copy Markdown
Collaborator

@salmanmkc salmanmkc commented May 12, 2026

Summary

Add a ghes-artifact-compat feature flag that makes the compiler emit actions/[email protected] and actions/[email protected] instead of the latest v7/v8.

Problem

GHES does not support @actions/artifact v2.0.0+ — upload-artifact@v4+ / download-artifact@v4+ fail with GHESNotSupportedError on enterprise instances (confirmed still the case on GHES 3.20, the latest release). Users running gh-aw compiled workflows on GHES cannot upload or download artifacts.

Solution

A compile-scoped flag in getActionPin() that automatically covers all 19 artifact action call sites across the compiler without modifying each caller. Users opt in via frontmatter:

features:
  ghes-artifact-compat: true

Or the GH_AW_FEATURES=ghes-artifact-compat environment variable.

When the flag is active, artifact actions are pinned to the highest v3.x releases that avoid the @actions/artifact v2.0.0+ GHES incompatibility. If a required v3 pin is missing from the embedded pin database, compilation fails fast rather than silently emitting a broken v4+ reference.

Files changed

File Change
pkg/actionpins/data/action_pins.json Added v3.2.2 upload-artifact + v3.0.2/v3.1.0 download-artifact pins (verified SHAs)
pkg/constants/feature_constants.go Added GHESArtifactCompatFeatureFlag constant
pkg/workflow/action_pins.go Added compile-scoped GHES compat mode to getActionPin() with fail-fast on missing pins
pkg/workflow/action_pins_test.go Added 3 tests: compat returns v3 SHAs, reset restores defaults, pin-invariant check
pkg/workflow/compiler.go Wired feature flag check in CompileWorkflowData() with defer cleanup

Design decisions

  • Compile-scoped package-level flag (not per-caller threading): 12 of 19 call sites have WorkflowData access, 7 don't. Threading the flag to all 19 callers would be a much larger change with no benefit — compilation is single-threaded per compiler instance.
  • Exact version map (not prefix matching): stable, reviewable, no surprise version drift.
  • Fail-fast on missing pins: if GHES compat is enabled but the v3 pin is missing from action_pins.json, getActionPin() returns empty instead of silently falling back to latest (which would break on GHES).
  • Highest v3.x releases: [email protected] and [email protected] are the highest non-node20 v3 tags (both released Mar 17, 2026).
  • v3 SHAs verified against GitHub API: [email protected]c6a366c94c3e0affe28c06c8df20a878f24da3cf, [email protected]a9bc5e6ef2cb54c177f32aa5726adaa15e7e2d59

Testing

  • TestGetActionPinGHESArtifactCompat — verifies compat mode returns v3 SHAs, non-artifact actions unaffected
  • TestGHESArtifactCompatReset — verifies defer cleanup restores defaults
  • TestGHESArtifactCompatPinsExist — invariant test: every entry in compat map exists in action_pins.json
  • Full pkg/workflow test suite passes (254s)

CI note

CGO build-wasm and test failures are pre-existing on main (golden test snapshots stale from unrelated changes) — not caused by this PR.

Closes #29551

Copilot AI review requested due to automatic review settings May 12, 2026 09:34
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in ghes-artifact-compat feature flag so the compiler can emit GHES-compatible artifact action pins ([email protected], [email protected]) instead of newer versions that fail on GHES.

Changes:

  • Added embedded pins for actions/[email protected] and actions/[email protected].
  • Introduced GHESArtifactCompatFeatureFlag and wired it into compilation to toggle artifact pin selection.
  • Added tests to verify compat-mode pin selection and reset behavior.
Show a summary per file
File Description
pkg/actionpins/data/action_pins.json Adds v3.x artifact action pins (SHA-locked) for GHES compatibility mode.
pkg/constants/feature_constants.go Defines the new ghes-artifact-compat feature flag constant and docs.
pkg/workflow/action_pins.go Implements compat-mode selection in getActionPin() (currently via a package-level toggle).
pkg/workflow/action_pins_test.go Adds unit tests covering compat-mode pin selection and reset.
pkg/workflow/compiler.go Enables compat mode at compile time when the feature flag is present.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment thread pkg/workflow/action_pins.go Outdated
Comment on lines +52 to +74
// ghesArtifactCompatEnabled controls whether artifact actions use GHES-compatible
// versions (v3.x) instead of the latest (v7/v8). GHES does not support
// @actions/artifact v2.0.0+ (upload-artifact@v4+ / download-artifact@v4+).
//
// Set by the compiler at the start of CompileWorkflowData when the
// ghes-artifact-compat feature flag is enabled, and reset via defer.
var ghesArtifactCompatEnabled bool

// ghesArtifactCompatVersions maps artifact action repos to the exact GHES-compatible
// version to use. These are the highest v3.x releases before the breaking v4 change.
var ghesArtifactCompatVersions = map[string]string{
"actions/upload-artifact": "v3.2.2",
"actions/download-artifact": "v3.0.2",
}

// SetGHESArtifactCompat enables or disables GHES-compatible artifact action versions
// for the current compilation scope. Callers must defer SetGHESArtifactCompat(false).
func SetGHESArtifactCompat(enabled bool) {
ghesArtifactCompatEnabled = enabled
if enabled {
actionPinsLog.Print("GHES artifact compatibility mode enabled: artifact actions will use v3.x pins")
}
}
Comment thread pkg/workflow/compiler.go Outdated
Comment on lines +397 to +402
// Enable GHES artifact compatibility when the feature flag is set.
// This makes getActionPin return v3.x artifact actions instead of v7/v8.
if isFeatureEnabled(constants.GHESArtifactCompatFeatureFlag, workflowData) {
SetGHESArtifactCompat(true)
defer SetGHESArtifactCompat(false)
}
Comment thread pkg/workflow/action_pins_test.go Outdated
Comment on lines +1477 to +1484
// Non-artifact actions should be unaffected
checkoutPin := getActionPin("actions/checkout")
if checkoutPin == "" {
t.Fatal("getActionPin(actions/checkout) returned empty with GHES compat")
}
if strings.Contains(checkoutPin, "# v3") {
t.Errorf("GHES compat should not affect non-artifact actions, got: %s", checkoutPin)
}
@salmanmkc salmanmkc force-pushed the salmanmkc/ghes-artifact-compat branch from bc44126 to d887f47 Compare May 12, 2026 09:41
@github-actions

This comment has been minimized.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

@pelikhan
Copy link
Copy Markdown
Collaborator

Feels like we should have a --ghes-compat argument on the compile command and/or a config flag in the aw.json file.

Add a ghes-artifact-compat feature flag that makes the compiler emit
actions/[email protected] and actions/[email protected]
instead of the latest v7/v8. GHES does not support @actions/artifact
v2.0.0+ (upload-artifact@v4+ / download-artifact@v4+), so compiled
workflows fail with GHESNotSupportedError on enterprise instances.

The fix uses a compile-scoped flag in getActionPin() that automatically
covers all 19 artifact action call sites across the compiler without
modifying each caller. Users opt in via frontmatter:

  features:
    ghes-artifact-compat: true

Or the GH_AW_FEATURES=ghes-artifact-compat environment variable.

Closes #29551
@salmanmkc salmanmkc force-pushed the salmanmkc/ghes-artifact-compat branch from d887f47 to 9b8d0da Compare May 12, 2026 13:54
@github-actions
Copy link
Copy Markdown
Contributor

✅ smoke-ci: safeoutputs CLI comment + comment-memory run (25739081440)

Generated by Smoke CI for issue #31664 ·

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot add a --ghes flag to the compile command to turn ghes compatibilty mode and a ghes: true/false field to aw.json config file to turn it on globally in the repo (cli flag wins_. Remove the individual features.ghes-artifact-compat in favor of these. Update GHES docs.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot update the init command to try to detect a GHES deployment and automatically configure aw.json ghes field.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot use the tech-writer agent to add a FAQ entry on configuring GHES + pointing to docs.

pelikhan and others added 2 commits May 12, 2026 07:12
Address three PR review comments:
1. Thread safety: replace package-level `ghesArtifactCompatEnabled` bool with
   `ghesArtifactCompat bool` field on the Compiler struct so each compilation
   instance is independent. No global state mutation during compilation.
2. Defer pattern: remove `defer SetGHESArtifactCompat(false)` — no longer needed
   since the field is set once per CompileWorkflowData call on a per-instance basis.
3. Fragile test assertion: replace `!strings.Contains(checkoutPin, "# v3")` with a
   before/after equality check that verifies the checkout pin is unchanged.

All 19 artifact action call sites updated to use `c.getActionPin()` (Compiler
methods) or accept a `pinAction func(string) string` parameter (standalone
functions). Test helper call sites also updated. Named `ghesArtifactPin` type
added for the compat pins map.

Co-authored-by: pelikhan <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI commented May 12, 2026

@copilot review all comments

All three review comments addressed in commit e7799d2:

  1. Thread safety (action_pins.go:52-75): Removed the ghesArtifactCompatEnabled package-level bool and SetGHESArtifactCompat() function. Added ghesArtifactCompat bool field to the Compiler struct and a (c *Compiler) getActionPin() method that applies compat logic per-instance. All 19 artifact action call sites across 14 files now use c.getActionPin() (Compiler methods) or accept a pinAction func(string) string parameter (standalone functions).

  2. Defer pattern (compiler.go:397-402): The defer SetGHESArtifactCompat(false) is gone — no longer needed. c.ghesArtifactCompat is set once at the start of CompileWorkflowData and lives only for that compilation's lifetime. Since each *Compiler instance is per-compilation, there is no state to restore.

  3. Fragile test assertion (action_pins_test.go:1477-1484): Replaced !strings.Contains(checkoutPin, "# v3") with a before/after equality check — captures the checkout pin before enabling compat and asserts compatCompiler.getActionPin("actions/checkout") == checkoutPinBefore. Also rewrote all three tests to use NewCompiler() with c.ghesArtifactCompat = true instead of the removed SetGHESArtifactCompat().

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all invocation of getActionPin to ensure consistent use since it is now behaving differently between the compiler instance and the global function.

Comment thread pkg/constants/feature_constants.go Outdated
//
// features:
// ghes-artifact-compat: true
GHESArtifactCompatFeatureFlag FeatureFlag = "ghes-artifact-compat"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot remove features flag in favor of compiler flag and aw.json config field

// This is the preferred call site for code running inside a Compiler method, since it
// automatically honours the per-compilation GHES compat flag without any global state.
func (c *Compiler) getActionPin(repo string) string {
if c.ghesArtifactCompat {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot ghesArtifactCompat -> ghesCompat

Comment thread pkg/workflow/compiler.go Outdated
Comment on lines +400 to +403
c.ghesArtifactCompat = isFeatureEnabled(constants.GHESArtifactCompatFeatureFlag, workflowData)
if c.ghesArtifactCompat {
actionPinsLog.Print("GHES artifact compatibility mode enabled: artifact actions will use v3.x pins")
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot remove flag and use compiler state

Copy link
Copy Markdown
Contributor

Copilot AI commented May 12, 2026

@copilot add a --ghes flag to the compile command to turn ghes compatibilty mode and a ghes: true/false field to aw.json config file to turn it on globally in the repo (cli flag wins_. Remove ...

Done in commit 57e4194:

  • --ghes compile flag: gh aw compile --ghes my-workflow.md enables GHES artifact compatibility for that run; takes precedence over aw.json.
  • ghes field in aw.json: {"ghes": true} enables compat globally for the repo. Added to the JSON schema (additionalProperties: false) and parsed in RepoConfig.UnmarshalJSON.
  • Removed features.ghes-artifact-compat: GHESArtifactCompatFeatureFlag constant deleted; compiler.go now reads c.ghesCompatFromCLI || repoConfig.GHES.
  • GHES docs: updated docs/enterprise-configuration.md; new docs/src/content/docs/reference/enterprise-configuration.md reference page added.

@copilot update the init command to try to detect a GHES deployment and automatically configure aw.json ghes field.

Done in commit 57e4194. InitRepository() now calls ensureGHESRepoConfig() at startup, which checks (in order): GITHUB_SERVER_URL env var, GH_HOST env var, then the git remote hostname. A host is GHES if it's not github.com and doesn't end in .ghe.com (GitHub Enterprise Cloud). When GHES is detected, ghes: true is written to .github/workflows/aw.json (idempotent — skipped if already set). Tests added in pkg/cli/init_test.go.

@copilot use the tech-writer agent to add a FAQ entry on configuring GHES + pointing to docs.

Done in commit 57e4194 via the tech-writer agent. Added:

  • FAQ entry in docs/src/content/docs/reference/faq.md: "Can I use agentic workflows on GitHub Enterprise Server (GHES)?" with aw.json and --ghes examples.
  • New reference page docs/src/content/docs/reference/enterprise-configuration.md covering auto-detection, aw.json ghes field, --ghes compile flag, and what action versions change under compat mode.

Copilot AI requested a review from pelikhan May 12, 2026 14:43
@pelikhan pelikhan merged commit bba23ef into main May 12, 2026
1 check failed
@pelikhan pelikhan deleted the salmanmkc/ghes-artifact-compat branch May 12, 2026 14:57
Copilot stopped work on behalf of pelikhan due to an error May 12, 2026 14:57
github-actions Bot added a commit that referenced this pull request May 13, 2026
…rtifact-compat feature flag

Add reference documentation for three user-facing surfaces merged in the
last 24h that had no prior documentation:

- firewall.effective-token-steering (#31796): budget-warning system
  message injection via AWF v0.25.44+
- concurrency.queue and features.group-concurrency-queue (#31764):
  queue:max behavior and opt-out for compiler-generated groups
- features.ghes-artifact-compat (#31664): per-workflow GHES artifact
  compatibility opt-in via frontmatter or GH_AW_FEATURES env var

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
pelikhan added a commit that referenced this pull request May 13, 2026
* docs: document token-steering firewall, concurrency.queue, and ghes-artifact-compat feature flag

Add reference documentation for three user-facing surfaces merged in the
last 24h that had no prior documentation:

- firewall.effective-token-steering (#31796): budget-warning system
  message injection via AWF v0.25.44+
- concurrency.queue and features.group-concurrency-queue (#31764):
  queue:max behavior and opt-out for compiler-generated groups
- features.ghes-artifact-compat (#31664): per-workflow GHES artifact
  compatibility opt-in via frontmatter or GH_AW_FEATURES env var

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* docs: add tip pointing to --ghes and aw.json options in per-workflow section

Co-authored-by: pelikhan <[email protected]>

* docs: remove features.ghes-artifact-compat per-workflow section (feature does not exist)

Co-authored-by: pelikhan <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: pelikhan <[email protected]>
Co-authored-by: Peli de Halleux <[email protected]>
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.

gh aw compile emits artifact actions unsupported on GHES

4 participants