Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[download] Migrate to Pulsar's Rolling Release Repo, off of CirrusCI #117

Merged
merged 4 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion microservices/download/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Download Endpoint

This Microservice allows easy linking to any Pulsar Alpha Cirrus CI Binary.
This Microservice allows easy linking to any Pulsar Rolling Release Binary.

This binaries are no longer downloaded directly from CirrusCI. Instead these binaries are now uploaded to [`pulsar-edit/pulsar-rolling-releases`](https://github.com/pulsar-edit/pulsar-rolling-releases).

Feel free to refer to the [download table](/docs/download_links.md) to use this endpoint.

Expand Down
324 changes: 169 additions & 155 deletions microservices/download/utils.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
const https = require("node:https");

function doRequest(queryString) {
const dataToSend = JSON.stringify({ query: queryString });
function doRequest() {

const options = {
hostname: 'api.cirrus-ci.com',
path: '/graphql',
method: 'POST',
hostname: 'api.github.com',
path: '/repos/pulsar-edit/pulsar-rolling-releases/releases',
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Content-Length': dataToSend.length
'Accept': 'application/vnd.github+json',
'User-Agent': 'pulsar-edit/package-frontend/microservices/download'
}
};

Expand All @@ -32,8 +30,6 @@ function doRequest(queryString) {
reject(e);
});

// Write data to request body
req.write(dataToSend);
req.end();
});
};
Expand Down Expand Up @@ -111,175 +107,193 @@ async function displayError(req, res, errMsg) {

async function findLink(os, type) {
try {
let repositoryQuery = `
query getRepositoryBuildStatuses {
repository(id: 6483909499158528) {
builds(branch: "master", last: 10) {
edges {
node {
id
status
}
}
}

let releases = await doRequest();

// Now these releases should be sorted already, if we find they aren't we might
// have to add semver as a dep on this microservice, which is no fun since this
// microservice has 0 deps currently. For now lets assume it's a sorted array
// This same assumption is made on the `pulsar-updater` core package
confused-Techie marked this conversation as resolved.
Show resolved Hide resolved

for (const version of releases) {
for (const asset of version.assets) {

let name = asset.name;
Copy link
Member

@DeeDeeG DeeDeeG Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let name = asset.name;
let name = asset?.name;

If you want to put optional chaining back, I might have been misinformed about just how "impossible" it is for asset to be undefined here (I have found out it is indeed possible, if you loop through an array with literal undefined in it).

We're pulling data from JSON where undefined isn't allowed, so it should be safe, but in general I think you had the motivation/implementation probably right before, sorry about that.


let returnObj = {
ok: true,
content: asset?.browser_download_url
};

// Ensure we have valid data to work with
if (typeof name !== "string" || typeof returnObj.content !== "string") {
continue;
}
}
`;

let repositoryGraph = await doRequest(repositoryQuery);
if (os === "windows") {
if (
type === "windows_setup" &&
name.startsWith("Pulsar.Setup") &&
name.endsWith(".exe")
) {

let buildID;
return returnObj;

for (const edge of repositoryGraph.data.repository.builds.edges) {
if (edge.node.status === "COMPLETED") {
buildID = edge.node.id;
break;
}
}
} else if (
type === "windows_portable" &&
name.endsWith("-win.zip")
) {

return returnObj;

} else if (
type === "windows_blockmap" &&
name.startsWith("Pulsar.Setup") &&
name.endsWith(".exe.blockmap")
) {

return returnObj;

let buildQuery = `
query GetTasksFromBuild {
build(id: "${buildID}") {
tasks {
name
id
status
}
}
}
`;
} else if (os === "silicon_mac") {
if (
type === "mac_zip" &&
name.endsWith("-arm64-mac.zip")
) {

let buildGraph = await doRequest(buildQuery);
return returnObj;

let taskid = undefined;
} else if (
type === "mac_zip_blockmap" &&
name.endsWith("-arm64-mac.zip.blockmap")
) {

const findID = function (name, tasks) {
for (const task of tasks) {
if (task.name === name && task.status === "COMPLETED") {
return task.id;
}
}
return undefined;
};
return returnObj;

// While it seems to make total sense to just run `findID(params.os, buildGraph.data.build.tasks)`
// The reason this was done, so that if these names change at all or more are added, the bulk
// of responsibility to update will lie soley here. Rather than let possible external links fail and expire.
// Such as `linux` changing to `Linux`, would only have to be done here rather than every location the link appears.
switch(os) {
case "linux":
taskid = findID("linux", buildGraph.data.build.tasks);
break;
case "arm_linux":
taskid = findID("arm_linux", buildGraph.data.build.tasks);
break;
case "silicon_mac":
taskid = findID("silicon_mac", buildGraph.data.build.tasks);
break;
case "intel_mac":
taskid = findID("intel_mac", buildGraph.data.build.tasks);
break;
case "windows":
taskid = findID("windows", buildGraph.data.build.tasks);
break;
default:
taskid = undefined;
break;
}
} else if (
type === "mac_dmg" &&
name.endsWith("-arm64.dmg")
) {

if (taskid === undefined) {
return {
ok: false,
code: 503,
msg: "Invalid OS Download Parameters Provided."
};
}
return returnObj;

} else if (
type === "mac_dmg_blockmap" &&
name.endsWith("-arm64.dmg.blockmap")
) {

return returnObj;

let taskQuery = `
query GetTaskDetails {
task(id: ${taskid}) {
artifacts {
files {
path
}
}
}
}
`;
} else if (os === "intel_mac") {
if (
type === "mac_zip" &&
name.endsWith("-mac.zip") &&
!name.endsWith("-arm64-mac.zip")
) {

return returnObj;

} else if (
type === "mac_zip_blockmap" &&
name.endsWith("-mac.zip.blockmap") &&
!name.endsWith("-arm64-mac.zip.blockmap")
) {

let taskGraph = await doRequest(taskQuery);
return returnObj;

let binaryPath = undefined;
} else if (
type === "mac_dmg" &&
name.endsWith(".dmg") &&
!name.endsWith("-arm64.dmg")
) {

return returnObj;

} else if (
type === "mac_dmg_blockmap" &&
name.endsWith(".dmg.blockmap") &&
!name.endsWith("-arm64.dmg.blockmap")
) {

return returnObj;

const findBinary = function (ext, loc, binaries) {
for (const binary of binaries) {
if (loc === "start") {
if (binary.path.startsWith(ext)) {
return binary.path;
}
} else if (loc === "end") {
if (binary.path.endsWith(ext)) {
return binary.path;
} else if (os === "arm_linux") {
if (
type === "linux_appimage" &&
name.endsWith("-arm64.AppImage")
) {

return returnObj;

} else if (
type === "linux_tar" &&
name.endsWith("-arm64.tar.gz")
) {

return returnObj;

} else if (
type === "linux_rpm" &&
name.endsWith(".aarch64.rpm")
) {

return returnObj;

} else if (
type === "linux_deb" &&
name.endsWith("_arm64.deb")
) {

return returnObj;

}
} else if (os === "linux") {
if (
type === "linux_appimage" &&
name.endsWith(".AppImage") &&
!name.endsWith("-arm64.AppImage")
) {

return returnObj;

} else if (
type === "linux_tar" &&
name.endsWith(".tar.gz") &&
!name.endsWith("-arm64.tar.gz")
) {

return returnObj;

} else if (
type === "linux_rpm" &&
name.endsWith(".x86_64.rpm")
) {

return returnObj;

} else if (
type === "linux_deb" &&
name.endsWith("_amd64.deb")
) {

return returnObj;

}
}
}
return undefined;
};

switch(type) {
// Linux Binaries
case "linux_appimage":
binaryPath = findBinary(".AppImage", "end", taskGraph.data.task.artifacts[0].files);
break;
case "linux_tar":
binaryPath = findBinary(".tar.gz", "end", taskGraph.data.task.artifacts[0].files);
break;
case "linux_rpm":
binaryPath = findBinary(".rpm", "end", taskGraph.data.task.artifacts[0].files);
break;
case "linux_deb":
binaryPath = findBinary(".deb", "end", taskGraph.data.task.artifacts[0].files);
break;
// Windows Binaries
case "windows_setup":
binaryPath = findBinary("binaries/Pulsar Setup", "start", taskGraph.data.task.artifacts[0].files);
break;
case "windows_portable":
binaryPath = findBinary(".zip", "end", taskGraph.data.task.artifacts[0].files);
break;
case "windows_blockmap":
binaryPath = findBinary(".exe.blockmap", "end", taskGraph.data.task.artifacts[0].files);
break;
// MacOS Builds
case "mac_zip":
binaryPath = findBinary(".zip", "end", taskGraph.data.task.artifacts[0].files);
break;
case "mac_zip_blockmap":
binaryPath = findBinary(".zip.blockmap", "end", taskGraph.data.task.artifacts[0].files);
break;
case "mac_dmg":
binaryPath = findBinary(".dmg", "end", taskGraph.data.task.artifacts[0].files);
break;
case "mac_dmg_blockmap":
binaryPath = findBinary(".dmb.blockmap", "end", taskGraph.data.task.artifacts[0].files);
break;
default:
binaryPath = undefined;
break;
}
}

if (binaryPath === undefined) {
return {
ok: false,
code: 503,
msg: "Invalid TYPE Download Parameters Provided"
};
}
// If we get to this point it means the above loop didn't return.
// Meaning we couldn't find a single valid asset among any versions
// So we will return an error

// Now that we have the binary, it's time to return a redirect.
return {
ok: true,
content: `https://api.cirrus-ci.com/v1/artifact/task/${taskid}/binary/${binaryPath}`
ok: false,
code: 404,
msg: `Unable to find any assets matching the provided parameters: os=${os};type=${type}`
};
confused-Techie marked this conversation as resolved.
Show resolved Hide resolved

} catch(err) {
Expand Down