Skip to content

feat!: expose getOctokit in script context and upgrade to @actions/github v9#700

Open
salmanmkc wants to merge 30 commits intomainfrom
salmanmkc/expose-getoctokit
Open

feat!: expose getOctokit in script context and upgrade to @actions/github v9#700
salmanmkc wants to merge 30 commits intomainfrom
salmanmkc/expose-getoctokit

Conversation

@salmanmkc
Copy link
Copy Markdown
Contributor

@salmanmkc salmanmkc commented Mar 1, 2026

What this does

Upgrades @actions/github to v9 and adds getOctokit to the script context.

Today, if you need a second Octokit client with a different token (GitHub App, PAT, cross-org), you do something like:

const { getOctokit } = require('@actions/github')
const appClient = getOctokit(process.env.APP_TOKEN)

That breaks in v9 because @actions/github is now ESM-only — require() no longer works. This PR replaces that pattern with a built-in getOctokit that's available directly in the script context, no imports needed:

- uses: actions/github-script@v9
  env:
    APP_TOKEN: ${{ secrets.MY_APP_TOKEN }}
  with:
    script: |
      // primary client uses GITHUB_TOKEN as usual
      await github.rest.issues.addLabels({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        labels: ['needs-review']
      })

      // secondary client uses a different token
      const appOctokit = getOctokit(process.env.APP_TOKEN)
      await appOctokit.rest.repos.createDispatchEvent({
        owner: 'my-org', repo: 'deploy', event_type: 'go'
      })

Works for GHES too:

const ghes = getOctokit(process.env.GHES_TOKEN, {
  baseUrl: 'https://github.example.com/api/v3'
})

The secondary client inherits retry settings, user-agent, proxy config, and plugins from the action — so it behaves consistently with the primary github client. request and retry options are deep-merged (so you can tweak one field without losing the rest), while other options like baseUrl or userAgent replace the default outright if you set them.

Why v9

