Merge pull request #4009 from github/mbg/action-state/additions

Add `Api`, `ReadOnlyEnv`, and `Base` state features
This commit is contained in:
Michael B. Gale
2026-07-16 10:20:43 +00:00
committed by GitHub
10 changed files with 44 additions and 12 deletions

3
package-lock.json generated
View File

@@ -22,6 +22,9 @@
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.1",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/plugin-retry": "^8.1.0",
"archiver": "^8.0.0",
"fast-deep-equal": "^3.1.3",

View File

@@ -30,6 +30,9 @@
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.1",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/plugin-retry": "^8.1.0",
"archiver": "^8.0.0",
"fast-deep-equal": "^3.1.3",

View File

@@ -1,8 +1,9 @@
import * as core from "@actions/core";
import { ActionsEnv, getActionsEnv } from "./actions-util";
import { Env } from "./environment";
import { FeatureEnablement } from "./feature-flags";
import type { ApiClient } from "./api-client";
import { Env, ReadOnlyEnv } from "./environment";
import type { FeatureEnablement } from "./feature-flags";
import { getActionsLogger, Logger } from "./logging";
import {
ActionName,
@@ -11,7 +12,7 @@ import {
} from "./status-report";
import { getEnv, getErrorMessage } from "./util";
/** Common state that is always available in `ActionState`. */
/** Base state that is available to an Action on startup. */
export interface BaseState {
/** The name of the Action. */
name: ActionName;
@@ -21,6 +22,7 @@ export interface BaseState {
/** Describes different state features that an Action may have. */
export interface FeatureState {
Base: BaseState;
Logger: {
/** The logger that is in use. */
logger: Logger;
@@ -29,10 +31,17 @@ export interface FeatureState {
/** Information about environment variables. */
env: Env;
};
ReadOnlyEnv: {
env: ReadOnlyEnv;
};
Actions: {
/** Access to Actions-related functionality. */
actions: ActionsEnv;
};
Api: {
/** A GitHub API client. */
apiClient: ApiClient;
};
FeatureFlags: {
/** Information about enabled feature flags. */
features: FeatureEnablement;
@@ -44,7 +53,7 @@ export type StateFeature = keyof FeatureState;
/** Constructs the intersection of all state types identifies by `Fs`. */
export type FieldsOf<Fs extends readonly StateFeature[]> = Fs extends []
? BaseState
? Record<never, never>
: Fs extends [
infer Head extends StateFeature,
...infer Tail extends readonly StateFeature[],
@@ -60,7 +69,7 @@ export type ActionState<Fs extends readonly StateFeature[]> = FieldsOf<Fs>;
* Each Action can then augment the `state` further if additional features are required.
*/
export type ActionMain = (
state: ActionState<["Logger", "Env", "Actions"]>,
state: ActionState<["Base", "Logger", "Env", "Actions"]>,
) => Promise<void>;
/** A specification for a CodeQL Action step. */

View File

@@ -212,7 +212,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
await runAutobuild(config, BuiltInLanguage.go, logger);
}
async function run({ startedAt, logger }: ActionState<["Logger"]>) {
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

View File

@@ -1,5 +1,8 @@
import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import { type Octokit } from "@octokit/core";
import { type PaginateInterface } from "@octokit/plugin-paginate-rest";
import { type Api } from "@octokit/plugin-rest-endpoint-methods";
import * as retry from "@octokit/plugin-retry";
import { getActionVersion, getRequiredInput } from "./actions-util";
@@ -43,10 +46,13 @@ export interface GitHubApiExternalRepoDetails {
apiURL: string | undefined;
}
/** The type of GitHub API client we use. */
export type ApiClient = Octokit & Api & { paginate: PaginateInterface };
function createApiClientWithDetails(
apiDetails: GitHubApiCombinedDetails,
{ allowExternal = false } = {},
) {
): ApiClient {
const auth =
(allowExternal && apiDetails.externalRepoAuth) || apiDetails.auth;
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);

View File

@@ -68,7 +68,7 @@ async function sendCompletedStatusReport(
}
}
async function run({ startedAt, logger }: ActionState<["Logger"]>) {
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

View File

@@ -203,7 +203,9 @@ async function sendCompletedStatusReport(
}
}
async function run(actionState: ActionState<["Logger", "Env", "Actions"]>) {
async function run(
actionState: ActionState<["Base", "Logger", "Env", "Actions"]>,
) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

View File

@@ -90,7 +90,7 @@ async function sendCompletedStatusReport(
async function run({
startedAt,
logger,
}: ActionState<["Logger"]>): Promise<void> {
}: ActionState<["Base", "Logger"]>): Promise<void> {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

View File

@@ -193,7 +193,15 @@ export function getTestActionsEnv(): ActionsEnv {
}
/** For testing purposes, we make all available state features accessible in `TestEnv`. */
type AllState = ["Logger", "Env", "Actions", "FeatureFlags"];
type AllState = [
"Base",
"Logger",
"Env",
"ReadOnlyEnv",
"Actions",
"Api",
"FeatureFlags",
];
/** Initialise a fresh `ActionState<AllState>` value. */
export function initAllState(
@@ -205,6 +213,7 @@ export function initAllState(
logger: new RecordingLogger(),
env: getTestEnv(),
actions: getTestActionsEnv(),
apiClient: github.getOctokit("123"),
features: createFeatures([]),
...overrides,
};

View File

@@ -54,7 +54,7 @@ async function sendSuccessStatusReport(
}
}
async function run({ startedAt, logger }: ActionState<["Logger"]>) {
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
try {