@qawolf/ci-sdk 2.8.0 → 3.1.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # QAWolf CI SDK
2
2
 
3
- This package provides a TypeScript (CSM and ESM compatible) SDK
3
+ This package provides a TypeScript, ESM-only SDK
4
4
  to interact with the QA Wolf Customer-facing API.
5
5
 
6
6
  It exposes several functions associated with different endpoints, which are detailed in the table of contents below.
@@ -11,9 +11,11 @@ to determine the status of your CI/CD step/job/action.
11
11
 
12
12
  ## Table of Contents
13
13
 
14
- - [Notify Deployment](#notify-deployment)
14
+ - [API Keys and Workspaces](#api-keys)
15
+ - [Report a Deployment](#report-deployment)
16
+ - [Notify Deployment (deprecated)](#notify-deployment)
15
17
  - [Notify Preview Deployment (Pull Request / Merge Request Testing)](#notify-preview)
16
- - [Poll for CI Greenlight Status](#ci-greenlight)
18
+ - [Poll for CI Greenlight Status (deprecated)](#ci-greenlight)
17
19
  - [Advanced: Poll with Custom Control (Iterator)](#ci-greenlight-iterator)
18
20
  - [Notify a Terminated Ephemeral Environment](#notify-terminated-ephemeral-environment)
19
21
  - [Upload Run Input Artifacts](#upload-artifacts)
@@ -21,10 +23,240 @@ to determine the status of your CI/CD step/job/action.
21
23
  - [Versioning](#versioning)
22
24
  - [Changelog](#changelog)
23
25
 
26
+ <a id="api-keys"></a>
27
+
28
+ ## API Keys and Workspaces
29
+
30
+ Every call you make is authenticated by the `apiKey` you pass to `makeQaWolfSdk`, and every call
31
+ acts on exactly one workspace. How that workspace is determined depends on which kind of API key
32
+ you hold:
33
+
34
+ - A **workspace API key** (also called a team API key) already belongs to a single workspace. It
35
+ implies the workspace, so you never need to name one.
36
+ - An **organization API key** or a **user API key** can reach several workspaces, so it does not
37
+ imply one. You name the workspace by passing `workspaceId` on the call.
38
+
39
+ `workspaceId` is the ID of the workspace (team) you want the call to act on. If you are not sure
40
+ which value to use, ask your QA Wolf representative; `attemptNotifyDeploy` also reports the
41
+ workspace it selected, with its ID, in the warning described below.
42
+
43
+ ### Passing `workspaceId`
44
+
45
+ The field is accepted on every function that acts on a workspace:
46
+
47
+ ```ts
48
+ const { attemptNotifyDeploy, generateSignedUrlForRunInputsExecutablesStorage } =
49
+ makeQaWolfSdk({
50
+ apiKey: "qawolf_xxxxx",
51
+ });
52
+
53
+ // Organization and user API keys must name the workspace on each call.
54
+ await generateSignedUrlForRunInputsExecutablesStorage({
55
+ destinationFilePath: "unityexpo.ipa",
56
+ workspaceId: "your-workspace-id",
57
+ });
58
+
59
+ await attemptNotifyDeploy({
60
+ ...deployConfig,
61
+ workspaceId: "your-workspace-id",
62
+ });
63
+ ```
64
+
65
+ `notifyTerminatedEphemeralEnvironment` accepts `workspaceId` the same way.
66
+
67
+ ### What happens when it is missing
68
+
69
+ Omitting `workspaceId` with an organization or user API key is handled differently depending on the
70
+ call, so it is worth knowing which behavior to expect:
71
+
72
+ | Function | Behavior when `workspaceId` is omitted |
73
+ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | `generateSignedUrlForTempTeamStorage` | Fails. The backend answers HTTP `400` with the message `workspaceId is a required query parameter for organization and user API keys`. |
75
+ | `generateSignedUrlForRunInputsExecutablesStorage` | Same as above — the backend answers HTTP `400`. |
76
+ | `attemptNotifyDeploy` | Succeeds against your organization's first-created workspace and returns a `warning` naming it. |
77
+ | `notifyTerminatedEphemeralEnvironment` | Succeeds against your organization's first-created workspace and returns a `warning` naming it. |
78
+
79
+ > ⚠️ The signed-URL functions **require** `workspaceId` for organization and user API keys — they do
80
+ > not fall back to a default workspace. A `400` from either function, especially when the API key and
81
+ > artifact name are otherwise correct, most often means `workspaceId` was not sent.
82
+
83
+ When this happens, the SDK logs the real status before it reports the outcome, so the log line is
84
+ the reliable signal:
85
+
86
+ ```
87
+ ❌ [callGenerateSignedUrlForRunInputsExecutablesStorage] 400 Bad Request undefined
88
+ 🚫 Unrecoverable error (status 0) when generating signed upload url: Network error, aborting request to generate signed URL. aborting. undefined
89
+ ```
90
+
91
+ The `400 Bad Request` on the first line is the actual response. The `status 0` and "Network error"
92
+ on the second line are misreported and do not indicate a transport problem — read the first line
93
+ and add `workspaceId`.
94
+
95
+ For the same reason, do not branch on `httpStatus` or `abortReason` to detect this case: a failed
96
+ signed-URL request currently reports `httpStatus: 0` and `abortReason: "client-network-error"`
97
+ whatever the real status was. Treat any `success: false` from these two functions as a request that
98
+ did not reach a signed URL, and check the logged status to find out why.
99
+
100
+ Passing `workspaceId` with a workspace API key is unnecessary but harmless, as long as the ID names
101
+ the key's own workspace. Naming a workspace the key cannot reach fails with HTTP `403`.
102
+
103
+ `workspaceId` support was added in `@qawolf/ci-sdk` 2.2.0. On earlier versions, use a
104
+ workspace-scoped API key.
105
+
106
+ <a id="report-deployment"></a>
107
+
108
+ ## Report a Deployment
109
+
110
+ Report a deployment through the public `deployment.reportStatus` API. A deployment's first `success` report is what evaluates your triggers, so this is the call that starts a run.
111
+
112
+ > ⚠️ Triggers only see deployments reported this way. `attemptNotifyDeploy` reports through the legacy `deploy_success` webhook and is deprecated.
113
+
114
+ ### Example
115
+
116
+ ```ts
117
+ import { detectProviderDeploymentId, makeQaWolfSdk } from "@qawolf/ci-sdk";
118
+
119
+ const { deployment } = makeQaWolfSdk({ apiKey: "qawolf_xxxxx" });
120
+
121
+ (async () => {
122
+ // `{ outcome: "detected", ci: "github", id: "18273645-2-deploy" }` on GitHub
123
+ // Actions, Jenkins, GitLab CI, CircleCI, Bitbucket Pipelines and Buildkite;
124
+ // `{ outcome: "unsupported" }` elsewhere, in which case compose the id from
125
+ // your own CI's variables.
126
+ const detected = detectProviderDeploymentId();
127
+ if (detected.outcome !== "detected") process.exit(1);
128
+
129
+ const result = await deployment.reportStatus({
130
+ environment: { name: "staging" },
131
+ metadata: {
132
+ commitSha: process.env.GITHUB_SHA,
133
+ ref: process.env.GITHUB_REF_NAME,
134
+ repository: process.env.GITHUB_REPOSITORY,
135
+ },
136
+ providerDeploymentId: detected.id,
137
+ status: "success",
138
+ workspaceId: "your-workspace-id",
139
+ });
140
+ if (result.outcome !== "reported") {
141
+ // `result.outcome` says why, and the refusal arms carry the server's
142
+ // explanation in `result.message` when it sent one.
143
+ process.exit(1);
144
+ }
145
+ console.log(
146
+ `Reported deployment ${result.deployment.id}: ${result.deployment.url}`,
147
+ );
148
+ })();
149
+ ```
150
+
151
+ The parameters are exactly the input of `deployment.reportStatus`; `ReportDeploymentParams` is derived from the published `@qawolf/api-contracts` package, so the SDK cannot drift from the API. Every field the legacy webhook accepted has an equivalent: `environmentVariables`, `deployTarget`, ephemeral environments through `environment.ephemeral` and `environment.baseEphemeralEnvironment`, and `metadata.repository` / `pullRequestNumber` / `commitSha` / `ref`.
152
+
153
+ ### `status`
154
+
155
+ You choose the status: `pending`, `success`, `failure` or `inactive`. Only a deployment's **first `success`** report evaluates triggers, and it is the only report that does. A `pending` report records the deployment and starts nothing, so report `pending` when the deploy begins if you want it visible, and report `success` when the deploy is actually live — that second call is what starts the run. A trigger added or unpaused after that first `success` does not make the deployment evaluate again; report the deployment under a new `providerDeploymentId` to evaluate it against the current triggers.
156
+
157
+ ### `providerDeploymentId`
158
+
159
+ This is the deployment's identity. Reports that share one update the same deployment, and the deployment stays bound to the environment its first report resolved.
160
+
161
+ It is required, and the SDK never guesses it. Beware that `GITHUB_RUN_ID` alone is **stable across re-runs** and **shared by every job in the workflow**: a re-run of a failed job, or a second job deploying alongside the first, would report into the deployment that already evaluated, create no run, and appear to hang. `detectProviderDeploymentId()` composes an id that is distinct for every job definition and every re-run:
162
+
163
+ | CI system | Detected on | Id |
164
+ | ------------------- | ------------------------------------------------- | -------------------------------------------------------------------------- |
165
+ | GitHub Actions | `GITHUB_ACTIONS` | `GITHUB_RUN_ID`-`GITHUB_RUN_ATTEMPT`-`GITHUB_JOB` |
166
+ | GitLab CI | `GITLAB_CI` | `CI_PIPELINE_ID`-`CI_JOB_ID` |
167
+ | CircleCI | `CIRCLECI` | `CIRCLE_WORKFLOW_ID`-`CIRCLE_BUILD_NUM` |
168
+ | Buildkite | `BUILDKITE` | `BUILDKITE_BUILD_ID`-`BUILDKITE_JOB_ID`-`BUILDKITE_RETRY_COUNT` |
169
+ | Jenkins | `JENKINS_URL` or `JENKINS_HOME` | `BUILD_TAG` |
170
+ | Bitbucket Pipelines | `BITBUCKET_WORKSPACE` or `BITBUCKET_BUILD_NUMBER` | `BITBUCKET_BUILD_NUMBER`-`BITBUCKET_STEP_UUID`-`BITBUCKET_STEP_RUN_NUMBER` |
171
+
172
+ Jenkins needs no job or attempt part: `BUILD_TAG` is `jenkins-<job name>-<build number>`, and Jenkins allocates a fresh build number for every re-run you can start from outside the build. Bitbucket needs all three, because rerunning only the failed steps keeps the build number while parallel steps share both it and the step run number.
173
+
174
+ The helper reports `{ outcome: "missing-variables", missingVariables }` when the CI system is recognized but a variable is absent, and `{ outcome: "unsupported" }` on any other CI, rather than guess an id that would not change on a re-run.
175
+
176
+ #### Concurrent deployments from one job definition
177
+
178
+ The guarantee stops at the job definition. Where one definition deploys more than once at a time, nothing in the environment tells the deployments apart, and the helper alone would hand them all the same id:
179
+
180
+ | CI system | What collides |
181
+ | -------------- | --------------------------------------------------------------------------------------------------- |
182
+ | GitHub Actions | Every leg of a `strategy.matrix` shares `GITHUB_JOB`, `GITHUB_RUN_ID` and `GITHUB_RUN_ATTEMPT`. |
183
+ | Jenkins | A declarative `matrix` or `parallel` block runs its branches inside one build, sharing `BUILD_TAG`. |
184
+ | CircleCI | A job run with `parallelism` shares `CIRCLE_BUILD_NUM` across its containers. |
185
+
186
+ GitLab CI, Buildkite and Bitbucket Pipelines are not affected: `CI_JOB_ID`, `BUILDKITE_JOB_ID` and `BITBUCKET_STEP_UUID` are already distinct for each parallel leg.
187
+
188
+ Pass a `discriminator` for those, and for any job that loops over several environments. It is appended to the composed id:
189
+
190
+ ```ts
191
+ const detected = detectProviderDeploymentId({
192
+ // In GitHub Actions: `discriminator: ${{ toJSON(matrix) }}` through an env
193
+ // variable, or just the one matrix value that names the deployment.
194
+ discriminator: process.env.DEPLOY_ENVIRONMENT,
195
+ });
196
+ ```
197
+
198
+ `detectProviderDeploymentId` also takes `env`, which defaults to `process.env`. Composing the `providerDeploymentId` yourself is always an option; the helper is a convenience, not a requirement.
199
+
200
+ ### `workspaceId`
201
+
202
+ `deployment.reportStatus` requires `workspaceId` for every key kind; a workspace API key must name its own workspace. See [API Keys and Workspaces](#api-keys).
203
+
204
+ ### Result
205
+
206
+ `deployment.reportStatus` returns a discriminated union and never throws for an outcome you are expected to branch on. Branch on `outcome` alone:
207
+
208
+ | `outcome` | Meaning |
209
+ | ----------------------- | ------------------------------------------------------------------------------------------ |
210
+ | `reported` | Carries `deployment`, the whole resource: `id`, `status` and `url`. |
211
+ | `invalid-request` | The report did not satisfy the contract. Carries `message`. |
212
+ | `unauthorized` | The API key is unknown, or may not act on this workspace. Carries `message`. |
213
+ | `not-found` | The workspace or environment named in the report does not exist. Carries `message`. |
214
+ | `conflict` | The `providerDeploymentId` is already bound to a different environment. Carries `message`. |
215
+ | `server-error` | QA Wolf failed to record the report; retrying is reasonable. Carries `httpStatus`. |
216
+ | `unexpected-status` | A status this SDK version does not map. Carries `httpStatus` and `message`. |
217
+ | `network-error` | The request never reached QA Wolf, or the connection dropped. |
218
+ | `invalid-response-body` | QA Wolf answered with a body the contract rejects. |
219
+
220
+ So `if (result.outcome !== "reported") process.exit(1)` is the whole check a gate needs.
221
+
222
+ The response carries the deployment only, not the resulting runs. Waiting for a verdict is not part of this call.
223
+
224
+ ### Migrating from `attemptNotifyDeploy`
225
+
226
+ ```diff
227
+ -const { attemptNotifyDeploy } = makeQaWolfSdk({ apiKey });
228
+ +const { deployment } = makeQaWolfSdk({ apiKey });
229
+
230
+ -const deploy = await attemptNotifyDeploy({
231
+ - branch: process.env.GITHUB_REF_NAME,
232
+ - deploymentType: "staging",
233
+ - hostingService: "GitHub",
234
+ - sha: process.env.GITHUB_SHA,
235
+ -});
236
+ -if (deploy.outcome === "skipped") process.exit(0);
237
+ -if (deploy.outcome !== "success") process.exit(1);
238
+ +const detected = detectProviderDeploymentId();
239
+ +if (detected.outcome !== "detected") process.exit(1);
240
+ +const deploy = await deployment.reportStatus({
241
+ + environment: { name: "staging" },
242
+ + metadata: { commitSha: process.env.GITHUB_SHA, ref: process.env.GITHUB_REF_NAME },
243
+ + providerDeploymentId: detected.id,
244
+ + status: "success",
245
+ + workspaceId: "your-workspace-id",
246
+ +});
247
+ +if (deploy.outcome !== "reported") process.exit(1);
248
+ ```
249
+
250
+ `detectProviderDeploymentId()` composes the run, attempt and job, so two jobs in one workflow do not report into the same deployment. Compose the id yourself only if you would rather not call it, and include the job.
251
+
252
+ Four things move: `deploymentType` becomes `environment.name`; `branch` / `sha` become `metadata`; `providerDeploymentId` is new and required (`deduplicationKey` is subsumed by it); `hostingService` disappears, since the code host comes from your integration. There is no `skipped` outcome: whether a trigger matched is not known at report time. `pollCiGreenlightStatus` takes a run id that only `attemptNotifyDeploy` returns, so a pipeline that waits for a verdict stays on the legacy pair until the successor to polling ships.
253
+
24
254
  <a id="notify-deployment"></a>
25
255
 
26
256
  ## Notify Deployment
27
257
 
258
+ > ⚠️ **Deprecated.** This reports through the legacy `deploy_success` webhook, which triggers do not see. Use [Report a Deployment](#report-deployment) instead. It keeps working and there is no removal date.
259
+
28
260
  Notify us of a successful deployment to trigger a run.
29
261
 
30
262
  > ℹ️ [See the API documentation page for this endpoint](https://qawolf.notion.site/dd72e46ceb7f451dae4e9ef06f64a2cc).
@@ -135,22 +367,34 @@ const { attemptNotifyDeploy } = makeQaWolfSdk({
135
367
 
136
368
  > ⚠️ **Important**: PR/MR testing functionality must be activated by QA Wolf. Please reach out to your QA Wolf representative to enable this feature and help with the setup.
137
369
 
138
- Once enabled, to use PR/MR testing functionality:
370
+ Once enabled, report the preview deployment with [`deployment.reportStatus`](#report-deployment) and name the pull or merge request in `metadata`. The code host comes from your QA Wolf integration, so there is no `hostingService` to pass:
139
371
 
140
- 1. For `GitHub` repositories:
141
- - Preferably, use our [GitHub Action](https://github.com/marketplace/actions/notify-qa-wolf-on-deploy)
142
- - Pass `hostingService: "GitHub"`, `repository` information, and `pullRequestNumber` while notifying a deployment as described in the [Notify Deployment](#notify-deployment) section
372
+ ```ts
373
+ await deployment.reportStatus({
374
+ deployTarget: previewUrl,
375
+ environment: { ephemeral: true, name: `pr-${pullRequestNumber}` },
376
+ metadata: {
377
+ commitSha: process.env.GITHUB_SHA,
378
+ pullRequestNumber,
379
+ ref: process.env.GITHUB_HEAD_REF,
380
+ repository: process.env.GITHUB_REPOSITORY,
381
+ },
382
+ providerDeploymentId: detected.id,
383
+ status: "success",
384
+ workspaceId: "your-workspace-id",
385
+ });
386
+ ```
143
387
 
144
- 2. For `GitLab` repositories:
145
- - Pass `hostingService: "GitLab"`, `repository` information, and `mergeRequestNumber` while notifying a deployment as described in the [Notify Deployment](#notify-deployment) section
388
+ `metadata.pullRequestNumber` names a GitLab merge request too. For a preview with no code hosting integration, pass `deployTarget` and an ephemeral `environment` and leave the repository fields out. `environment.baseEphemeralEnvironment` names the environment an ephemeral one inherits its configuration from.
146
389
 
147
- 3. For `Ephemeral` deployments (no code hosting integration):
148
- - Pass `ephemeralEnvironment: true` and `deploymentUrl` while notifying a deployment as described in the [Notify Deployment](#notify-deployment) section
390
+ The [GitHub Action](https://github.com/marketplace/actions/notify-qa-wolf-on-deploy) and `attemptNotifyDeploy`'s `hostingService` / `mergeRequestNumber` / `ephemeralEnvironment` / `deploymentUrl` fields still work, but they report through the legacy `deploy_success` webhook that triggers do not see.
149
391
 
150
392
  <a id="ci-greenlight"></a>
151
393
 
152
394
  ## Poll for CI Greenlight Status
153
395
 
396
+ > ⚠️ **Deprecated.** This polls a run id that only `attemptNotifyDeploy` returns, so it keeps your pipeline on the legacy `deploy_success` webhook. It keeps working and there is no removal date.
397
+
154
398
  > ℹ️ [See the API documentation page for this endpoint](https://qawolf.notion.site/1b170576efea411fa785842a71e7c99e).
155
399
 
156
400
  ```ts
@@ -407,6 +651,8 @@ const { generateSignedUrlForTempTeamStorage, attemptNotifyDeploy } =
407
651
  async function uploadRunArtifact(filePath: string): Promise<string> {
408
652
  const fileName = path.basename(filePath);
409
653
 
654
+ // Organization and user API keys must also pass `workspaceId` here, or the
655
+ // request fails with a 400. See "API Keys and Workspaces" above.
410
656
  const signedUrlResponse = await generateSignedUrlForTempTeamStorage({
411
657
  destinationFilePath: fileName,
412
658
  });
@@ -444,12 +690,17 @@ async function uploadRunArtifact(filePath: string): Promise<string> {
444
690
  `playgroundFileLocation` field with the same value as `interactiveRunFileLocation`. It is deprecated and
445
691
  will be removed in a future major version — use `interactiveRunFileLocation` instead.
446
692
 
693
+ Both functions require `workspaceId` when you authenticate with an organization or user API key.
694
+ See [API Keys and Workspaces](#api-keys) for details and for what a missing `workspaceId` looks
695
+ like.
696
+
447
697
  <a name="requirements"></a>
448
698
 
449
699
  ## Requirements
450
700
 
451
- This packages will work out of the box with NodeJS ≥ 18. If you are using an older NodeJS version, you will need to pass a `fetch` polyfill function to `makeQaWolfSdk`. We recommend
452
- [undici](https://undici.nodejs.org/) for this purpose, see below snippet:
701
+ This package requires NodeJS ≥ 24.14.1 and < 25 and is published as ESM only. Import it with `import`; `require("@qawolf/ci-sdk")` is not supported.
702
+
703
+ You can still pass your own `fetch` implementation to `makeQaWolfSdk` if you want to route requests through a custom client, for example [undici](https://undici.nodejs.org/):
453
704
 
454
705
  ```ts
455
706
  import { fetch } from "undici";
@@ -484,6 +735,19 @@ This package follows the [SemVer](https://semver.org/) versioning scheme. Additi
484
735
 
485
736
  # Changelog
486
737
 
738
+ ## v3.1.0
739
+
740
+ - New `deployment.reportStatus` function, which reports a deployment through the public `deployment.reportStatus` API — the one your triggers watch. A deployment's first `success` report is what starts a run. Its parameters are the API's own input, taken from the `@qawolf/api-contracts` package, so the SDK cannot drift from the API, and it returns `{ outcome: "reported", deployment }` carrying the deployment's `id`, `status` and `url`. A refusal comes back as one named outcome you can branch on — `invalid-request`, `unauthorized`, `not-found`, `conflict`, `server-error`, `unexpected-status`, `network-error` or `invalid-response-body` — so `if (result.outcome !== "reported")` is the whole check a CI gate needs. See [Report a Deployment](#report-deployment).
741
+ - New `detectProviderDeploymentId` function, which works out the `providerDeploymentId` for you from the variables your CI already sets on GitHub Actions, GitLab CI, CircleCI, Buildkite, Jenkins and Bitbucket Pipelines. The id it builds is different for every job definition and every re-run, so a retried job and two jobs deploying side by side each get their own deployment instead of reporting into one that has already started its run. Where one job definition deploys more than once at a time — a GitHub Actions or Jenkins matrix, a CircleCI job with `parallelism`, a loop over environments — no variable tells the legs apart, so pass a `discriminator` and it is appended to the id. On any other CI it returns `{ outcome: "unsupported" }` rather than guess. Passing `providerDeploymentId` yourself is still required; this helper is optional.
742
+ - Pull request and merge request testing is now documented on `deployment.reportStatus`: pass `metadata.repository` and `metadata.pullRequestNumber` instead of `hostingService`. See [Notify Preview Deployment](#notify-preview).
743
+ - `attemptNotifyDeploy` and `pollCiGreenlightStatus` are deprecated in favour of `deployment.reportStatus`. They report through the legacy `deploy_success` webhook, which triggers do not see. Both keep working unchanged, print no warning, and have no removal date.
744
+
745
+ ## v3.0.0
746
+
747
+ - **Breaking**: this package is now published as ESM only. The CommonJS build (`dist/index.cjs`) and the `require` entry in `exports` are gone, so `require("@qawolf/ci-sdk")` no longer resolves. Import the package with `import` from an ESM module, or load it from CommonJS with a dynamic `await import("@qawolf/ci-sdk")`.
748
+ - **Breaking**: the supported NodeJS range is now `>=24.14.1 <25`, up from `>=20`. Older NodeJS versions are no longer tested or supported.
749
+ - **Breaking**: requires `@qawolf/ci-utils` v2, which carries the same two changes.
750
+
487
751
  ## v2.8.0
488
752
 
489
753
  - `pollCiGreenlightStatus`: a `completed` status now carries optional `message` and `reason`, holding QA Wolf's explanation of the outcome. QA Wolf sends them when it assessed a change as low risk and ran no flows, where `reason` is `low-risk-change`. Both were previously typed only on a `canceled` status, so this explanation was unreadable without a cast. `reason` is an open set of string codes; new ones appear without an SDK release, and both fields are absent against a backend that predates them.
@@ -512,6 +776,10 @@ This package follows the [SemVer](https://semver.org/) versioning scheme. Additi
512
776
  - `attemptNotifyDeploy`: a failed suite result now carries `failureCode` and `failureMessage` alongside the existing `failureReason`. `failureCode` is a stable code to branch on, such as `billing-prevented`, `environment-terminated`, `environment-not-ready`, `test-configuration-error`, or `internal-error`; treat it as an open set, since new codes are added over time. Note that `environment-terminated` is permanent while `environment-not-ready` clears on its own, so retrying is worthwhile only for the second. `failureMessage` is human-readable copy for logs, and the SDK's own failure log line now prints it instead of the internal reason.
513
777
  - `attemptNotifyDeploy`: `failureReason` is deprecated. It still ships unchanged and is safe to keep reading, but it carries an internal diagnostic whose values change without notice. Branch on `failureCode` and display `failureMessage` instead. Both are `undefined` when the SDK talks to a backend that predates them.
514
778
 
779
+ ## v2.2.0
780
+
781
+ - `generateSignedUrlForTempTeamStorage` and `generateSignedUrlForRunInputsExecutablesStorage`: both now accept an optional `workspaceId`, which names the workspace the signed URL is generated for. It is required when authenticating with an organization or user API key, since neither is tied to a single workspace, and without it the request fails with HTTP `400`. Omit it when authenticating with a workspace API key, which already implies one. This entry was missing from the changelog when 2.2.0 was released and is documented here retroactively; see [API Keys and Workspaces](#api-keys).
782
+
515
783
  ## v2.1.0
516
784
 
517
785
  - `attemptNotifyDeploy`: the `deploy_success` response is now validated at runtime instead of being trusted. A response with a non-JSON body, or a body that does not match the expected shape, returns `outcome: "aborted"` with the new `abortReason: "invalid-response-body"`. This is distinct from `"network-error"`, which now covers only transport and body-stream failures rather than also masking unparseable responses. A result the SDK does not recognize (for example a new result kind sent by a newer backend) returns `outcome: "failed"` with the new `failReason: "unknown-result"` instead of being reported as a success. Recognized results are unchanged, and older backends that omit newer fields remain fully supported.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { CiGreenlightStatus, CiGreenlightStatusBase, } from "./lib/api/index.js";
2
2
  export * from "./lib/sdk/dependencies.js";
3
3
  export * from "./lib/sdk/domain/attemptDeploy/types.js";
4
+ export * from "./lib/sdk/domain/detectProviderDeploymentId/index.js";
4
5
  export * from "./lib/sdk/index.js";