Skip to content

feat: add createOctokit to script context for multi-token workflows#700

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

feat: add createOctokit to script context for multi-token workflows#700
salmanmkc wants to merge 11 commits intomainfrom
salmanmkc/expose-getoctokit

Conversation

@salmanmkc
Copy link
Copy Markdown
Contributor

@salmanmkc salmanmkc commented Mar 1, 2026

Summary

Adds createOctokit(token) to the script execution context alongside github, context, core, etc. It returns a new Octokit client using the same default configuration as the pre-built github client — retry, request logging, orchestration ID, base URL handling, user-agent, and the action's retries input. Callers can optionally override options and plugins.

createOctokit(token: string, options?: OctokitOptions, ...plugins: OctokitPlugin[]): InstanceType<typeof GitHub>

This is a new addition — the existing github client and all other context variables are unchanged.

Motivation

Workflows commonly need multiple authentication tokens — for example, a GITHUB_TOKEN for the current repo and a separate PAT or GitHub App installation token for cross-repo operations. Today the only way to create additional Octokit clients in github-script is through require('@actions/github'), which:

  1. Is an undocumented implementation detail
  2. Returns a bare client — no retry plugin, no request logging, no orchestration ID tracking
  3. Can break if the bundling strategy changes

createOctokit(token) gives users a supported, first-class way to create additional clients that inherit the action's configuration.

Why the name is createOctokit

createOctokit is named intentionally. github-script injects context values as function parameters (new AsyncFunction(...Object.keys(args), source)), so exposing getOctokit directly would collide with scripts that already do:

const { getOctokit } = require('@actions/github')

This throws SyntaxError: Identifier 'getOctokit' has already been declaredconst/let cannot redeclare a function parameter. This pattern appears in ~10 public workflows today (e.g. MetaMask, github/migration-actions). createOctokit avoids the collision and better communicates factory semantics — each call creates a new client instance.

Demo: 8-job before/after workflow run — BEFORE jobs use getOctokit binding and hit SyntaxError; AFTER jobs use createOctokit and pass cleanly.

What createOctokit inherits

createOctokit uses the same default configuration as the built-in github client: base URL handling (GHES / Proxima / base-url input), user-agent with orchestration ID, previews, debug logger, @octokit/plugin-retry, @octokit/plugin-request-log, and the action's retries count — unless explicitly overridden.

Overrides are merged, not replaced: partial request or retry options (e.g. {request: {timeout: 5000}}) are deep-merged with inherited defaults so they don't clobber unrelated fields. undefined values in user options are stripped to prevent accidental clobbering (e.g. {baseUrl: undefined} wiping a GHES URL). Default plugins are always included; user-supplied duplicates are skipped.

Usage

- uses: actions/github-script@v8
  env:
    APP_TOKEN: ${{ secrets.APP_INSTALLATION_TOKEN }}
  with:
    script: |
      // Default client using github-token input
      const { data: issue } = await github.rest.issues.get({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number
      })

      // Second client using a different token
      const app = createOctokit(process.env.APP_TOKEN)
      await app.rest.repos.createDispatchEvent({
        owner: 'my-org',
        repo: 'another-repo',
        event_type: 'trigger-deploy',
        client_payload: { issue: issue.number }
      })

Changes

  • src/create-configured-getoctokit.ts (new) — Factory wrapper around @actions/github's getOctokit with deep-merge, stripUndefined, and plugin deduplication
  • src/main.ts — Creates the wrapped factory and adds createOctokit to the script context
  • src/async-function.ts / types/async-function.d.ts — Added createOctokit to AsyncFunctionArguments type
  • .github/workflows/integration.yml — Added test-get-octokit integration job
  • README.md — Documents createOctokit in the context variables table
  • dist/index.js — Rebuilt

Tests

  • __test__/create-configured-getoctokit.test.ts — 15 tests: option merging, stripUndefined, retry deep-merge, plugin dedup, no-mutation, falsy-but-valid values (0, false, '')
  • __test__/async-function.test.ts — 9 tests: verifies createOctokit is passed through to scripts
  • __test__/getoctokit-integration.test.ts — 4 integration tests for multi-token usage

Checklist

  • createOctokit added to script execution context
  • TypeScript types updated
  • README.md documents createOctokit
  • dist/ rebuilt
  • Unit tests (15 factory + 9 async function)
  • Integration tests (4 multi-token + CI workflow job)
  • No breaking changes — createOctokit is a new binding; existing scripts unaffected

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! (085618e)

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
@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
Extract createConfiguredGetOctokit factory that wraps getOctokit with:
- retry and requestLog plugins (from action defaults)
- retries count, proxy agent, orchestration ID user-agent
- deep-merge for request options so user overrides don't clobber retries
- plugin deduplication to prevent double-application

This ensures secondary Octokit clients created via getOctokit() in
github-script workflows inherit the same defaults as the primary
github client.
@salmanmkc salmanmkc force-pushed the salmanmkc/expose-getoctokit branch from 7f52c47 to 95933be Compare April 8, 2026 20:48
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 8, 2026 20:48 — with GitHub Actions Inactive
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 8, 2026 21:39 — with GitHub Actions Inactive
- Rename context binding from getOctokit to createOctokit to avoid
  SyntaxError when users write const { getOctokit } = require(...)
  in their scripts (~10 public workflows affected)
- Strip undefined values from user options to prevent clobbering
  defaults (e.g. GHES baseUrl)
- Deep-merge retry options alongside request options
- Use nullish coalescing (??) instead of logical OR (||)
- Shallow-copy opts to prevent shared reference mutation
- Add tests: undefined stripping, retry merge, falsy value preservation,
  no mutation of defaults
- 32 tests passing, lint clean, dist rebuilt
@salmanmkc salmanmkc force-pushed the salmanmkc/expose-getoctokit branch from 47f6d8e to 7ece71c Compare April 8, 2026 21:41
@salmanmkc salmanmkc temporarily deployed to debug-integration-test April 8, 2026 21:41 — with GitHub Actions Inactive
@salmanmkc salmanmkc deployed to debug-integration-test April 8, 2026 21:46 — with GitHub Actions Active
@salmanmkc salmanmkc changed the title feat: expose getOctokit in script context for multi-token workflows feat: add createOctokit to script context for multi-token workflows Apr 8, 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.

3 participants