feature: commit dialog for upgradeable components

Signed-off-by: Stefan Dej <meteyou@gmail.com>
This commit is contained in:
Stefan Dej 2021-02-27 01:01:47 +01:00
parent a0d21f6664
commit f2ea6c039f
3 changed files with 265 additions and 85 deletions

87
package-lock.json generated
View File

@ -15,6 +15,7 @@
"object-assign-deep": "^0.4.0",
"plotly.js": "^1.58.4",
"prismjs": "^1.23.0",
"semver": "^5.7.1",
"vue": "^2.6.12",
"vue-context": "^6.0.0",
"vue-github-api": "^0.1.7",
@ -2282,7 +2283,6 @@
"thread-loader": "^2.1.3",
"url-loader": "^2.2.0",
"vue-loader": "^15.9.2",
"vue-loader-v16": "npm:vue-loader@^16.1.0",
"vue-style-loader": "^4.1.2",
"webpack": "^4.0.0",
"webpack-bundle-analyzer": "^3.8.0",
@ -2581,7 +2581,6 @@
"merge-source-map": "^1.1.0",
"postcss": "^7.0.14",
"postcss-selector-parser": "^6.0.2",
"prettier": "^1.18.2",
"source-map": "~0.6.1",
"vue-template-es2015-compiler": "^1.9.0"
},
@ -4312,7 +4311,6 @@
"dependencies": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.1.2",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
@ -6700,8 +6698,7 @@
"esprima": "^4.0.1",
"estraverse": "^4.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1",
"source-map": "~0.6.1"
"optionator": "^0.8.1"
},
"optionalDependencies": {
"source-map": "~0.6.1"
@ -7812,7 +7809,72 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz",
"integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==",
"bundleDependencies": [
"node-pre-gyp"
"node-pre-gyp",
"abbrev",
"ansi-regex",
"aproba",
"are-we-there-yet",
"balanced-match",
"brace-expansion",
"chownr",
"code-point-at",
"concat-map",
"console-control-strings",
"core-util-is",
"debug",
"deep-extend",
"delegates",
"detect-libc",
"fs-minipass",
"fs.realpath",
"gauge",
"glob",
"has-unicode",
"iconv-lite",
"ignore-walk",
"inflight",
"inherits",
"ini",
"is-fullwidth-code-point",
"isarray",
"minimatch",
"minimist",
"minipass",
"minizlib",
"mkdirp",
"ms",
"needle",
"nopt",
"npm-bundled",
"npm-normalize-package-bin",
"npm-packlist",
"npmlog",
"number-is-nan",
"object-assign",
"once",
"os-homedir",
"os-tmpdir",
"osenv",
"path-is-absolute",
"process-nextick-args",
"rc",
"readable-stream",
"rimraf",
"safe-buffer",
"safer-buffer",
"sax",
"semver",
"set-blocking",
"signal-exit",
"string_decoder",
"string-width",
"strip-ansi",
"strip-json-comments",
"tar",
"util-deprecate",
"wide-align",
"wrappy",
"yallist"
],
"dev": true,
"optional": true,
@ -13517,9 +13579,6 @@
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==",
"dependencies": {
"clipboard": "^2.0.0"
},
"optionalDependencies": {
"clipboard": "^2.0.0"
}
@ -14721,8 +14780,7 @@
"node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"node_modules/semver-diff": {
"version": "2.1.0",
@ -17595,8 +17653,7 @@
"dependencies": {
"chokidar": "^3.4.1",
"graceful-fs": "^4.1.2",
"neo-async": "^2.5.0",
"watchpack-chokidar2": "^2.0.0"
"neo-async": "^2.5.0"
},
"optionalDependencies": {
"watchpack-chokidar2": "^2.0.0"
@ -17812,7 +17869,6 @@
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
@ -32563,8 +32619,7 @@
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"semver-diff": {
"version": "2.1.0",

View File

@ -16,6 +16,7 @@
"object-assign-deep": "^0.4.0",
"plotly.js": "^1.58.4",
"prismjs": "^1.23.0",
"semver": "^5.7.1",
"vue": "^2.6.12",
"vue-context": "^6.0.0",
"vue-github-api": "^0.1.7",

View File

@ -1,73 +1,120 @@
<template>
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>Update Manager</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="primary" :loading="loadings.includes('loadingBtnSyncUpdateManager')" :disabled="['printing', 'paused'].includes(printer_state)" @click="btnSync" v-bind="attrs" v-on="on"><v-icon small>mdi-refresh</v-icon></v-btn>
</template>
<span>Check for updates</span>
</v-tooltip>
</v-toolbar>
<v-card-text class="px-0 py-0">
<v-container py-0 px-0>
<style scoped>
.cursor--pointer {
cursor: pointer;
}
</style>
<div v-for="(value, key) of updateableSoftwares" v-bind:key="key">
<v-divider class="my-0" ></v-divider>
<v-row class="py-2">
<v-col class="pl-6">
{{ 'name' in value ? value.name : key }}<br />
{{ getVersionOutput(value) }}
</v-col>
<v-col class="col-auto pr-6 text-right" align-self="center">
<v-chip
small
label
outlined
:color="getBtnColor(value)"
@click="updateModule(key)"
:disabled="getBtnDisabled(value)"
class="minwidth-0 px-2 text-uppercase"
><v-icon small class="mr-1">mdi-{{ getBtnIcon(value) }}</v-icon>{{ getBtnText(value) }}</v-chip>
<template>
<div>
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>Update Manager</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="primary" :loading="loadings.includes('loadingBtnSyncUpdateManager')" :disabled="['printing', 'paused'].includes(printer_state)" @click="btnSync" v-bind="attrs" v-on="on"><v-icon small>mdi-refresh</v-icon></v-btn>
</template>
<span>Check for updates</span>
</v-tooltip>
</v-toolbar>
<v-card-text class="px-0 py-0">
<v-container py-0 px-0>
<div v-for="(value, key) of updateableSoftwares" v-bind:key="key">
<v-divider class="my-0" ></v-divider>
<v-row class="py-2">
<v-col class="pl-6">
<strong>{{ 'name' in value ? value.name : key }}</strong><br />
<span @click="openCommitsOverlay(key, value)" :class="getVersionClickable(value) ? 'primary--text cursor--pointer' : ''">{{ getVersionOutput(value) }}</span>
</v-col>
<v-col class="col-auto pr-6 text-right" align-self="center">
<v-chip
small
label
outlined
:color="getBtnColor(value)"
@click="updateModule(key)"
:disabled="getBtnDisabled(value)"
class="minwidth-0 px-2 text-uppercase"
><v-icon small class="mr-1">mdi-{{ getBtnIcon(value) }}</v-icon>{{ getBtnText(value) }}</v-chip>
</v-col>
</v-row>
</div>
<div v-if="'system' in version_info">
<v-divider class="my-0 border-top-2" ></v-divider>
<v-row class="pt-2">
<v-col class="col-auto pl-6 text-no-wrap">
<strong>System</strong><br />
<v-tooltip top v-if="version_info.system.package_count > 0" :max-width="300">
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">{{ version_info.system.package_count }} packages can be upgraded</span>
</template>
<span>{{ version_info.system.package_list.join(', ') }}</span>
</v-tooltip>
<span v-if="version_info.system.package_count === 0">OS-Packages</span>
</v-col>
<v-col class="pr-6 text-right" align-self="center">
<v-chip
small
label
outlined
:color="version_info.system.package_count ? 'primary' : 'green'"
:disabled="!(version_info.system.package_count) || printer_state === 'printing'"
@click="updateSystem"
class="minwidth-0 px-2 text-uppercase"
><v-icon small class="mr-1">mdi-{{ version_info.system.package_count ? 'progress-upload' : 'check' }}</v-icon>{{ version_info.system.package_count ? 'upgrade' : 'up-to-date' }}</v-chip>
</v-col>
</v-row>
</div>
</v-container>
</v-card-text>
</v-card>
<v-dialog v-model="commitsOverlay.bool" persistent width="60%" max-width="800">
<v-card dark>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>Commits</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn small class="minwidth-0" color="grey darken-3" @click="commitsOverlay.bool = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</v-toolbar>
<v-card-text class="py-5" v-if="commitsOverlay.loading">
<v-row class="pt-0">
<v-col>
<v-progress-linear color="primary" indeterminate></v-progress-linear>
</v-col>
</v-row>
</div>
<div v-if="'system' in version_info">
<v-divider class="my-0 border-top-2" ></v-divider>
<v-row class="pt-2">
<v-col class="col-auto pl-6 text-no-wrap">
<strong>System</strong><br />
<v-tooltip top v-if="version_info.system.package_count > 0" :max-width="300">
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">{{ version_info.system.package_count }} packages can be upgraded</span>
</template>
<span>{{ version_info.system.package_list.join(', ') }}</span>
</v-tooltip>
<span v-if="version_info.system.package_count === 0">OS-Packages</span>
</v-col>
<v-col class="pr-6 text-right" align-self="center">
<v-chip
small
label
outlined
:color="version_info.system.package_count ? 'primary' : 'green'"
:disabled="!(version_info.system.package_count) || printer_state === 'printing'"
@click="updateSystem"
class="minwidth-0 px-2 text-uppercase"
><v-icon small class="mr-1">mdi-{{ version_info.system.package_count ? 'progress-upload' : 'check' }}</v-icon>{{ version_info.system.package_count ? 'upgrade' : 'up-to-date' }}</v-chip>
</v-card-text>
<v-card-text class="py-0" v-if="!commitsOverlay.loading" style="max-height: 400px; overflow-y: scroll;">
<v-row>
<v-col class="pt-3 pl-0">
<v-timeline align-top dense >
<v-timeline-item color="primary" small v-for="commit of commitsOverlay.outputs" v-bind:key="commit.sha">
<v-row class="pt-0">
<v-col>
<a class="font-weight-bold white--text" :href="commit.url" target="_blank">{{ commit.title }}</a><br />
<p v-if="commit.message" class="mb-0" v-html="commit.message.replace(/(?:\r\n|\r|\n)/g, '<br />')"></p>
<div class="caption">
<strong>{{ commit.author }}</strong> committed at {{ commit.date.toLocaleString() }}
</div>
</v-col>
</v-row>
</v-timeline-item>
</v-timeline>
</v-col>
</v-row>
</div>
</v-container>
</v-card-text>
</v-card>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { mapState } from 'vuex'
import semver from 'semver'
import axios from 'axios'
export default {
components: {
@ -75,12 +122,16 @@
},
data: function() {
return {
commitsOverlay: {
bool: false,
loading: false,
response: [],
outputs: [],
}
}
},
computed: {
...mapState({
package_version: state => state.packageVersion,
loadings: state => state.socket.loadings,
remoteMode: state => state.socket.remoteMode,
printer_state: state => state.printer.print_stats.state,
@ -103,7 +154,7 @@
if ('is_dirty' in object && object.is_dirty) return 'orange'
if ('is_valid' in object && !object.is_valid) return 'red'
if ('version' in object && 'remote_version' in object && object.version !== object.remote_version) return 'primary'
if ('version' in object && 'remote_version' in object && semver.gt(object.remote_version, object.version)) return 'primary'
return 'green'
}
@ -116,7 +167,7 @@
if ('is_valid' in object && !object.is_valid) return 'invalid'
if ('is_dirty' in object && object.is_dirty) return 'dirty'
if ('version' in object && 'remote_version' in object && object.version !== object.remote_version) return 'update'
if ('version' in object && 'remote_version' in object && semver.gt(object.remote_version, object.version)) return 'update'
return 'up-to-date'
}
@ -129,7 +180,7 @@
if ('is_valid' in object && !object.is_valid) return 'alert-circle'
if ('is_dirty' in object && object.is_dirty) return 'alert-circle'
if ('version' in object && 'remote_version' in object && object.version !== object.remote_version) return 'progress-upload'
if ('version' in object && 'remote_version' in object && semver.gt(object.remote_version, object.version)) return 'progress-upload'
return 'check'
}
@ -142,7 +193,7 @@
if (typeof object === 'object' && object !== false) {
if ('is_valid' in object && !object.is_valid) return true
if ('version' in object && 'remote_version' in object && object.version !== object.remote_version) return false
if ('version' in object && 'remote_version' in object && semver.gt(object.remote_version, object.version)) return false
}
return true
@ -160,10 +211,19 @@
}
if (output !== "") output += ": "
output += local_version !== remote_version ? local_version+" > "+remote_version : local_version
output += semver.gt(remote_version, local_version) ? local_version+" > "+remote_version : local_version
return output
},
getVersionClickable(object) {
return (
'branch' in object &&
object.branch === "master" &&
'current_hash' in object &&
'remote_hash' in object &&
object.current_hash !== object.remote_hash
)
},
updateModule(key) {
if (["klipper", "moonraker"].includes(key)) this.$socket.sendObj('machine.update.'+key, { })
else this.$socket.sendObj('machine.update.client', { name: key })
@ -171,6 +231,70 @@
updateSystem() {
this.$socket.sendObj('machine.update.system', { })
},
openCommitsOverlay(key, object) {
if (
['klipper', 'moonraker'].includes(key) &&
this.getVersionClickable(object)
) {
this.commitsOverlay.bool = true
this.commitsOverlay.loading = true
let owner = ""
switch(key) {
case 'klipper':
owner = "kevinOConnor"
break
case 'moonraker':
owner = "arksine"
break
}
const apiUrl = "https://api.github.com/repos/"+owner+"/"+key+"/commits"
if (apiUrl) {
axios
.get(apiUrl)
.then(response => {
this.commitsOverlay.response = response
this.commitsOverlay.outputs.splice(0, this.commitsOverlay.outputs.length)
const index = response.data.findIndex(commit => commit.sha === object.current_hash)
if (index !== -1) {
this.commitsOverlay.response = response.data.splice(0, index).sort((a,b) => {
const dataA = new Date(a.commit.author.date)
const dataB = new Date(b.commit.author.date)
return dataB - dataA
})
this.commitsOverlay.response.forEach(commit => {
const date = new Date(commit.commit.author.date)
const author = commit.commit.author.name
const firstIndex = commit.commit.message.indexOf('\n\n')
let lastIndex = commit.commit.message.lastIndexOf('\n\n')
if (firstIndex === lastIndex && commit.commit.message.lastIndexOf('\r') > firstIndex) {
lastIndex = commit.commit.message.lastIndexOf('\r')
}
this.commitsOverlay.outputs.push({
sha: commit.sha,
date: date,
author: author,
url: commit.html_url,
title: commit.commit.message.substr(0, firstIndex),
message: (firstIndex+2 < lastIndex-2) ? commit.commit.message.substr(firstIndex+2, lastIndex-firstIndex-2) : "",
firstIndex: firstIndex,
lastIndex: lastIndex,
})
})
}
this.commitsOverlay.loading = false
})
}
}
},
}
}
</script>