QAWolf CI SDK
:warning: This is an experimental package. Please report any
issues you encounter to your support channel. It should still
provide a better experience than using our internal GraphQL
API directly.
This package provides a TypeScript (CSM and ESM compatible) SDK
to interact with the QA Wolf Customer-facing API.
It exposes two functions associated with the two central endpoints, showcased in
the examples below. There is also an experimental VCS Branch Testing (aka PR Testing)
feature, encapsulated in its own object, that is described at the bottom of this
document.
Note that these functions do not throw. They yield a result object that
contains the outcome of the operation. This outcome should be scrutinized
to determine the status of your CI/CD step/job/action.
Example: Trigger Run on Deployment
ℹ️ See the API documentation page for this endpoint.
import { type DeployConfig, makeQaWolfSdk } from "@qawolf/ci-sdk";
const deployConfig: DeployConfig = {
branch: undefined,
commitUrl: undefined,
deduplicationKey: undefined,
deploymentType: undefined,
deploymentUrl: undefined,
hostingService: undefined,
sha: undefined,
variables: undefined,
};
const { attemptNotifyDeploy } = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
});
(async () => {
const result = await attemptNotifyDeploy(deployConfig);
if (result.outcome !== "success") {
process.exit(1);
}
const runId = result.runId;
})();
Example: Poll for CI Greenlight Status
ℹ️ See the API documentation page for this endpoint.
import { makeQaWolfSdk } from "@qawolf/ci-sdk";
const { pollCiGreenlightStatus } = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
});
(async () => {
const { outcome } = await pollCiGreenlightStatus({
runId,
onRunStageChanged: (current, previous) => {
console.log(current, previous);
},
abortOnSuperseded: false,
});
if (outcome !== "success") {
process.exit(1);
}
})();
Example: Upload Run Input Artifacts
import { type DeployConfig, makeQaWolfSdk } from "@qawolf/ci-sdk";
import fs from "fs/promises";
import path from "path";
const { generateSignedUrlForTempTeamStorage, attemptNotifyDeploy } =
makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
});
(async () => {
const playgroundFileLocation = await uploadRunArtifact("");
if (playgroundFileLocation) {
const deployConfig: DeployConfig = {
branch: undefined,
commitUrl: undefined,
deduplicationKey: undefined,
deploymentType: undefined,
deploymentUrl: undefined,
hostingService: undefined,
sha: undefined,
variables: {
RUN_INPUT_PATH: playgroundFileLocation,
},
};
const result = await attemptNotifyDeploy(deployConfig);
if (result.outcome !== "success") {
process.exit(1);
}
const runId = result.runId;
}
})();
async function uploadRunArtifact(filePath: string): Promise<string> {
const fileName = path.basename(filePath);
const signedUrlResponse = await generateSignedUrlForTempTeamStorage({
destinationFilePath: fileName,
});
if (
signedUrlResponse?.success &&
signedUrlResponse.playgroundFileLocation &&
signedUrlResponse.uploadUrl
) {
const fileBuffer = await fs.readFile(filePath);
const url = signedUrlResponse.uploadUrl;
try {
const response = await fetch(url, {
method: "PUT",
body: fileBuffer,
headers: {
"Content-Type": "application/octet-stream",
},
});
if (!response.ok) {
return "";
}
} catch (error) {
return "";
}
return signedUrlResponse.playgroundFileLocation;
}
return "";
}
VCS Branch Testing (Experimental)
⚠️ This section is not covered by SemVer and will change.
ℹ️ This feature must be activated by QA Wolf. Please reach out to a QA Wolf
representative to enable it, and help you with the setup.
Highlights and Definitions
VCS Branch Testing allows you to set up QA Wolf ephemeral environments associated with
preview deployments (builds) of your product, which code is hosted in VCS branches (e.g.
Git branches). It can be understood as "PR Testing", however the SDK does not need
the concept of PR or MR to operate.
ℹ️ Read our introduction to VCS Branch Testing to get familiar with core concepts. We also provide a more detailed SDK guide over here.
Usage
This SDK exposes an experimental VCS Branch Testing object:
import { makeQaWolfSdk } from "@qawolf/ci-sdk";
const vcsBranchTestingSdk = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
}).experimental_vcsBranchTesting;
The above object contains the following methods:
notifyVCSBranchBuildDeployed
;notifyVCSBranchMergeCanceled
;notifyVCSBranchMergeCompleted
.
Notify Build Deployed notifyVCSBranchBuildDeployed
import {
makeQaWolfSdk,
arbitraryStringToEnvironmentAlias,
} from "@qawolf/ci-sdk";
const { notifyVCSBranchBuildDeployed } = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
}).experimental_vcsBranchTesting;
const baseEnvironmentsMapping = [
{
environmentAlias: "develop",
vcsBranch: "main",
},
];
(async () => {
const baseVcsBranch = "main";
const headVcsBranch = "feature/foo";
const headPreviewUrl = "https://feature-foo.example.com";
const headVcsCommitId = "abcdef";
const optionalHeadCommitUrl = "https://.../commits/abcdef";
const optionalPullOrMergeRequestNumber = 123;
const headEnvironmentAlias = arbitraryStringToEnvironmentAlias(
`org/repo/${headVcsBranch}`,
);
const headEnvironmentName = `org/repo/${headVcsBranch}`;
const headEnvironmentVariables = {
URL: headPreviewUrl,
};
const { outcome } = await notifyVCSBranchBuildDeployed({
baseEnvironmentsMapping,
baseVcsBranch: "main",
headEnvironmentAlias,
headEnvironmentName,
headVcsBranch,
headVcsCommitId,
headCommitUrl: optionalHeadCommitUrl,
headEnvironmentVariables,
pullOrMergeRequestNumber: optionalPullOrMergeRequestNumber,
});
if (outcome !== "success") {
process.exit(1);
}
})();
Notify Merge Canceled notifyVCSBranchMergeCanceled
import { makeQaWolfSdk } from "@qawolf/ci-sdk";
const { notifyVCSBranchMergeCanceled } = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
}).experimental_vcsBranchTesting;
(async () => {
const headVcsBranch = "feature/foo";
const headEnvironmentAlias = arbitraryStringToEnvironmentAlias(
`org/repo/${headVcsBranch}`,
);
const result = await notifyVCSBranchMergeCanceled({
headEnvironmentAlias,
});
if (result.outcome !== "success") {
if (result.abortReason === "head-environment-not-found") {
console.warn(
`Head environment not found. Skipping! The QA Wolf environment was either never created or it was manually deleted.`,
);
return;
}
process.exit(1);
}
})();
Notify Merge Completed notifyVCSBranchMergeCompleted
import {
makeQaWolfSdk,
arbitraryStringToEnvironmentAlias,
} from "@qawolf/ci-sdk";
const { notifyVCSBranchMergeCompleted } = makeQaWolfSdk({
apiKey: "qawolf_xxxxx",
}).experimental_vcsBranchTesting;
const baseEnvironmentsMapping = [
{
environmentAlias: "develop",
vcsBranch: "main",
},
];
(async () => {
const headVcsBranch = "feature/foo";
const headEnvironmentAlias = arbitraryStringToEnvironmentAlias(
`org/repo/${headVcsBranch}`,
);
const baseVcsBranch = "main";
const { outcome } = await notifyVCSBranchMergeCompleted({
baseEnvironmentsMapping,
headVcsBranch,
headEnvironmentAlias,
baseVcsBranch,
});
if (outcome !== "success") {
process.exit(1);
}
})();
Requirements
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
undici for this purpose, see below snippet:
import { fetch } from "undici";
const sdk = makeQaWolfSdk(
{
apiKey: "qawolf_xxxxx",
},
{ fetch },
);
Versioning
This package follows the SemVer versioning scheme. Additional notes:
- Versions below 1.0.0 still follow this scheme.
- We recommend depending on the
^
range operator for this package, as it will not introduce breaking changes and guarantee
an up-to-date API usage version.
- We will provide a changelog for each release, which will be available in the Changelog section below.
- This package major version will be bumped when an API breaking change is introduced. This won't
happen too often, and we will reach out to you and give advance notice when it does.
- Only top-level exports are considered part of the public API and covered by SemVer.
- Addition of new fields in the API response types are not considered breaking changes.
- Logs and debug messages are not considered part of the public API and can change at any time.
Changelog
v0.20.0
- In
pollCiGreenlightStatus
, bug data fields are now available in the "underReview"
run stage.
v0.19.0
- Add
abortOnSuperseded
option to pollCiGreenlightStatus
to allow for aborting the polling operation when the run is superseded.
v0.18.0
- Add
generateSignedUrlForRunInputsExecutablesStorage
function to generate a signed url used to upload files to the Run Inputs Executables bucket.
v0.17.1
- Add detailed feedback when
generateSignedUrlForTempTeamStorage
fails.
v0.17.0
- Add
generateSignedUrlForTempTeamStorage
function to be used in customer's CI pipeline.
v0.16.0
- Document experimental VCS Branch/PR Testing features.
- Deprecate
experimental_testPreview
and experimental_removeEnvironment
. These methods will be removed in an upcoming release.
v0.15.8
- Fix vcsBranchTesting error handling.
v0.15.7
- Set the default timeout for
fetch
calls to 60 seconds.
v0.15.6
- Add a new dependency,
@qawolf/ci-utils
, and move the LogDriver
interface to it. - Add new experimental features to the SDK in
experimental_vcsBranchTesting
. These
features are not available to customers nor documented yet, but will be advertised
in a near future.
v0.15.5
- Throw a runtime error when
fetch
is not defined. This would be the case for
setups with a NodeJS version < 18.
v0.15.4
- Fix broken ESM build due to lacking
.js
extension in some built files.
v0.15.0
- New
experimental_testPreview
and experimental_deleteEnvironment
methods.
v0.14.0
- New
failReason
, abortReason
and httpStatus
fields added to the attemptNotifyDeploy
function result object to provide more context on why the operation failed or was aborted. - Exported missing TypeScript typings describing result objects and fields for both functions.
v0.13.0
- New
pollTimeout
parameter for pollCiGreenlightStatus
to allow for a custom
timeout in milliseconds for the polling operation. It now defaults to two hours. - New
abortReason
and httpStatus
fields added to the PollCiGreenlightStatus
type
with "aborted"
outcome to provide more context on why the poll was aborted.
v0.12.1
- Export dependencies types from root module.
- Refine
DeployConfig.hostingService
type to match API requirements. - Fix inaccessible changelog file from NPM.
v0.12.0
- Define a more restrictive
LogDriver
interface for easy integration with GHA
core
interface.
v0.11.0
- Support
reproducedBugs
field from CI-greenlight API.
:warning: These are the last breaking changes brought to the 0.x major version.
These are being introduced while the SDK hasn't been advertised to the public yet.
Future changes will follow the SemVer versioning scheme.
BREAKING CHANGES:
pollCiGreenlightStatus
and attemptNotifyDeploy
now return a "result" object
with an outcome
field that can be either "success"
, "failed"
or "aborted"
,
instead of exiting the process with a non-zero code. This will provide more
flexibility to the user to decide how to handle the outcome.
v0.10.3
BREAKING CHANGES:
- Renamed
attemptDeploy
to attemptNotifyDeploy
.
v0.10.2
- Avoid logging dots after URL names in the logs. Dots can confuse terminal
URL detection.
v0.10.1
- Fix TypeScript types visibility.
v0.10.0
Initial release.
Supported Endpoints
/api/deploy_success
/api/v0/ci-greenlight/[root-run-id]