From 9fc6f4182963f8f121c77db3b517f2d70b37dd02 Mon Sep 17 00:00:00 2001 From: Maksim Terpilovskii Date: Wed, 8 Mar 2023 00:19:20 +0300 Subject: [PATCH] implemented a better hierarchy in builds view, added approve and decline buttons, improved status icons --- CHANGELOG.md | 8 ++- README.md | 3 +- package.json | 38 +++++------- src/builds.ts | 147 ++++++++++++++++++++++++++++++++--------------- src/extension.ts | 8 +++ 5 files changed, 131 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd2bb6f..86d5577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 1.2.0 - 2023-03-08 + +- Builds view has a build → stage → step hierarchy now. +- Added Approve and Decline buttons to stage items in Builds view. +- Improved status icons in Builds view. + ## 1.1.0 - 2023-03-07 - Improved error handling. @@ -12,4 +18,4 @@ ## 1.0.0 - 2023-03-04 -- Initial release \ No newline at end of file +- Initial release. \ No newline at end of file diff --git a/README.md b/README.md index e4365a0..c68982f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/maximtrp/droneci-vscode-extension/main.yml) ![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/maximtrp.drone-ci) +![Codacy Grade](https://img.shields.io/codacy/grade/5bfec3730914417d958e8dfb3bd00f3e/main) This extension integrates Drone CI into VS Code. It allows you to quickly browse and manage your repos, builds, secrets and cron jobs. @@ -17,7 +18,7 @@ and cron jobs. ## Installation -This extension is available on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=maximtrp.drone-ci) for Visual Studio Code. +This extension is available on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=maximtrp.drone-ci) for Visual Studio Code and [Open VSX Registry](https://open-vsx.org/extension/maximtrp/drone-ci). ## Copyright diff --git a/package.json b/package.json index 1a1eed1..d81a914 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "drone-ci", "displayName": "Drone CI", "description": "Manage your Drone CI servers easily", - "version": "1.1.0", + "version": "1.2.0", "publisher": "maximtrp", "author": { "name": "Maksim Terpilovskii", @@ -176,12 +176,12 @@ "icon": "$(open-preview)" }, { - "command": "drone-ci.approveBuild", + "command": "drone-ci.approveBuildStage", "title": "Approve", "icon": "$(check)" }, { - "command": "drone-ci.declineBuild", + "command": "drone-ci.declineBuildStage", "title": "Decline", "icon": "$(close)" }, @@ -389,19 +389,19 @@ "when": "view == drone-ci-repos && viewItem =~ /^repo/" }, { - "command": "drone-ci.approveBuild", - "when": "view == drone-ci-builds && viewItem == build_blocked", - "group": "actions" + "command": "drone-ci.approveBuildStage", + "when": "view == drone-ci-builds && viewItem == stage_blocked", + "group": "inline" }, { - "command": "drone-ci.declineBuild", - "when": "view == drone-ci-builds && viewItem == build_blocked", - "group": "actions" + "command": "drone-ci.declineBuildStage", + "when": "view == drone-ci-builds && viewItem == stage_blocked", + "group": "inline" }, { "command": "drone-ci.cancelBuild", "when": "view == drone-ci-builds && viewItem == build_running", - "group": "actions" + "group": "inline" }, { "command": "drone-ci.restartBuild", @@ -516,16 +516,6 @@ } ], "drone-ci.build": [ - { - "command": "drone-ci.approveBuild", - "when": "view == drone-ci-builds && viewItem == build_blocked", - "group": "actions" - }, - { - "command": "drone-ci.declineBuild", - "when": "view == drone-ci-builds && viewItem == build_blocked", - "group": "actions" - }, { "command": "drone-ci.restartBuild", "when": "view == drone-ci-builds && viewItem =~ /^build/", @@ -645,19 +635,19 @@ "when": "false" }, { - "command": "drone-ci.viewBuildStepLog", + "command": "drone-ci.declineBuildStage", "when": "false" }, { - "command": "drone-ci.viewBuildInfo", + "command": "drone-ci.approveBuildStage", "when": "false" }, { - "command": "drone-ci.approveBuild", + "command": "drone-ci.viewBuildStepLog", "when": "false" }, { - "command": "drone-ci.declineBuild", + "command": "drone-ci.viewBuildInfo", "when": "false" }, { diff --git a/src/builds.ts b/src/builds.ts index 6f11962..0bac090 100644 --- a/src/builds.ts +++ b/src/builds.ts @@ -27,7 +27,9 @@ interface BuildInfo { } interface StageInfo { + name: string; number: number; + status: string; steps: StepInfo[]; } @@ -46,6 +48,33 @@ interface BuildIcon { color: string; } +function getIcon(status: string = "fallback", event?: string): vscode.ThemeIcon { + let presets: { [status: string]: BuildIcon } = { + skipped: { icon: "debug-step-over", color: "disabledForeground" }, + declined: { icon: "circle-slash", color: "charts.red" }, + // eslint-disable-next-line @typescript-eslint/naming-convention + waiting_on_dependencies: { icon: "watch", color: "charts.yellow" }, + blocked: { icon: "warning", color: "charts.yellow" }, + success: { icon: "pass", color: "charts.green" }, + error: { icon: "error", color: "charts.red" }, + failure: { icon: "error", color: "charts.red" }, + pending: { icon: "sync~spin", color: "charts.blue" }, + killed: { icon: "stop-circle", color: "charts.red" }, + running: { icon: "run", color: "charts.green" }, + fallback: { icon: "pass", color: "charts.purple" }, + }; + let selectedPreset: BuildIcon = presets[status] || presets.fallback; + if (event === "custom" && status !== "running") { + selectedPreset.icon = "repl"; + } else if (event === "promote" && status !== "running") { + selectedPreset.icon = "rocket"; + } else if (event === "cron" && status !== "running") { + selectedPreset.icon = "calendar"; + } + + return new vscode.ThemeIcon(selectedPreset.icon, new vscode.ThemeColor(selectedPreset.color)); +} + export class BuildsProvider implements vscode.TreeDataProvider { private client: any | null = null; public data: RepoInfo | null = null; @@ -75,25 +104,30 @@ export class BuildsProvider implements vscode.TreeDataProvider { + async getChildren(element?: Build | Stage): Promise { if (this.client && this.data) { - if (build) { - let buildInfo: BuildInfo | null = await this.client.getBuild(this.data.owner, this.data.repo, build.number); - - if (buildInfo && buildInfo.stages && buildInfo.stages[0].steps) { - const stepsInfo: { step: StepInfo; stage: StageInfo; build: number }[] = buildInfo.stages - .map((stage) => - stage.steps.map((step) => ({ - build: build.number, - stage, - step, - })) - ) - .flat(); - let steps: Step[] = stepsInfo.map((stepInfo) => new Step(stepInfo.step, stepInfo.stage, stepInfo.build)); - if (steps.length > 0) { - return steps; + if (element) { + if (element.contextValue?.startsWith("build")) { + try { + let buildInfo: BuildInfo | null = await this.client.getBuild( + this.data.owner, + this.data.repo, + element.number + ); + if (buildInfo && buildInfo.stages && buildInfo.stages.length > 0) { + return buildInfo.stages.map((stage) => new Stage(stage, buildInfo!.number)); + } else { + return [new None("No stages found")]; + } + } catch (e) { + return [new None("Error occurred while loading build stages")]; + } + } else if (element.contextValue?.startsWith("stage")) { + const stage = element as Stage; + if (stage.steps && stage.steps.length > 0) { + return stage.steps.map((step) => new Step(step, stage.number, stage.build)); } + return [new None("No steps found")]; } return [new None("Nothing found")]; } else { @@ -208,6 +242,32 @@ export class BuildsProvider implements vscode.TreeDataProvider !!i) .join("\n"); this.contextValue = `build_${build.status}`; - this.iconPath = this.selectIcon(build); - } - - selectIcon(build: BuildInfo): vscode.ThemeIcon { - let presets: { [status: string]: BuildIcon } = { - success: { icon: "pass", color: "charts.green" }, - error: { icon: "error", color: "charts.red" }, - failure: { icon: "error", color: "charts.red" }, - pending: { icon: "sync~spin", color: "charts.blue" }, - killed: { icon: "stop-circle", color: "charts.red" }, - running: { icon: "run", color: "charts.green" }, - fallback: { icon: "pass", color: "charts.yellow" }, - }; - let selectedPreset: BuildIcon = presets[build.status] || presets.fallback; - if (build.event === "custom" && build.status !== "running") { - selectedPreset.icon = "repl"; - } else if (build.event === "promote" && build.status !== "running") { - selectedPreset.icon = "rocket"; - } else if (build.event === "cron" && build.status !== "running") { - selectedPreset.icon = "calendar"; - } - - return new vscode.ThemeIcon(selectedPreset.icon, new vscode.ThemeColor(selectedPreset.color)); + this.iconPath = getIcon(build.status, build.event); } } diff --git a/src/extension.ts b/src/extension.ts index 976f391..11b14e0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -88,6 +88,12 @@ export function activate(context: vscode.ExtensionContext) { let commandPromoteBuild = vscode.commands.registerCommand("drone-ci.promoteBuild", (build) => buildsProvider.promoteBuild(build) ); + let commandApproveBuildStage = vscode.commands.registerCommand("drone-ci.approveBuildStage", (stage) => + buildsProvider.approveBuildStage(stage) + ); + let commandDeclineBuildStage = vscode.commands.registerCommand("drone-ci.declineBuildStage", (stage) => + buildsProvider.declineBuildStage(stage) + ); let commandCancelBuild = vscode.commands.registerCommand("drone-ci.cancelBuild", (build) => buildsProvider.cancelBuild(build) ); @@ -196,6 +202,8 @@ export function activate(context: vscode.ExtensionContext) { commandAddCron, commandEditCron, commandTriggerBuild, + commandApproveBuildStage, + commandDeclineBuildStage, commandDisableRepo, commandEnableRepo, commandRepairRepo,