@effected/github 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,24 @@ var WorkflowRunStatus = class extends Schema.Class("WorkflowRunStatus")({
26
26
  return this.status === "completed";
27
27
  }
28
28
  };
29
+ /**
30
+ * The run a `workflow_dispatch` created, as GitHub reports it when asked.
31
+ *
32
+ * @remarks
33
+ * Returned by {@link WorkflowDispatchShape.dispatchWithRun}. `runId` is usable
34
+ * directly with {@link WorkflowDispatchShape.runStatus} and
35
+ * {@link WorkflowDispatchShape.cancelRun}.
36
+ *
37
+ * @public
38
+ */
39
+ var DispatchedRun = class extends Schema.Class("DispatchedRun")({
40
+ /** The created run's numeric id. */
41
+ runId: Schema.Int,
42
+ /** The run's API URL. */
43
+ runUrl: Schema.String,
44
+ /** The run's web URL. */
45
+ htmlUrl: Schema.String
46
+ }) {};
29
47
  const DEFAULT_INTERVAL = Duration.seconds(10);
30
48
  const DEFAULT_TIMEOUT = Duration.minutes(5);
31
49
  /**
@@ -60,6 +78,7 @@ var WorkflowDispatch = class WorkflowDispatch extends Context.Service()("@effect
60
78
  /** An in-memory double; unstubbed members die naming themselves. */
