@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
158 lines • 5.88 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.releaseSpeculosDeviceCI = exports.createSpeculosDeviceCI = exports.waitForSpeculosReady = void 0;
const axios_1 = __importDefault(require("axios"));
const speculos_transport_1 = require("@ledgerhq/speculos-transport");
const https_1 = __importDefault(require("https"));
const { SEED, GITHUB_TOKEN, AWS_ROLE, CLUSTER, SPECULOS_IMAGE_TAG } = process.env;
const GIT_API_URL = "https://api.github.com/repos/LedgerHQ/actions/actions/";
const START_WORKFLOW_ID = "workflows/161487603/dispatches";
const STOP_WORKFLOW_ID = "workflows/161487604/dispatches";
const GITHUB_REF = "main";
const getSpeculosAddress = (runId) => `https://${runId}.speculos.aws.stg.ldg-tech.com`;
const speculosPort = 443;
function uniqueId() {
const timestamp = Date.now().toString(36);
const randomString = Math.random().toString(36).slice(2, 7);
return timestamp + randomString;
}
function slugify(name) {
return name
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
/**
* Helper function to make API requests with error handling
*/
async function githubApiRequest({ method = "POST", urlSuffix, data, params, }) {
const url = `${GIT_API_URL}${urlSuffix}`;
try {
const response = await (0, axios_1.default)({
method,
url,
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
data,
params,
});
return response.data;
}
catch (error) {
console.warn(`API Request failed: ${method} ${url}`, axios_1.default.isAxiosError(error) ? error.response?.data : error.message);
throw error;
}
}
function waitForSpeculosReady(deviceId, { interval = 2_000, timeout = 150_000 } = {}) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
let currentRequest = null;
const url = getSpeculosAddress(deviceId);
function cleanup() {
if (currentRequest) {
currentRequest.destroy();
currentRequest = null;
}
}
function check() {
cleanup();
currentRequest = https_1.default.get(url, { timeout: 10000 }, res => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {
process.env.SPECULOS_ADDRESS = url;
cleanup();
console.warn(`Speculos is ready at ${url}`);
resolve(true);
}
else {
console.warn(`Speculos not ready yet, status: ${res.statusCode}`);
retry();
}
});
currentRequest.on("error", error => {
console.error(`Request error: ${error.message}`);
retry();
});
currentRequest.on("timeout", () => {
console.error("Request timeout");
retry();
});
}
function retry() {
if (Date.now() - startTime >= timeout) {
cleanup();
reject(new Error(`Timeout: ${url} did not become available within ${timeout}ms`));
}
else {
setTimeout(check, interval);
}
}
check();
});
}
exports.waitForSpeculosReady = waitForSpeculosReady;
function createStartPayload(deviceParams, runId) {
const { model, firmware, appName, appVersion, dependency, dependencies } = deviceParams;
let additional_args = "-p";
if (dependency) {
additional_args = `${additional_args} -l ${dependency}:/apps/${(0, speculos_transport_1.conventionalAppSubpath)(model, firmware, dependency, appVersion)}`;
}
else if (dependencies) {
additional_args = [
...new Set(dependencies.map(dep => `${additional_args} -l ${dep.name}:/apps/${(0, speculos_transport_1.conventionalAppSubpath)(model, firmware, dep.name, dep.appVersion ?? "1.0.0")}`)),
].join(" ");
}
return {
ref: GITHUB_REF,
inputs: {
speculos_version: SPECULOS_IMAGE_TAG?.split(":")[1] || "master",
coin_app: appName,
coin_app_version: appVersion,
device: speculos_transport_1.reverseModelMap[model],
device_os_version: firmware,
aws_role: AWS_ROLE,
cluster: CLUSTER,
seed: SEED,
run_id: runId,
additional_args,
},
};
}
async function createSpeculosDeviceCI(deviceParams) {
const runId = `${slugify(deviceParams.appName)}-${uniqueId()}`;
try {
const data = createStartPayload(deviceParams, runId);
await githubApiRequest({ urlSuffix: START_WORKFLOW_ID, data });
return {
id: runId,
port: speculosPort,
};
}
catch (e) {
console.warn(`Creating remote speculos ${deviceParams.appName}:${deviceParams.appVersion} failed with ${String(e)}`);
return {
id: runId,
port: 0,
};
}
}
exports.createSpeculosDeviceCI = createSpeculosDeviceCI;
async function releaseSpeculosDeviceCI(runId) {
const data = {
ref: GITHUB_REF,
inputs: {
run_id: runId.toString(),
aws_role: AWS_ROLE,
cluster: CLUSTER,
},
};
await githubApiRequest({ urlSuffix: STOP_WORKFLOW_ID, data });
}
exports.releaseSpeculosDeviceCI = releaseSpeculosDeviceCI;
//# sourceMappingURL=speculosCI.js.map