@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
161 lines • 5.82 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.waitForSpeculosReady = waitForSpeculosReady;
exports.createSpeculosDeviceCI = createSpeculosDeviceCI;
exports.releaseSpeculosDeviceCI = releaseSpeculosDeviceCI;
const axios_1 = __importDefault(require("axios"));
const speculos_transport_1 = require("@ledgerhq/speculos-transport");
const https_1 = __importDefault(require("https"));
const index_1 = require("./index");
const uuid_1 = require("uuid");
const { GITHUB_TOKEN, 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() {
return (0, uuid_1.v4)();
}
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}`, (0, index_1.sanitizeError)(error));
throw (0, index_1.sanitizeError)(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();
});
}
function createStartPayload(deviceParams, runId) {
const { model, firmware, appName, appVersion, dependencies } = deviceParams;
let additional_args = "-p";
if (dependencies?.length) {
additional_args = [
additional_args,
...new Set(dependencies.map(dep => `-l ${dep.name}:/apps/${(0, speculos_transport_1.conventionalAppSubpath)(model, firmware, dep.name, dep.appVersion ?? appVersion)}`)),
].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,
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,
appName: deviceParams.appName,
appVersion: deviceParams.appVersion,
dependencies: deviceParams.dependencies,
};
}
catch (error) {
console.warn(`Failed to create remote Speculos ${deviceParams.appName}:${deviceParams.appVersion}:`, (0, index_1.sanitizeError)(error));
return {
id: runId,
port: 0,
appName: deviceParams.appName,
appVersion: deviceParams.appVersion,
dependencies: deviceParams.dependencies,
};
}
}
async function releaseSpeculosDeviceCI(runId) {
const data = {
ref: GITHUB_REF,
inputs: {
run_id: runId.toString(),
},
};
try {
await githubApiRequest({ urlSuffix: STOP_WORKFLOW_ID, data });
}
catch (error) {
console.warn(`Failed to release remote Speculos ${runId}:`, (0, index_1.sanitizeError)(error));
}
}
//# sourceMappingURL=speculosCI.js.map