61
79
  static makeTest = (overrides = {}) => ({
62
80
  dispatch: overrides.dispatch ?? (() => unstubbed("dispatch")),
81
+ dispatchWithRun: overrides.dispatchWithRun ?? (() => unstubbed("dispatchWithRun")),
63
82
  runStatus: overrides.runStatus ?? (() => unstubbed("runStatus")),
64
83
  cancelRun: overrides.cancelRun ?? (() => unstubbed("cancelRun")),
65
84
  list: overrides.list ?? Effect.sync(() => unstubbed("list")),
@@ -77,6 +96,14 @@ const statusOf = (raw) => WorkflowRunStatus.make({
77
96
  ...raw.conclusion != null ? { conclusion: raw.conclusion } : {},
78
97
  url: raw.html_url
79
98
  });
99
+ /** GitHub's `workflow-dispatch-response`; the id is `number | bigint` in the generated types. */
100
+ const DispatchResponse = Schema.Struct({
101
+ workflow_run_id: Schema.Union([Schema.Number, Schema.BigInt]),
102
+ run_url: Schema.String,
103
+ html_url: Schema.String
104
+ });
105
+ const decodeDispatchResponse = Schema.decodeUnknownEffect(DispatchResponse);
106
+ const decodeDispatchedRun = Schema.decodeUnknownEffect(DispatchedRun);
80
107
  const make = (client) => {
81
108
  const dispatch = Effect.fn("WorkflowDispatch.dispatch")(function* (workflow, ref, inputs) {
82
109
  const { owner, repo } = yield* Repo;
@@ -94,6 +121,30 @@ const make = (client) => {
94
121
  ...inputs !== void 0 ? { inputs } : {}
95
122
  });
96
123
  });
124
+ const dispatchWithRun = Effect.fn("WorkflowDispatch.dispatchWithRun")(function* (workflow, ref, inputs) {
125
+ const { owner, repo } = yield* Repo;
126
+ yield* Effect.annotateCurrentSpan({
127
+ owner,
128
+ repo,
129
+ workflow,
130
+ ref
131
+ });
132
+ const data = yield* client.request("POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", {
133
+ owner,
134
+ repo,
135
+ workflow_id: workflow,
136
+ ref,
137
+ ...inputs !== void 0 ? { inputs } : {},
138
+ return_run_details: true
139
+ });
140
+ if (data === "" || data === null) return Option.none();
141
+ const run = yield* decodeDispatchResponse(data).pipe(Effect.flatMap((raw) => decodeDispatchedRun({
142
+ runId: numericId(raw.workflow_run_id),
143
+ runUrl: raw.run_url,
144
+ htmlUrl: raw.html_url
145
+ })), Effect.catchTag("SchemaError", (error) => Effect.fail(GitHubError.decode("WorkflowDispatch.dispatchWithRun", "GitHub returned unexpected run details", error))));
146
+ return Option.some(run);
147
+ });
97
148
  const runStatus = Effect.fn("WorkflowDispatch.runStatus")(function* (runId) {
98
149
  const { owner, repo } = yield* Repo;
99
150
  yield* Effect.annotateCurrentSpan({
@@ -126,6 +177,7 @@ const make = (client) => {
126
177
  });
127
178
  return {
128
179
  dispatch,
180
+ dispatchWithRun,
129
181
  runStatus,
130
182
  cancelRun: Effect.fn("WorkflowDispatch.cancelRun")(function* (runId) {
131
183
  const { owner, repo } = yield* Repo;
@@ -154,8 +206,8 @@ const make = (client) => {
154
206
  attempts
155
207
  });
156
208
  const dispatchedAt = new Date(yield* Clock.currentTimeMillis).toISOString();
157
- yield* dispatch(workflow, ref, options?.inputs);
158
- const findRun = Effect.gen(function* () {
209
+ const dispatched = yield* dispatchWithRun(workflow, ref, options?.inputs);
210
+ const findRun = Option.isSome(dispatched) ? Effect.map(runStatus(dispatched.value.runId), Option.some) : Effect.gen(function* () {
159
211
  const match = (yield* client.paginate("GET /repos/{owner}/{repo}/actions/runs", {
160
212
  owner,
161
213
  repo,
@@ -179,4 +231,4 @@ const make = (client) => {
179
231
  };
180
232
 
181
233
  //#endregion
182
- export { WorkflowDispatch, WorkflowRunStatus };
234
+ export { DispatchedRun, WorkflowDispatch, WorkflowRunStatus };
package/index.d.ts CHANGED
@@ -4253,6 +4253,25 @@ export declare class WorkflowRunStatus extends WorkflowRunStatus_base {
4253
4253
  /** Has the run finished, whatever the outcome? */
4254
4254
  get isDone(): boolean;
4255
4255
  }
4256
+ declare const DispatchedRun_base: Schema.Class<DispatchedRun, Schema.Struct<{
4257
+ /** The created run's numeric id. */
4258
+ readonly runId: Schema.Int;
4259
+ /** The run's API URL. */
4260
+ readonly runUrl: Schema.String;
4261
+ /** The run's web URL. */
4262
+ readonly htmlUrl: Schema.String;
4263
+ }>, {}>;
4264
+ /**
4265
+ * The run a `workflow_dispatch` created, as GitHub reports it when asked.
4266
+ *
4267
+ * @remarks
4268
+ * Returned by {@link WorkflowDispatchShape.dispatchWithRun}. `runId` is usable
4269
+ * directly with {@link WorkflowDispatchShape.runStatus} and
4270
+ * {@link WorkflowDispatchShape.cancelRun}.
4271
+ *
4272
+ * @public
4273
+ */
4274
+ export declare class DispatchedRun extends DispatchedRun_base {}
4256
4275
  /**
4257
4276
  * One workflow defined in the repository.
4258
4277
  *
@@ -4293,8 +4312,34 @@ interface PollOptions {
4293
4312
  * @public
4294
4313
  */
4295
4314
  interface WorkflowDispatchShape {
4296
- /** Fire a `workflow_dispatch` event. GitHub answers 204 with no run id. */
4315
+ /**
4316
+ * Fire a `workflow_dispatch` event and discard GitHub's answer; use
4317
+ * {@link WorkflowDispatchShape.dispatchWithRun} when the run id is needed.
4318
+ */
4297
4319
  readonly dispatch: (workflow: string, ref: string, inputs?: Record<string, string>) => Effect.Effect<void, GitHubError, Repo>;
4320
+ /**
4321
+ * Fire a `workflow_dispatch` event and report the run it created.
4322
+ *
4323
+ * @remarks
4324
+ * Sends the same request as {@link WorkflowDispatchShape.dispatch} with
4325
+ * `return_run_details: true`. GitHub then answers 200 with the run's id and
4326
+ * URLs, which arrive as `Option.some` of a {@link DispatchedRun}; a 200 body
4327
+ * that does not have that shape fails with a `decode` `GitHubError`.
4328
+ *
4329
+ * A 204 with no body is `Option.none()`. That is what a GitHub Enterprise
4330
+ * Server predating the field answers — it ignores `return_run_details` and
4331
+ * still dispatches — so it is **not a failure**: the workflow was dispatched,
4332
+ * the server just did not say which run it created.
4333
+ *
4334
+ * Under `GitHubClient.layerFixture`, stub the 204 the way every other
4335
+ * no-body route is stubbed: answer the dispatch route with `null`. `""`
4336
+ * (what octokit itself hands back for an empty body) works too.
4337
+ *
4338
+ * Every failure of the request itself (a 404 for an unknown workflow, a 422
4339
+ * for a workflow without a `workflow_dispatch` trigger, …) is the
4340
+ * `GitHubError` the client classified, passed through unchanged.
4341
+ */
4342
+ readonly dispatchWithRun: (workflow: string, ref: string, inputs?: Record<string, string>) => Effect.Effect<Option.Option<DispatchedRun>, GitHubError, Repo>;
4298
4343
  /** Read one workflow run's status. */
4299
4344
  readonly runStatus: (runId: number) => Effect.Effect<WorkflowRunStatus, GitHubError, Repo>;
4300
4345
  /**
@@ -4330,8 +4375,15 @@ interface WorkflowDispatchShape {
4330
4375
  * The wait is `Effect.repeat` with a predicate over the **success** value, so
4331
4376
  * "not finished yet" is never an error. If the run is not found finished
4332
4377
  * within `poll.timeout`, it fails with a `rejected` `GitHubError` (status
4333
- * 408). The run is matched by branch, creation time and workflow path, so
4334
- * concurrent dispatches of the same workflow on the same ref can be confused.
4378
+ * 408).
4379
+ *
4380
+ * The run is identified by {@link WorkflowDispatchShape.dispatchWithRun}:
4381
+ * when GitHub reports the run it created, exactly that run is polled, so
4382
+ * concurrent dispatches cannot be confused. When it does not (a 204 from a
4383
+ * GitHub Enterprise Server predating `return_run_details`), the run is found
4384
+ * instead by branch, creation time and workflow path — and on that fallback
4385
+ * alone, concurrent dispatches of the same workflow on the same ref can be
4386
+ * confused.
4335
4387
  */
4336
4388
  readonly dispatchAndWait: (workflow: string, ref: string, options?: {
4337
4389
  readonly inputs?: Record<string, string> | undefined;
package/index.js CHANGED
@@ -27,7 +27,7 @@ import { RepositorySecurity } from "./RepositorySecurity.js";
27
27
  import { RepositoryVariable } from "./RepositoryVariable.js";
28
28
  import { Ruleset } from "./Ruleset.js";
29
29
  import { ExtraPermission, PermissionGap, PermissionLevel, PermissionResult, TokenPermissionError, TokenPermissions } from "./TokenPermissions.js";
30
- import { WorkflowDispatch, WorkflowRunStatus } from "./WorkflowDispatch.js";
30
+ import { DispatchedRun, WorkflowDispatch, WorkflowRunStatus } from "./WorkflowDispatch.js";
31
31
  import { CLOSING_KEYWORDS, harvestIssueReferences, parseBareLineReference } from "@effected/github-references";
32
32
 
33
- export { ActionsOidc, ActionsOidcClaims, Annotation, AnnotationLevel, AppIdentity, ArtifactMetadata, Attestation, AttestationListEntry, AttestationRecord, BotIdentity, CLOSING_KEYWORDS, CheckConclusion, CheckRun, CheckRunOutput, CheckRunRef, CodeScanning, CommentMarker, CommentOnceResult, CommentRecord, CommitComparison, CommitFile, CommitRef, CommitSummary, DeploymentEnvironment, ExtraPermission, FileChange, FileContent, FileDeletion, FileMode, FileStatus, GRAPHQL_ONLY_SETTINGS, GitBranch, GitCommit, GitHubApp, GitHubAppError, GitHubClient, GitHubCommit, GitHubContent, GitHubError, GitHubErrorKind, GitHubFixtures, GitHubGraphQLError, GitHubIssue, GitHubRelease, GitHubRepository, GitHubValidationCode, GitHubValidationEntry, GitTag, GraphQLDocument, GraphQLErrorEntry, Installation, InstallationToken, InstallationTokenStore, InvalidRepoRefError, IssueInfo, LinkedIssue, MergeMethod, PageOptions, PermissionGap, PermissionLevel, PermissionResult, PullRequest, PullRequestComment, PullRequestInfo, RateLimitSnapshot, RawFailure, ReleaseAsset, ReleaseInfo, Repo, RepoRef, RepositorySecret, RepositorySecurity, RepositoryVariable, RetryPolicy, Ruleset, SECURITY_ANALYSIS_STATUS_FIELDS, SemverTag, StorageRecordInput, TagRef, TokenPermissionError, TokenPermissions, WorkflowDispatch, WorkflowRunStatus, harvestIssueReferences, parseBareLineReference, repositoryPatch, transformSecurityAndAnalysis, versionFromTag };
33
+ export { ActionsOidc, ActionsOidcClaims, Annotation, AnnotationLevel, AppIdentity, ArtifactMetadata, Attestation, AttestationListEntry, AttestationRecord, BotIdentity, CLOSING_KEYWORDS, CheckConclusion, CheckRun, CheckRunOutput, CheckRunRef, CodeScanning, CommentMarker, CommentOnceResult, CommentRecord, CommitComparison, CommitFile, CommitRef, CommitSummary, DeploymentEnvironment, DispatchedRun, ExtraPermission, FileChange, FileContent, FileDeletion, FileMode, FileStatus, GRAPHQL_ONLY_SETTINGS, GitBranch, GitCommit, GitHubApp, GitHubAppError, GitHubClient, GitHubCommit, GitHubContent, GitHubError, GitHubErrorKind, GitHubFixtures, GitHubGraphQLError, GitHubIssue, GitHubRelease, GitHubRepository, GitHubValidationCode, GitHubValidationEntry, GitTag, GraphQLDocument, GraphQLErrorEntry, Installation, InstallationToken, InstallationTokenStore, InvalidRepoRefError, IssueInfo, LinkedIssue, MergeMethod, PageOptions, PermissionGap, PermissionLevel, PermissionResult, PullRequest, PullRequestComment, PullRequestInfo, RateLimitSnapshot, RawFailure, ReleaseAsset, ReleaseInfo, Repo, RepoRef, RepositorySecret, RepositorySecurity, RepositoryVariable, RetryPolicy, Ruleset, SECURITY_ANALYSIS_STATUS_FIELDS, SemverTag, StorageRecordInput, TagRef, TokenPermissionError, TokenPermissions, WorkflowDispatch, WorkflowRunStatus, harvestIssueReferences, parseBareLineReference, repositoryPatch, transformSecurityAndAnalysis, versionFromTag };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/github",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "private": false,
5
5
  "description": "Typed GitHub REST and GraphQL services over the octokit core request surface, with app auth and resource helpers.",
6
6
  "keywords": [