DEV: Modernize upgrade-show and repo model
fixup upgrade-show controller
This commit is contained in:
parent
4e784351ad
commit
9163b3d2e2
|
|
@ -17,7 +17,7 @@ export default Controller.extend({
|
|||
),
|
||||
|
||||
allUpToDate: computed("model.@each.upToDate", function () {
|
||||
return this.get("model").every((repo) => repo.get("upToDate"));
|
||||
return this.get("model").every((repo) => repo.upToDate);
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
|
|
|||
|
|
@ -1,66 +1,72 @@
|
|||
import Repo from "discourse/plugins/docker_manager/discourse/models/repo";
|
||||
import Controller from "@ember/controller";
|
||||
import { equal } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { equal } from "@ember/object/computed";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Controller.extend({
|
||||
dialog: service(),
|
||||
export default class UpgradeShow extends Controller {
|
||||
@service messageBus;
|
||||
@service dialog;
|
||||
|
||||
output: null,
|
||||
@tracked output = "";
|
||||
@tracked status = null;
|
||||
@tracked percent = 0;
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
this.reset();
|
||||
},
|
||||
@equal("status", "complete") complete;
|
||||
@equal("status", "failed") failed;
|
||||
|
||||
complete: equal("status", "complete"),
|
||||
failed: equal("status", "failed"),
|
||||
get multiUpgrade() {
|
||||
return this.model.length !== 1;
|
||||
}
|
||||
|
||||
multiUpgrade: computed("model.length", function () {
|
||||
return this.get("model.length") !== 1;
|
||||
}),
|
||||
get title() {
|
||||
return this.multiUpgrade ? "All" : this.model[0].name;
|
||||
}
|
||||
|
||||
title: computed("model.@each.name", function () {
|
||||
return this.get("multiUpgrade") ? "All" : this.get("model")[0].get("name");
|
||||
}),
|
||||
get isUpToDate() {
|
||||
return this.model.every((repo) => repo.upToDate);
|
||||
}
|
||||
|
||||
isUpToDate: computed("model.@each.upToDate", function () {
|
||||
return this.get("model").every((repo) => repo.get("upToDate"));
|
||||
}),
|
||||
get upgrading() {
|
||||
return this.model.some((repo) => repo.upgrading);
|
||||
}
|
||||
|
||||
upgrading: computed("model.@each.upgrading", function () {
|
||||
return this.get("model").some((repo) => repo.get("upgrading"));
|
||||
}),
|
||||
get repos() {
|
||||
return this.isMultiple ? this.model : [this.model];
|
||||
}
|
||||
|
||||
repos() {
|
||||
const model = this.get("model");
|
||||
return this.get("isMultiple") ? model : [model];
|
||||
},
|
||||
get upgradeButtonText() {
|
||||
if (this.upgrading) {
|
||||
return "Upgrading...";
|
||||
} else {
|
||||
return "Start Upgrading";
|
||||
}
|
||||
}
|
||||
|
||||
updateAttribute(key, value, valueIsKey = false) {
|
||||
this.get("model").forEach((repo) => {
|
||||
value = valueIsKey ? repo.get(value) : value;
|
||||
repo.set(key, value);
|
||||
this.model.forEach((repo) => {
|
||||
value = valueIsKey ? repo[value] : value;
|
||||
repo[key] = value;
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
messageReceived(msg) {
|
||||
switch (msg.type) {
|
||||
case "log":
|
||||
this.set("output", this.get("output") + msg.value + "\n");
|
||||
this.output = this.output + msg.value + "\n";
|
||||
break;
|
||||
case "percent":
|
||||
this.set("percent", msg.value);
|
||||
this.percent = msg.value;
|
||||
break;
|
||||
case "status":
|
||||
this.set("status", msg.value);
|
||||
this.status = msg.value;
|
||||
|
||||
if (msg.value === "complete") {
|
||||
this.get("model")
|
||||
.filter((repo) => repo.get("upgrading"))
|
||||
this.model
|
||||
.filter((repo) => repo.upgrading)
|
||||
.forEach((repo) => {
|
||||
repo.set("version", repo.get("latest.version"));
|
||||
repo.version = repo.latest?.version;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,73 +76,68 @@ export default Controller.extend({
|
|||
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
upgradeButtonText: computed("upgrading", function () {
|
||||
if (this.get("upgrading")) {
|
||||
return "Upgrading...";
|
||||
} else {
|
||||
return "Start Upgrading";
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
startBus() {
|
||||
this.messageBus.subscribe("/docker/upgrade", (msg) => {
|
||||
this.messageReceived(msg);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
stopBus() {
|
||||
this.messageBus.unsubscribe("/docker/upgrade");
|
||||
},
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.setProperties({ output: "", status: null, percent: 0 });
|
||||
},
|
||||
this.output = "";
|
||||
this.status = null;
|
||||
this.percent = 0;
|
||||
}
|
||||
|
||||
actions: {
|
||||
start() {
|
||||
this.reset();
|
||||
@action
|
||||
start() {
|
||||
this.reset();
|
||||
|
||||
if (this.get("multiUpgrade")) {
|
||||
this.get("model")
|
||||
.filter((repo) => !repo.get("upToDate"))
|
||||
.forEach((repo) => repo.set("upgrading", true));
|
||||
return Repo.upgradeAll();
|
||||
}
|
||||
if (this.multiUpgrade) {
|
||||
this.model
|
||||
.filter((repo) => !repo.upToDate)
|
||||
.forEach((repo) => (repo.upgrading = true));
|
||||
|
||||
const repo = this.get("model")[0];
|
||||
if (repo.get("upgrading")) {
|
||||
return;
|
||||
}
|
||||
repo.startUpgrade();
|
||||
},
|
||||
return Repo.upgradeAll();
|
||||
}
|
||||
|
||||
resetUpgrade() {
|
||||
const message = `
|
||||
WARNING: You should only reset upgrades that have failed and are not running.
|
||||
const repo = this.model[0];
|
||||
if (repo.upgrading) {
|
||||
return;
|
||||
}
|
||||
|
||||
This will NOT cancel currently running builds and should only be used as a last resort.
|
||||
`;
|
||||
repo.startUpgrade();
|
||||
}
|
||||
|
||||
this.dialog.confirm({
|
||||
message,
|
||||
didConfirm: () => {
|
||||
if (this.get("multiUpgrade")) {
|
||||
return Repo.resetAll(
|
||||
this.get("model").filter((repo) => !repo.get("upToDate"))
|
||||
).finally(() => {
|
||||
this.reset();
|
||||
this.updateAttribute("upgrading", false);
|
||||
});
|
||||
}
|
||||
@action
|
||||
resetUpgrade() {
|
||||
const message = `
|
||||
WARNING: You should only reset upgrades that have failed and are not running.
|
||||
This will NOT cancel currently running builds and should only be used as a last resort.
|
||||
`;
|
||||
|
||||
const repo = this.get("model")[0];
|
||||
repo.resetUpgrade().then(() => {
|
||||
this.dialog.confirm({
|
||||
message,
|
||||
didConfirm: async () => {
|
||||
if (this.multiUpgrade) {
|
||||
try {
|
||||
await Repo.resetAll(this.model.filter((repo) => !repo.upToDate));
|
||||
} finally {
|
||||
this.reset();
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
this.updateAttribute("upgrading", false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const repo = this.model[0];
|
||||
await repo.resetUpgrade();
|
||||
this.reset();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { helper as buildHelper } from "@ember/component/helper";
|
||||
import { isNone } from "@ember/utils";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default buildHelper(function (params) {
|
||||
const [commitsBehind, oldSha, newSha, url] = params;
|
||||
|
||||
if (!commitsBehind) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (parseInt(commitsBehind, 10) === 0) {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -13,7 +16,7 @@ export default buildHelper(function (params) {
|
|||
commitsBehind === 1 ? "" : "s"
|
||||
}`;
|
||||
|
||||
if (isNone(url)) {
|
||||
if (!url) {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,162 +1,176 @@
|
|||
import { default as EmberObject, computed } from "@ember/object";
|
||||
import { or } from "@ember/object/computed";
|
||||
import { isNone } from "@ember/utils";
|
||||
import { Promise } from "rsvp";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
|
||||
let loaded = [];
|
||||
|
||||
function concatVersions(repos) {
|
||||
return repos.map((repo) => repo.get("version")).join(", ");
|
||||
return repos.map((repo) => repo.version).join(", ");
|
||||
}
|
||||
|
||||
const Repo = EmberObject.extend({
|
||||
unloaded: true,
|
||||
checking: false,
|
||||
export default class Repo {
|
||||
@tracked unloaded = true;
|
||||
@tracked checking = false;
|
||||
@tracked lastCheckedAt = null;
|
||||
@tracked latest = new TrackedObject({});
|
||||
|
||||
checkingStatus: or("unloaded", "checking"),
|
||||
upToDate: computed("upgrading", "version", "latest.version", function () {
|
||||
return (
|
||||
!this.get("upgrading") &&
|
||||
(this.get("version") === this.get("latest.version"))
|
||||
);
|
||||
}),
|
||||
// model attributes
|
||||
@tracked name = null;
|
||||
@tracked path = null;
|
||||
@tracked branch = null;
|
||||
@tracked official = false;
|
||||
@tracked fork = false;
|
||||
@tracked id = null;
|
||||
@tracked version = null;
|
||||
@tracked pretty_version = null;
|
||||
@tracked url = null;
|
||||
@tracked upgrading = false;
|
||||
|
||||
prettyVersion: computed("version", "pretty_version", function () {
|
||||
return this.get("pretty_version") || this.get("version")?.substring(0, 8);
|
||||
}),
|
||||
constructor(attributes) {
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
this[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
prettyLatestVersion: computed("latest.{version,pretty_version}", function () {
|
||||
return (
|
||||
this.get("latest.pretty_version") ||
|
||||
this.get("latest.version")?.substring(0, 8)
|
||||
);
|
||||
}),
|
||||
get checkingStatus() {
|
||||
return this.unloaded || this.checking;
|
||||
}
|
||||
|
||||
get upToDate() {
|
||||
return !this.upgrading && this.version === this.latest?.version;
|
||||
}
|
||||
|
||||
get prettyVersion() {
|
||||
return this.pretty_version || this.version?.substring(0, 8);
|
||||
}
|
||||
|
||||
get prettyLatestVersion() {
|
||||
return this.latest?.pretty_version || this.latest?.version?.substring(0, 8);
|
||||
}
|
||||
|
||||
get shouldCheck() {
|
||||
if (isNone(this.get("version"))) {
|
||||
if (this.version === null) {
|
||||
return false;
|
||||
}
|
||||
if (this.get("checking")) {
|
||||
|
||||
if (this.checking) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only check once every minute
|
||||
const lastCheckedAt = this.get("lastCheckedAt");
|
||||
if (lastCheckedAt) {
|
||||
const ago = new Date().getTime() - lastCheckedAt;
|
||||
if (this.lastCheckedAt) {
|
||||
const ago = new Date().getTime() - this.lastCheckedAt;
|
||||
return ago > 60 * 1000;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
repoAjax(url, args) {
|
||||
args = args || {};
|
||||
args.data = this.getProperties("path", "version", "branch");
|
||||
return true;
|
||||
}
|
||||
|
||||
repoAjax(url, args = {}) {
|
||||
args.data = {
|
||||
path: this.path,
|
||||
version: this.version,
|
||||
branch: this.branch,
|
||||
};
|
||||
|
||||
return ajax(url, args);
|
||||
},
|
||||
}
|
||||
|
||||
findLatest() {
|
||||
return new Promise((resolve) => {
|
||||
if (!this.get("shouldCheck")) {
|
||||
this.set("unloaded", false);
|
||||
return resolve();
|
||||
}
|
||||
async findLatest() {
|
||||
if (!this.shouldCheck) {
|
||||
this.unloaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("checking", true);
|
||||
this.repoAjax("/admin/docker/latest").then((result) => {
|
||||
this.setProperties({
|
||||
unloaded: false,
|
||||
checking: false,
|
||||
lastCheckedAt: new Date().getTime(),
|
||||
latest: EmberObject.create(result.latest),
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
this.checking = true;
|
||||
|
||||
findProgress() {
|
||||
return this.repoAjax("/admin/docker/progress").then(
|
||||
(result) => result.progress
|
||||
);
|
||||
},
|
||||
const result = await this.repoAjax("/admin/docker/latest");
|
||||
|
||||
resetUpgrade() {
|
||||
return this.repoAjax("/admin/docker/upgrade", {
|
||||
this.unloaded = false;
|
||||
this.checking = false;
|
||||
this.lastCheckedAt = new Date().getTime();
|
||||
|
||||
for (const [key, value] of Object.entries(result.latest)) {
|
||||
this.latest[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
async findProgress() {
|
||||
const result = await this.repoAjax("/admin/docker/progress");
|
||||
return result.progress;
|
||||
}
|
||||
|
||||
async resetUpgrade() {
|
||||
await this.repoAjax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "DELETE",
|
||||
}).then(() => {
|
||||
this.set("upgrading", false);
|
||||
});
|
||||
},
|
||||
|
||||
startUpgrade() {
|
||||
this.set("upgrading", true);
|
||||
this.upgrading = false;
|
||||
}
|
||||
|
||||
return this.repoAjax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "POST",
|
||||
}).catch(() => {
|
||||
this.set("upgrading", false);
|
||||
});
|
||||
},
|
||||
});
|
||||
async startUpgrade() {
|
||||
this.upgrading = true;
|
||||
|
||||
Repo.reopenClass({
|
||||
findAll() {
|
||||
return new Promise((resolve) => {
|
||||
if (loaded.length) {
|
||||
return resolve(loaded);
|
||||
}
|
||||
|
||||
ajax("/admin/docker/repos").then((result) => {
|
||||
loaded = result.repos.map((r) => Repo.create(r));
|
||||
resolve(loaded);
|
||||
try {
|
||||
await this.repoAjax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "POST",
|
||||
});
|
||||
});
|
||||
},
|
||||
} catch (e) {
|
||||
this.upgrading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findUpgrading() {
|
||||
return this.findAll().then((result) => result.findBy("upgrading", true));
|
||||
},
|
||||
Repo.findAll = async function () {
|
||||
if (loaded.length) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
find(id) {
|
||||
return this.findAll().then((result) => result.findBy("id", id));
|
||||
},
|
||||
const result = await ajax("/admin/docker/repos");
|
||||
loaded = result.repos.map((r) => new Repo(r));
|
||||
return loaded;
|
||||
};
|
||||
|
||||
upgradeAll() {
|
||||
return ajax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "POST",
|
||||
data: { path: "all" },
|
||||
});
|
||||
},
|
||||
Repo.findUpgrading = async function () {
|
||||
const result = await Repo.findAll();
|
||||
return result.findBy("upgrading", true);
|
||||
};
|
||||
|
||||
resetAll(repos) {
|
||||
return ajax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "DELETE",
|
||||
data: { path: "all", version: concatVersions(repos) },
|
||||
});
|
||||
},
|
||||
Repo.find = async function (id) {
|
||||
const result = await Repo.findAll();
|
||||
return result.findBy("id", id);
|
||||
};
|
||||
|
||||
findLatestAll() {
|
||||
return ajax("/admin/docker/latest", {
|
||||
dataType: "text",
|
||||
type: "GET",
|
||||
data: { path: "all" },
|
||||
});
|
||||
},
|
||||
Repo.upgradeAll = function () {
|
||||
return ajax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "POST",
|
||||
data: { path: "all" },
|
||||
});
|
||||
};
|
||||
|
||||
findAllProgress(repos) {
|
||||
return ajax("/admin/docker/progress", {
|
||||
dataType: "text",
|
||||
type: "GET",
|
||||
data: { path: "all", version: concatVersions(repos) },
|
||||
});
|
||||
},
|
||||
});
|
||||
Repo.resetAll = function (repos) {
|
||||
return ajax("/admin/docker/upgrade", {
|
||||
dataType: "text",
|
||||
type: "DELETE",
|
||||
data: { path: "all", version: concatVersions(repos) },
|
||||
});
|
||||
};
|
||||
|
||||
export default Repo;
|
||||
Repo.findLatestAll = function () {
|
||||
return ajax("/admin/docker/latest", {
|
||||
dataType: "text",
|
||||
type: "GET",
|
||||
data: { path: "all" },
|
||||
});
|
||||
};
|
||||
|
||||
Repo.findAllProgress = function (repos) {
|
||||
return ajax("/admin/docker/progress", {
|
||||
dataType: "text",
|
||||
type: "GET",
|
||||
data: { path: "all", version: concatVersions(repos) },
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,20 +22,17 @@ export default Route.extend({
|
|||
controller.setProperties({ model, upgrading: null });
|
||||
|
||||
model.forEach((repo) => {
|
||||
if (repo.get("upgrading")) {
|
||||
if (repo.upgrading) {
|
||||
controller.set("upgrading", repo);
|
||||
}
|
||||
|
||||
// Special case: Upgrade docker manager first
|
||||
if (repo.get("id") === "docker_manager") {
|
||||
if (repo.id === "docker_manager") {
|
||||
controller.set("managerRepo", repo);
|
||||
}
|
||||
|
||||
// Special case: If the branch is "main" warn user
|
||||
if (
|
||||
repo.get("id") === "discourse" &&
|
||||
repo.get("branch") === "origin/main"
|
||||
) {
|
||||
if (repo.id === "discourse" && repo.branch === "origin/main") {
|
||||
upgradeController.appendBannerHtml(`
|
||||
<b>WARNING:</b>
|
||||
Your Discourse is tracking the 'main' branch which may be unstable,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import Repo from "discourse/plugins/docker_manager/discourse/models/repo";
|
||||
import Route from "@ember/routing/route";
|
||||
import EmberObject from "@ember/object";
|
||||
import { Promise } from "rsvp";
|
||||
|
||||
export default Route.extend({
|
||||
|
|
@ -15,16 +14,19 @@ export default Route.extend({
|
|||
if (Array.isArray(model)) {
|
||||
return Repo.findLatestAll().then((response) => {
|
||||
JSON.parse(response).repos.forEach((_repo) => {
|
||||
const repo = model.find((repo) => repo.get("path") === _repo.path);
|
||||
const repo = model.find((repo) => repo.path === _repo.path);
|
||||
if (!repo) {
|
||||
return;
|
||||
}
|
||||
delete _repo.path;
|
||||
repo.set("latest", EmberObject.create(_repo));
|
||||
|
||||
for (const [key, value] of Object.entries(_repo)) {
|
||||
repo.latest[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return Repo.findAllProgress(
|
||||
model.filter((repo) => !repo.get("upToDate"))
|
||||
model.filter((repo) => !repo.upToDate)
|
||||
).then((progress) => {
|
||||
this.set("progress", JSON.parse(progress).progress);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,33 +1,29 @@
|
|||
<h3>Upgrade {{title}}</h3>
|
||||
<h3>Upgrade {{this.title}}</h3>
|
||||
|
||||
<ProgressBar @percent={{this.percent}} />
|
||||
|
||||
{{#if complete}}
|
||||
{{#if this.complete}}
|
||||
<p>Upgrade completed successfully!</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if failed}}
|
||||
{{else if this.failed}}
|
||||
<p>Sorry, there was an error upgrading Discourse. Please check the logs below.</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if isUpToDate}}
|
||||
{{#unless multiUpgrade}}
|
||||
<p>{{title}} is at the newest version.</p>
|
||||
{{#if this.isUpToDate}}
|
||||
{{#unless this.multiUpgrade}}
|
||||
<p>{{this.title}} is at the newest version.</p>
|
||||
{{else}}
|
||||
<p>Everything is up-to-date.</p>
|
||||
{{/unless}}
|
||||
{{else}}
|
||||
<div style="clear: both">
|
||||
<button {{action "start"}} disabled={{upgrading}} class="btn">
|
||||
{{upgradeButtonText}}
|
||||
</button>
|
||||
<button {{on "click" this.start}} disabled={{this.upgrading}} class="btn">
|
||||
{{upgradeButtonText}}
|
||||
</button>
|
||||
|
||||
{{#if upgrading}}
|
||||
<button {{action "resetUpgrade"}} class="btn unlock">
|
||||
Reset Upgrade
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if this.upgrading}}
|
||||
<button {{on "click" this.resetUpgrade}} class="btn unlock">
|
||||
Reset Upgrade
|
||||
</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<Console @output={{this.output}} @followOutput={{true}} />
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import { find, render } from "@ember/test-helpers";
|
|||
import hbs from "htmlbars-inline-precompile";
|
||||
import Repo from "discourse/plugins/docker_manager/discourse/models/repo";
|
||||
|
||||
const repoObject = Repo.create({
|
||||
unloaded: false,
|
||||
const repoObject = new Repo({
|
||||
branch: "origin/main",
|
||||
id: "discourse",
|
||||
name: "discourse",
|
||||
|
|
@ -23,8 +22,7 @@ const repoObject = Repo.create({
|
|||
},
|
||||
});
|
||||
|
||||
const managerRepo = Repo.create({
|
||||
unloaded: false,
|
||||
const managerRepo = new Repo({
|
||||
branch: "origin/main",
|
||||
id: "docker_manager",
|
||||
name: "docker_manager",
|
||||
|
|
|
|||
Loading…
Reference in New Issue