@actions/github v6 → v9 brings updated Octokit types and the orchestration ID user-agent feature (toolkit#2364). The main breaking change is that require('@actions/github') stops working inside scripts — workflows like MetaMask's that use this pattern will need to switch to the injected getOctokit instead.

Other breaking changes:

  • const getOctokit = ... or let getOctokit = ... in scripts will SyntaxError (same as const github = ... today — function parameters can't be redeclared with const/let). Use it directly or use var if you really need to redeclare.
  • Internal @actions/github imports (like @actions/github/lib/utils) may have changed paths.

What's in the diff

New:

  • src/create-configured-getoctokit.ts — factory wrapper (deep merge, undefined stripping, plugin dedup)
  • Tests: 16 factory unit tests, 4 integration tests, CI workflow job with real API calls

Changed:

  • src/main.ts — wires factory into script context
  • src/async-function.ts — v9 type imports, getOctokit in argument types
  • package.json — version 9.0.0, dependency bumps
  • tsconfig.json — ES2022 + bundler resolution
  • README.md — v9 docs, getOctokit section with examples, breaking changes
  • .github/workflows/integration.yml — new getOctokit test job, user-agent test fix

Testing

  • 35 tests across 4 suites, all green
  • 15/15 CI checks passing
  • Live demo — real multi-token workflow reading a private repo via SECOND_PAT, GraphQL queries, cross-identity verification

Copilot AI review requested due to automatic review settings March 1, 2026 00:26
@salmanmkc salmanmkc requested a review from a team as a code owner March 1, 2026 00:26
@salmanmkc salmanmkc temporarily deployed to debug-integration-test March 1, 2026 00:26 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 1, 2026

Hello from actions/github-script! (9206a2b)

Copy link
Copy Markdown

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

This PR exposes getOctokit in the github-script runtime context so user scripts can create additional authenticated Octokit clients (e.g., for multi-token workflows) without relying on require('@actions/github').

Changes:

  • Passes getOctokit into the script execution context in src/main.ts.
  • Extends the AsyncFunctionArguments TypeScript type to include getOctokit with an Octokit-typed signature.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/main.ts Adds getOctokit to the object passed into callAsyncFunction so scripts can access it.
src/async-function.ts Updates the script context type (AsyncFunctionArguments) to type getOctokit and imports Octokit types.
Comments suppressed due to low confidence (2)

src/main.ts:71

  • getOctokit is being passed through directly from @actions/github, so any Octokit clients created inside the user script won’t automatically inherit this action’s configured defaults (e.g., base-url for GHES, user-agent with orchestration ID, retries/request options, and the installed retry/requestLog plugins). This can lead to surprising differences between github and getOctokit(...) behavior. Consider exposing a wrapper that pre-applies the same options/plugins by default (while still allowing callers to override/extend options/plugins when needed).
      github,
      octokit: github,
      getOctokit,
      context,
      core,

src/async-function.ts:20

  • This adds a new deep import from @octokit/core/types, but the codebase already imports Octokit types via @octokit/core/dist-types/types (e.g. src/retry-options.ts). To stay consistent (and to reduce the risk of relying on a non-exported subpath), align the import path with the existing convention or derive the type directly from @actions/github (e.g., type getOctokit as typeof import('@actions/github').getOctokit) so the signature can’t drift from the actual implementation.
import {GitHub} from '@actions/github/lib/utils'
import * as glob from '@actions/glob'
import * as io from '@actions/io'
import type {OctokitOptions, OctokitPlugin} from '@octokit/core/types'

const AsyncFunction = Object.getPrototypeOf(async () => null).constructor

export declare type AsyncFunctionArguments = {
  context: Context
  core: typeof core
  github: InstanceType<typeof GitHub>
  octokit: InstanceType<typeof GitHub>
  getOctokit: (
    token: string,
    options?: OctokitOptions,
    ...additionalPlugins: OctokitPlugin[]
  ) => InstanceType<typeof GitHub>

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

@salmanmkc salmanmkc temporarily deployed to debug-integration-test March 1, 2026 01:30 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test March 9, 2026 11:47 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test March 9, 2026 11:52 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test March 9, 2026 12:02 — with GitHub Actions Inactive
Copilot AI and others added 5 commits March 23, 2026 20:17
- @actions/github: ^6.0.0 → ^9.0.0
- @octokit/core: ^5.0.1 → ^7.0.0
- @octokit/plugin-request-log: ^4.0.0 → ^6.0.0
- @octokit/plugin-retry: ^6.0.1 → ^8.0.0
- Update tsconfig.json to use moduleResolution: "bundler" for ESM exports map support
- Update import paths for new package structures
- Update build:types script for compatible compiler options

Co-authored-by: angel-jiakou <115738347+angel-jiakou@users.noreply.github.com>
Agent-Logs-Url: https://github.com/actions/github-script/sessions/17de5ca1-8bdc-41e4-a06d-ab2d8c2e6e8c
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 7, 2026 15:47 — with GitHub Actions Inactive
@salmanmkc salmanmkc force-pushed the salmanmkc/expose-getoctokit branch from c7fb361 to 2fe016f Compare April 7, 2026 15:50
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 7, 2026 15:50 — with GitHub Actions Inactive
Verifies the real getOctokit from @actions/github creates functional
Octokit clients when invoked through callAsyncFunction, ensuring:
- Secondary clients have full REST/GraphQL API surface
- Secondary clients are independent from primary github client
- GHES base URL option is accepted
- Multiple tokens produce distinct client instances
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 7, 2026 16:08 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 8, 2026 20:41 — with GitHub Actions Inactive
@salmanmkc salmanmkc force-pushed the salmanmkc/expose-getoctokit branch from 1bdc919 to 7f52c47 Compare April 8, 2026 20:44
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 8, 2026 20:44 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 08:00 — with GitHub Actions Inactive
…it name

Replace function-parameter injection with const destructuring + block scope
so that user scripts can shadow injected names (e.g. const { getOctokit } = ...)
without SyntaxError. This eliminates the need for the createOctokit rename and
aligns with the ADR's getOctokit naming.

Changes:
- callAsyncFunction now wraps user source in a block: const {...} = __scope__; { source }
- Renamed createOctokit binding back to getOctokit everywhere
- Added 5 new tests: const/let shadowing, await, return, syntax error, access
- Updated integration workflow and type declarations
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 08:55 — with GitHub Actions Inactive
@salmanmkc salmanmkc changed the title feat: add createOctokit to script context for multi-token workflows feat!: expose getOctokit in script context and upgrade to @actions/github v9 Apr 9, 2026
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 09:03 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 09:10 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 09:22 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 09:28 — with GitHub Actions Inactive
…ards

- Change const to var destructuring in callAsyncFunction so var
  redeclaration and reassignment of injected names still works (v8 compat)
- Add identifier validation for argument keys (defensive for exported API)
- Add __scope__ collision guard
- Fix integration test hardcoded repo name to use github.repository
- Add 4 new tests: var redecl, reassignment, invalid key, __scope__ guard
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 10:02 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 16:03 — with GitHub Actions Inactive
Remove block-scope var destructuring mechanism — use original
function-parameter injection (new AsyncFunction(...keys, source))
to minimize risk.

getOctokit is added as a standard function parameter alongside
github, core, context, etc. The const/let redeclaration collision
is accepted as a documented trade-off in the major version bump.
…otes

- Expand V9 breaking changes section with new features and breaking changes
- Add orchestration ID user-agent as new feature
- Document const/let redeclaration SyntaxError for getOctokit binding
- Add note callout in getOctokit docs section about injected parameter
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 16:33 — with GitHub Actions Inactive
require('@actions/github') no longer works in v9 scripts because
@actions/github v9 is ESM-only. Users who previously used this
pattern (e.g. MetaMask) should use the injected getOctokit instead.
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 16:49 — with GitHub Actions Inactive
Apply stripUndefined to defaultOptions too, not just user options.
Prevents log: undefined and previews: undefined from main.ts opts
from being passed through to the Octokit constructor where they
could interfere with Octokit's own defaults.
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 16:54 — with GitHub Actions Inactive
The error message referenced $expected but the variable was never
set, making failure diagnostics unhelpful.
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 9, 2026 17:06 — with GitHub Actions Inactive
- Deep-copy retry and request when passing opts to factory to prevent
  shared references with the primary client
- Document that request/retry are deep-merged while other options are
  replaced outright
@salmanmkc salmanmkc deployed to debug-integration-test April 9, 2026 17:13 — with GitHub Actions Active
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.

5 participants