From 15cdb396d7ac3e5699067994ead6c573441a8307 Mon Sep 17 00:00:00 2001 From: mqxf Date: Sat, 24 Sep 2022 22:56:03 +0200 Subject: [PATCH] Added download with progress, and new version check --- .mvc/config.json | 19 +++--- .mvc/scripts.json | 2 +- src/create/create.ts | 4 +- src/create/java.ts | 4 ++ src/create/setup.ts | 5 +- src/download/download.ts | 97 ++++++++++++++++++++++++++++++ src/file.ts | 1 + src/main.ts | 28 +++++++-- src/update/repos.ts | 126 +++++++++++++++++++++++++++++++++++++++ src/update/update.ts | 77 ++++++++++++++++++++++++ src/update/upgrade.ts | 88 +++++++++++++++++++++++++++ 11 files changed, 435 insertions(+), 16 deletions(-) create mode 100644 src/download/download.ts create mode 100644 src/update/repos.ts create mode 100644 src/update/update.ts create mode 100644 src/update/upgrade.ts diff --git a/.mvc/config.json b/.mvc/config.json index d6ab7d0..67fc64c 100644 --- a/.mvc/config.json +++ b/.mvc/config.json @@ -1,10 +1,11 @@ { - "name": "mvc", - "language": "ts", - "type": "app", - "framework": "none", - "author": "Maxim Savenkov", - "git": true, - "gitLink": "https://github.com/TeamMV/mvc.git", - "licence": "MIT" -} + "name": "mvc", + "language": "ts", + "type": "app", + "framework": "none", + "author": "Maxim Savenkov", + "git": true, + "gitLink": "https://github.com/TeamMV/mvc.git", + "licence": "MIT", + "frameworkVersion": "v0.0.0" +} \ No newline at end of file diff --git a/.mvc/scripts.json b/.mvc/scripts.json index 093b3d4..3814731 100644 --- a/.mvc/scripts.json +++ b/.mvc/scripts.json @@ -15,7 +15,7 @@ }, { "name": "build", - "script": "bWFrZQpzdWRvIG12IGJpbi9tdmMgL3Vzci9sb2NhbC9iaW4vLgo=", + "script": "bWFrZQpzdWRvIG12IGJpbi9tdmMgL3Vzci9iaW4vLgo=", "type": "sh", "args": 0 } diff --git a/src/create/create.ts b/src/create/create.ts index e3e0b86..784f9b4 100644 --- a/src/create/create.ts +++ b/src/create/create.ts @@ -176,7 +176,7 @@ export async function createProject(args: string[]) { }, { name: "None", - value: "" + value: "none" } ] }]); @@ -230,5 +230,7 @@ export async function createProject(args: string[]) { case "java": finalizeJava(setup); break; + case "ts": + break; } } \ No newline at end of file diff --git a/src/create/java.ts b/src/create/java.ts index 69aa550..6d213fc 100644 --- a/src/create/java.ts +++ b/src/create/java.ts @@ -1,5 +1,6 @@ import {Input, prompt } from "https://deno.land/x/cliffy@v0.25.0/prompt/mod.ts"; import { ConfigFile, ScriptsFile, writeConfig, writeScripts } from "../file.ts"; +import { updateVersion } from "../update/repos.ts"; import { shScript, sh } from "../utils.ts"; import { Setup } from "./create.ts"; @@ -68,6 +69,7 @@ export async function finalizeJava(setup: Setup) { language: "java", type: setup.type, framework: setup.framework, + frameworkVersion: "none", author: setup.author, git: setup.git, gitLink: setup.gitLink, @@ -92,4 +94,6 @@ export async function finalizeJava(setup: Setup) { await writeConfig(config); await writeScripts(scripts); + + await updateVersion("java", config.framework); } \ No newline at end of file diff --git a/src/create/setup.ts b/src/create/setup.ts index 81cc341..8b769b4 100644 --- a/src/create/setup.ts +++ b/src/create/setup.ts @@ -1,6 +1,8 @@ import { Confirm, Input, Select, prompt } from "https://deno.land/x/cliffy@v0.25.0/prompt/mod.ts"; import { writeConfig, writeScripts } from "../file.ts"; import { printSetupHelpMenu } from "../help.ts"; +import { getRepo } from "../update/repos.ts"; +import { getVersion } from "../update/repos.ts"; import { sh } from "../utils.ts"; export interface SetupEmpty { @@ -169,7 +171,7 @@ export async function setupProject(args: string[]) { }, { name: "None", - value: "" + value: "none" } ] }]); @@ -185,6 +187,7 @@ export async function setupProject(args: string[]) { language: setup.language!, type: setup.type!, framework: setup.framework!, + frameworkVersion: await getVersion(getRepo(setup.language, setup.framework)), author: setup.author!, git: setup.git!, gitLink: setup.gitLink!, diff --git a/src/download/download.ts b/src/download/download.ts new file mode 100644 index 0000000..5a5fb37 --- /dev/null +++ b/src/download/download.ts @@ -0,0 +1,97 @@ +// deno-lint-ignore-file prefer-const ban-types +import ProgressBar from "https://deno.land/x/progress@v1.2.7/mod.ts"; +import { bgWhite, green, } from "https://deno.land/std@0.74.0/fmt/colors.ts"; +import { Buffer } from "https://deno.land/std@0.141.0/io/buffer.ts"; + +export interface Destination { + dir?: string, + file?: string, + mode?: number +} + +export interface DownlodedFile { + file: string, + dir: string, + fullPath: string, + size: number +} + +export async function download(url: string | URL, destination?: Destination, options?: RequestInit) { + let file: string; + let fullPath: string; + let dir = ''; + let mode: object = {}; + let finalUrl: string; + let size: number; + + const response = await fetch(url, options); + finalUrl = response.url.replace(/\/$/, ""); + if (response.status != 200) { + return Promise.reject( + new Deno.errors.Http(`status ${response.status}-'${response.statusText}' received instead of 200`) + ); + } + + if (response == null) { + return; + } + + const reader = response.body!.getReader(); + + const contentLength = Math.floor(parseInt(response.headers.get('Content-Length')!) / 1024); + + const progress = new ProgressBar({ + total: contentLength, + preciseBar: [ + bgWhite(green("▏")), + bgWhite(green("▎")), + bgWhite(green("▍")), + bgWhite(green("▌")), + bgWhite(green("▋")), + bgWhite(green("▊")), + bgWhite(green("▉")), + ] + }); + + let receivedLength = 0; + + while (true) { + const { done, value } = await reader.read(); + + if (done) { + break; + } + + receivedLength += value.length; + + progress.render(Math.floor(receivedLength / 1024)); + } + + const blob = await response.blob(); + size = blob.size; + const buffer = await blob.arrayBuffer(); + const unit8arr = new Buffer(buffer).bytes(); + if (typeof destination === 'undefined' || typeof destination.dir === 'undefined') { + dir = Deno.makeTempDirSync({ prefix: 'deno_dwld' }); + } + else { + dir = destination.dir; + } + + if (typeof destination === 'undefined' || typeof destination.file === 'undefined') { + file = finalUrl.substring(finalUrl.lastIndexOf('/') + 1); + } + else { + file = destination.file; + } + + if (typeof destination != 'undefined' && typeof destination.mode != 'undefined') { + mode = { mode: destination.mode } + } + + dir = dir.replace(/\/$/, ""); + + fullPath = `${dir}/${file}`; + Deno.writeFileSync(fullPath, unit8arr, mode); + return Promise.resolve({ file, dir, fullPath, size }); +} \ No newline at end of file diff --git a/src/file.ts b/src/file.ts index 3fd7daf..63cf32d 100644 --- a/src/file.ts +++ b/src/file.ts @@ -3,6 +3,7 @@ export interface ConfigFile { language: string; type: string; framework: string; + frameworkVersion: string; author: string; git: boolean; gitLink: string; diff --git a/src/main.ts b/src/main.ts index 9ab06c4..6015b42 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,48 +4,68 @@ import { printMainHelpMenu } from "./help.ts"; import { build, commit, other, push } from "./script/basic.ts"; import { runScript } from "./script/run.ts"; import { editScript } from "./script/script.ts"; +import { update } from "./update/update.ts"; +import { checkVersion, upgrade } from "./update/upgrade.ts"; import { checkDir, info } from "./utils.ts"; async function main(args: string[]) { if (args.length < 1) { - console.log("No command specified. Run 'mv help' for a list of commands."); + console.log("No command specified. Run 'mvc help' for a list of commands."); + return; + } + if (args[0] == "help") { + printMainHelpMenu(); return; } switch (args[0]) { - case "help": - printMainHelpMenu(); - break; case "create": + await checkVersion(); await createProject(args); break; case "setup": + await checkVersion(); await setupProject(args); break; + case "upgrade": + upgrade(); + break; case "info": + await checkVersion(); checkDir(); info(); break; + case "update": + await checkVersion(); + checkDir(); + update(); + break; case "run": + await checkVersion(); checkDir(); await runScript(args); break; case "script": + await checkVersion(); checkDir(); await editScript(args); break; case "push": + await checkVersion(); checkDir(); await push(args); break; case "commit": + await checkVersion(); checkDir(); await commit(args); break; case "build": + await checkVersion(); checkDir(); await build(args); break; default: + await checkVersion(); checkDir(); await other(args); break; diff --git a/src/update/repos.ts b/src/update/repos.ts new file mode 100644 index 0000000..09e1f50 --- /dev/null +++ b/src/update/repos.ts @@ -0,0 +1,126 @@ +import { updateJavaRender } from "./update.ts"; + +export const repos = { + mvc: "https://api.github.com/repos/TeamMV/mvc", + java: { + render: "https://api.github.com/repos/TeamMV/Rendering" + }, + ts: { + + } +}; + + +export function getRepo(language: string, framework: string) { + if (framework == "none" || framework == "") { + return ""; + } + + switch (language) { + case "java": + switch (framework) { + case "render": + return repos.java.render; + } + break; + case "typescript": + break; + } + + return ""; +} + +export async function updateVersion(language: string, framework: string) { + if (framework == "none") { + return; + } + + switch (language) { + case "java": + switch (framework) { + case "render": + await updateJavaRender(); + } + break; + case "typescript": + break; + } +} + +export interface Author { + login: string; + id: 1; + node_id: string; + avatar_url: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; +} + +export interface Asset { + url: string; + browser_download_url: string; + id: number; + node_id: string; + name: string; + label: string; + state: string; + content_type: string; + size: number; + download_count: number; + created_at: string; + updated_at: string; + uploader: Author; +} + +export interface Release { + url: string; + html_url: string; + assets_url: string; + upload_url: string; + tarball_url: string; + zipball_url: string; + discussion_url: string; + id: 1; + node_id: string; + tag_name: string; + target_commitish: string; + name: string; + body: string; + draft: boolean; + prerelease: boolean; + created_at: string; + published_at: string; + author: Author; + assets: Asset[]; +} + +export async function getVersion(repo: string) { + if (repo == "") return "v0.0.0"; + const res = await fetch(`${repo}/releases/latest`, { + method: "GET" + }); + const release = await res.json() as Release; + if (release == null || release.tag_name == null) { + return "v0.0.0"; + } + return release.tag_name; +} + +export async function getRelease(repo: string) { + const res = await fetch(`${repo}/releases/latest`, { + method: "GET" + }); + return await res.json() as Release; +} \ No newline at end of file diff --git a/src/update/update.ts b/src/update/update.ts new file mode 100644 index 0000000..a8eb673 --- /dev/null +++ b/src/update/update.ts @@ -0,0 +1,77 @@ +import { ConfigFile, getConfig, writeConfig } from "../file.ts"; +import { getRelease, getRepo, getVersion, updateVersion } from "./repos.ts"; +import { download, Destination } from "../download/download.ts"; +import { sh } from "../utils.ts"; + +export async function update() { + const config: ConfigFile = await getConfig(); + + if (config.frameworkVersion == null) { + config.frameworkVersion = "v0.0.0"; + await writeConfig(config); + console.log("Added frameworkVersion to config"); + } + + if (config.framework == "none") { + console.log("This project isn't using a framework, no need to update!"); + Deno.exit(0); + } + + console.log("Finding version..."); + + const link = getRepo(config.language, config.framework); + + const version = await getVersion(link); + + if (version == "-1" || version == null) { + console.log("Language or framework invalid, or github link failed..."); + Deno.exit(1); + } + + if (version != config.frameworkVersion) { + console.log("New version detected!"); + await updateVersion(config.language, config.framework); + } + else { + console.log("Framework is up to date!"); + } + +} + +export async function updateJavaRender() { + console.log("Checking assets..."); + const release = await getRelease(getRepo("java", "render")); + if (release == null) { + console.log("Assets not found!"); + return; + } + const asset = release.assets.find(asset => { + return asset.name == "rendering.jar"; + }); + if (asset == undefined) { + console.log("No new assets found, framework up to date!"); + return; + } + console.log("New assets found!"); + console.log("Downloading new assets..."); + await sh("mv libs/jar/rendering.jar libs/jar/old.jar"); + try { + const dest: Destination = { + file: "rendering.jar", + dir: "./libs/jar" + }; + await download(asset.browser_download_url, dest); + console.log("Downloaded new assets"); + await sh("rm libs/jar/old.jar"); + } + catch (_err) { + await sh("rm libs/jar/rendering.jar"); + await sh("mv libs/jar/old.jar libs/jar/rendering.jar"); + console.log("New assets download failed, try again later."); + return; + } + const config = await getConfig(); + config.frameworkVersion = release.tag_name; + await writeConfig(config); + console.log("Project is now up to date!"); +} \ No newline at end of file diff --git a/src/update/upgrade.ts b/src/update/upgrade.ts new file mode 100644 index 0000000..54055ef --- /dev/null +++ b/src/update/upgrade.ts @@ -0,0 +1,88 @@ +import { Destination, download } from "../download/download.ts"; +import { sh } from "../utils.ts"; +import { getRelease, getVersion, repos } from "./repos.ts"; + +const env = { + version: "v1.1.0", + type: "linux" +}; + +export async function upgrade() { + try { + await Deno.writeTextFile("/usr/bin/mvc-test-file-Gpl5sKHFIzg17MLj7wGT", ""); + sh("rm /usr/bin/mvc-test-file-Gpl5sKHFIzg17MLj7wGT"); + } + catch (_err) { + console.log("ERROR: This command needs to be ran as sudo!"); + return; + } + + try { + console.log("Checking local versions"); + await Deno.stat("/tmp/mvc-newVersion-G63fwFw3f8w"); + console.log("Local version found!"); + await sh("chmod +x /tmp/mvc-newVersion-G63fwFw3f8w", true); + await sh("mv /tmp/mvc-newVersion-G63fwFw3f8w /usr/bin/mvc"); + console.log("Tool is now up to date"); + return; + } catch (_err) { + console.log("No local versions found."); + } + + const version = await getVersion(repos.mvc); + if (version != env.version) { + console.log("New version detected!"); + console.log("Finding version release..."); + const release = await getRelease(repos.mvc); + if (release == null) { + console.log("Version release not found!"); + return; + } + const asset = release.assets.find(asset => { + return asset.name == `mvc-${env.type}`; + }); + if (asset == null) { + console.log("No new version releases found!"); + return; + } + console.log("Found new version release!"); + console.log("Downloading new version..."); + try { + const dest: Destination = { + file: "mvc-newVersion-G63fwFw3f8w", + dir: "/tmp/" + }; + await download(asset.browser_download_url, dest); + console.log("Downloaded new version!"); + await sh("chmod +x /tmp/mvc-newVersion-G63fwFw3f8w", true); + await sh("mv /tmp/mvc-newVersion-G63fwFw3f8w /usr/bin/mvc"); + } + catch (_err) { + console.log(_err); + console.log("New version download failed."); + return; + } + console.log("Tool is now up to date!"); + } + else { + console.log("Tool up to date!"); + } +} + +export async function checkVersion() { + try { + console.log("Checking for new version..."); + const version = await getVersion(repos.mvc); + if (version != env.version) { + console.log("New version detected!"); + console.log(` + Run 'sudo mvc upgrade' to update to the new version! + `); + } + else { + console.log("Tool is up to date!"); + } + } catch (_err) { + console.log("Tool is up to date!"); + } +}