Skip to content

Commit

Permalink
add LineageOS plugin (#1834)
Browse files Browse the repository at this point in the history
* add LineageOS plugin

still need some love and test
this commit is supposed to be temporary...
My future self if you see this, you screwed again

* lineage_os plugin: rename downloaded file

and enable the plugin

* lineage_os plugin: add OpenRecoveryScript install action

add an install action compatible with any ORS compatible recovery.
I keep the download action in case of any devices with no ORS compatible recovery (just use adb sideload then)

* lineage_os plugin: fix indentation

and remove extra space

* Lint

* Test

Co-authored-by: J. Sprinz <[email protected]>
Co-authored-by: J. Sprinz <[email protected]>
  • Loading branch information
3 people authored Sep 26, 2021
1 parent 796db75 commit a9b64d3
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/core/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const { CancelablePromise } = require("cancelable-promise");

const AdbPlugin = require("./adb/plugin.js");
const AsteroidOsPlugin = require("./asteroid_os/plugin.js");
const LineageOSPlugin = require("./lineage_os/plugin.js");
const CorePlugin = require("./core/plugin.js");
const FastbootPlugin = require("./fastboot/plugin.js");
const HeimdallPlugin = require("./heimdall/plugin.js");
Expand All @@ -32,6 +33,7 @@ const SystemimagePlugin = require("./systemimage/plugin.js");
* @property {Object} plugins plugins namespace
* @property {AdbPlugin} plugins.adb adb plugin
* @property {AsteroidOsPlugin} plugins.asteroid_os AteroidOS plugin
* @property {LineageOSPlugin} plugins.lineage_os LineageOS plugin
* @property {CorePlugin} plugins.core core plugin
* @property {FastbootPlugin} plugins.fastboot fastboot plugin
* @property {HeimdallPlugin} plugins.heimdall heimdall plugin
Expand All @@ -46,6 +48,7 @@ class PluginIndex {
this.plugins = {
adb: new AdbPlugin(...pluginArgs),
asteroid_os: new AsteroidOsPlugin(...pluginArgs),
lineage_os: new LineageOSPlugin(...pluginArgs),
core: new CorePlugin(...pluginArgs),
fastboot: new FastbootPlugin(...pluginArgs),
heimdall: new HeimdallPlugin(...pluginArgs),
Expand Down
2 changes: 1 addition & 1 deletion src/core/plugins/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe("PluginIndex", () => {
});
describe("getPluginMappable()", () => {
it("should return plugin array", () =>
expect(pluginIndex.getPluginMappable()).toHaveLength(6));
expect(pluginIndex.getPluginMappable()).toHaveLength(7));
});
["init", "kill"].forEach(target =>
describe(`${target}()`, () => {
Expand Down
72 changes: 72 additions & 0 deletions src/core/plugins/lineage_os/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"use strict";

/*
* Copyright (C) 2020-2021 UBports Foundation <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

const axios = require("axios");

/** @module lineage_os */

const baseURL = "https://download.lineageos.org/api/v1/";
const deviceBuildTypeURL = `${baseURL}types/`;
const rootfsDefaultName = "lineageos_rootfs_";

const api = axios.create({ baseURL, timeout: 15000 });

/**
* get latest build from the api TODO make it better
* @param {String} channel channel
* @param {String} device device codename
* @returns {Promise<Array<Object>>} images array
* @throws {Error} message "no network" if request failed
*/
const getLatestBuild = (channel, device) =>
api
.get(`${device}/${channel}/abc`)
.then(({ data }) => {
return [
{
url: data.response[data.response.length - 1].url,
checksum: {
sum: data.response[data.response.length - 1].id,
algorithm: "sha256"
},
name: rootfsDefaultName + device + ".zip"
}
];
})
.catch(error => {
throw error;
});

/**
* get channels available for a device (usually always "nightly", but who knows)
* @param {String} device device codename
* @returns {Promise<Array<String>>} channels
* @throws {Error} message "unsupported" if 404 not found
*/
const getChannels = device =>
api
.get(`${deviceBuildTypeURL}${device}`)
.then(({ data }) => {
return data.response;
})
.catch(error => {
throw error;
});

module.exports = { getLatestBuild, getChannels };
53 changes: 53 additions & 0 deletions src/core/plugins/lineage_os/api.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
process.argv = [null, null, "-vv"];
const { ipcMain } = require("electron");
jest.mock("electron");
const axios = require("axios");
jest.mock("axios");
axios.create.mockReturnValue(axios);
const api = require("./api.js");

describe("lineage_os api", () => {
describe("getLatestBuild", () => {
it("should resolve images", () => {
axios.get.mockResolvedValueOnce({
data: {
response: [
{ url: "a", id: "42" },
{ url: "b", id: "1337" }
]
}
});
return api.getLatestBuild("nightlies", "bacon").then(r =>
expect(r).toEqual([
{
checksum: { algorithm: "sha256", sum: "1337" },
url: "b",
name: "lineageos_rootfs_bacon.zip"
}
])
);
});
it("should reject on network error", done => {
axios.get.mockRejectedValueOnce(new Error("no network"));
return api.getLatestBuild("1.0", "lenok").catch(e => {
expect(e.message).toEqual("no network");
done();
});
});
});
describe("getChannels", () => {
it("should resolve channels", () => {
axios.get.mockResolvedValue({ data: { response: ["nightlies"] } });
return api
.getChannels("lenok")
.then(r => expect(r).toEqual(["nightlies"]));
});
it("should reject on error", done => {
axios.get.mockRejectedValueOnce(new Error("no network"));
return api.getChannels("lenok").catch(r => {
expect(r.message).toEqual("no network");
done();
});
});
});
});
108 changes: 108 additions & 0 deletions src/core/plugins/lineage_os/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"use strict";

/*
* Copyright (C) 2020-2021 UBports Foundation <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

const Plugin = require("../plugin.js");
const api = require("./api.js");

/**
* LineageOS plugin
* @extends Plugin
*/
class LineageOSPlugin extends Plugin {
/**
* action download
* @returns {Promise<Array<Object>>}
*/
action__download() {
return api
.getLatestBuild(this.props.settings.channel, this.props.config.codename)
.then(rootfs_infos => [
{
actions: [
{
"core:download": {
group: "LineageOS",
files: rootfs_infos
}
}
]
}
]);
}

/**
* lineage_os:install action
* @returns {Promise<Array<Object>>}
*/
action__install() {
return api
.getLatestBuild(this.props.settings.channel, this.props.config.codename)
.then(rootfs_infos => [
{
actions: [
{
"core:download": {
group: "LineageOS",
files: rootfs_infos
}
},
{
"core:write": {
content: "install /data/" + rootfs_infos[0].name,
group: "LineageOS",
file: "openrecoveryscript"
}
},
{
"adb:wait": null
},
{
"adb:push": {
group: "LineageOS",
files: [rootfs_infos[0].name],
dest: "/data/"
}
},
{
"adb:push": {
group: "LineageOS",
files: ["openrecoveryscript"],
dest: "/cache/recovery/"
}
}
]
}
]);
}

/**
* channels remote_values
* @returns {Promise<Array<Object>>}
*/
remote_values__channels() {
return api.getChannels(this.props.config.codename).then(channels =>
channels.map(channel => ({
value: channel,
label: channel
}))
);
}
}

module.exports = LineageOSPlugin;
81 changes: 81 additions & 0 deletions src/core/plugins/lineage_os/plugin.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const api = require("./api.js");
jest.mock("./api.js");
api.getLatestBuild.mockResolvedValue([{ name: "default" }]);
api.getChannels.mockResolvedValue(["1.0", "nightlies"]);
const lineage_os = new (require("./plugin.js"))({
os: { name: "Lineage OS" },
config: { codename: "bacon" },
settings: { channel: "nightlies" }
});

describe("lineage_os plugin", () => {
describe("actions", () => {
describe("download", () => {
it("should create download steps", () =>
lineage_os.action__download().then(r =>
expect(r).toEqual([
{
actions: [
{
"core:download": {
files: [{ name: "default" }],
group: "LineageOS"
}
}
]
}
])
));
});
describe("install", () => {
it("should create install steps", () =>
lineage_os.action__install().then(r =>
expect(r).toEqual([
{
actions: [
{
"core:download": {
group: "LineageOS",
files: [{ name: "default" }]
}
},
{
"core:write": {
content: "install /data/default",
group: "LineageOS",
file: "openrecoveryscript"
}
},
{ "adb:wait": null },
{
"adb:push": {
group: "LineageOS",
files: ["default"],
dest: "/data/"
}
},
{
"adb:push": {
group: "LineageOS",
files: ["openrecoveryscript"],
dest: "/cache/recovery/"
}
}
]
}
])
));
});
});
describe("remote_values", () => {
describe("channels", () => {
it("should resolve channels", () =>
lineage_os.remote_values__channels().then(r =>
expect(r).toEqual([
{ label: "1.0", value: "1.0" },
{ label: "nightlies", value: "nightlies" }
])
));
});
});
});

0 comments on commit a9b64d3

Please sign in to comment.