azure-pipelines-tui 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EnvironmentsScreen = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const blessed = __importStar(require("blessed"));
39
+ const api_js_1 = require("../lib/api.js");
40
+ const format_js_1 = require("../lib/format.js");
41
+ const cache_js_1 = require("../cache.js");
42
+ class EnvironmentsScreen {
43
+ screen;
44
+ ctx;
45
+ colHeader;
46
+ widget;
47
+ rows = [];
48
+ flatItems = [];
49
+ collapsed = new Set();
50
+ get footerText() {
51
+ return (" {cyan-fg}↑↓{/} Navigate {cyan-fg}Enter{/} Expand/Stages {cyan-fg}←{/} Collapse " +
52
+ "{cyan-fg}p{/} Pipelines {cyan-fg}m{/} Mapping {cyan-fg}r{/} Refresh {cyan-fg}c{/} Clear {cyan-fg}q{/} Quit");
53
+ }
54
+ constructor(screen, ctx) {
55
+ this.screen = screen;
56
+ this.ctx = ctx;
57
+ this.colHeader = blessed.box({
58
+ parent: screen, top: 1, left: 0, width: "100%", height: 1,
59
+ style: { bg: "black", fg: "cyan", bold: true },
60
+ content: (0, format_js_1.envColHeaderStr)(), hidden: true,
61
+ });
62
+ this.widget = blessed.list({
63
+ parent: screen, top: 2, left: 0, width: "100%", height: "100%-4",
64
+ border: { type: "line" }, label: " Environments ",
65
+ tags: true, keys: true, vi: true, scrollable: true,
66
+ scrollbar: { ch: "│", style: { fg: "blue" } },
67
+ style: {
68
+ border: { fg: "cyan" }, selected: { bg: "blue", fg: "white", bold: true },
69
+ item: { fg: "white" }, focus: { border: { fg: "white" } },
70
+ },
71
+ items: [], hidden: true,
72
+ });
73
+ this.registerKeys();
74
+ }
75
+ show() {
76
+ this.colHeader.show();
77
+ this.widget.show();
78
+ this.widget.focus();
79
+ if (this.rows.length === 0)
80
+ this.loadData();
81
+ }
82
+ hide() { this.colHeader.hide(); this.widget.hide(); }
83
+ getRows() { return this.rows; }
84
+ refresh() {
85
+ const prevKey = this.flatItems[this.widget.selected]?.key;
86
+ const tree = (0, format_js_1.buildEnvTree)(this.rows);
87
+ this.flatItems = [];
88
+ (0, format_js_1.flattenEnvTree)(tree, this.collapsed, 0, this.flatItems);
89
+ this.widget.setItems(this.flatItems.map(format_js_1.formatEnvItem));
90
+ if (prevKey) {
91
+ const idx = this.flatItems.findIndex(i => i.key === prevKey);
92
+ if (idx >= 0)
93
+ this.widget.select(idx);
94
+ }
95
+ this.screen.render();
96
+ }
97
+ selected() {
98
+ const idx = this.widget.selected ?? 0;
99
+ return this.flatItems[idx];
100
+ }
101
+ async openStagesForRow(row) {
102
+ if (!row.mapping) {
103
+ const bid = row.deploy?.owner?.id ? Number(row.deploy.owner.id) : 0;
104
+ if (!bid)
105
+ return;
106
+ const url = `https://dev.azure.com/${this.ctx.org}/${this.ctx.project}/_build/results?buildId=${bid}`;
107
+ try {
108
+ (0, child_process_1.spawn)("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
109
+ }
110
+ catch { }
111
+ return;
112
+ }
113
+ let pip = this.ctx.state.pipelines.find(p => p.id === row.mapping.pipelineId);
114
+ if (!pip) {
115
+ this.ctx.setStatus("Loading pipeline definitions…", 0);
116
+ try {
117
+ await this.ctx.loadPipelineDefinitions();
118
+ pip = this.ctx.state.pipelines.find(p => p.id === row.mapping.pipelineId);
119
+ }
120
+ catch (e) {
121
+ this.ctx.setStatus(`Error: ${e.message.slice(0, 80)}`, 5000);
122
+ return;
123
+ }
124
+ }
125
+ if (pip)
126
+ this.ctx.navigate({ view: "stages", pipeline: pip });
127
+ else
128
+ this.ctx.setStatus(`Pipeline ${row.mapping.pipelineId} not found`);
129
+ }
130
+ async loadData() {
131
+ this.ctx.setStatus("Loading environments…", 0);
132
+ try {
133
+ const token = await this.ctx.getToken();
134
+ const envs = await (0, api_js_1.fetchAllEnvironments)(this.ctx.org, this.ctx.project, token);
135
+ this.rows = envs.map(env => ({
136
+ env, mapping: this.ctx.config.mappings.find(m => m.environmentId === env.id), loading: true,
137
+ }));
138
+ this.refresh();
139
+ this.ctx.setStatus(`${envs.length} environments — loading deployments…`, 0);
140
+ if (this.ctx.state.pipelines.length === 0) {
141
+ (0, api_js_1.fetchPipelineDefinitions)(this.ctx.org, this.ctx.project, token)
142
+ .then(defs => { this.ctx.state.pipelines = defs; })
143
+ .catch(() => { });
144
+ }
145
+ const BATCH = 10;
146
+ for (let i = 0; i < this.rows.length; i += BATCH) {
147
+ const batch = this.rows.slice(i, i + BATCH);
148
+ await Promise.all(batch.map(async (row) => {
149
+ row.deploy = (await (0, api_js_1.fetchLatestDeployment)(this.ctx.org, this.ctx.project, row.env.id, token)) ?? undefined;
150
+ row.loading = false;
151
+ }));
152
+ this.refresh();
153
+ this.ctx.setStatus(`Deployments ${Math.min(i + BATCH, this.rows.length)}/${this.rows.length}…`, 0);
154
+ }
155
+ const withDeploy = this.rows.filter(r => r.deploy?.owner?.id);
156
+ let done = 0;
157
+ for (let i = 0; i < withDeploy.length; i += BATCH) {
158
+ const batch = withDeploy.slice(i, i + BATCH);
159
+ await Promise.all(batch.map(async (row) => {
160
+ row.build = (await (0, api_js_1.fetchBuildInfo)(this.ctx.org, this.ctx.project, row.deploy.owner.id, token)) ?? undefined;
161
+ done++;
162
+ }));
163
+ this.refresh();
164
+ this.ctx.setStatus(`Build info ${done}/${withDeploy.length}…`, 0);
165
+ }
166
+ this.ctx.setStatus("", 0);
167
+ }
168
+ catch (e) {
169
+ this.ctx.setStatus(`Error: ${e.message.slice(0, 80)}`, 10_000);
170
+ }
171
+ }
172
+ registerKeys() {
173
+ this.widget.key("p", () => this.ctx.navigate({ view: "pipelines" }));
174
+ this.widget.key("m", () => this.ctx.navigate({ view: "mapping" }));
175
+ this.widget.key("r", () => {
176
+ const { org, project } = this.ctx;
177
+ (0, cache_js_1.clearByPrefix)(`envs_${org}_${project}`);
178
+ (0, cache_js_1.clearByPrefix)(`deploy_${org}_${project}`);
179
+ this.rows = [];
180
+ this.ctx.setStatus("Refreshing…", 0);
181
+ this.loadData();
182
+ });
183
+ this.widget.key("c", () => { (0, cache_js_1.clearAllCache)(); this.ctx.setStatus("All caches cleared"); });
184
+ this.widget.key("enter", () => {
185
+ const item = this.selected();
186
+ if (!item)
187
+ return;
188
+ if (item.kind === "group") {
189
+ if (this.collapsed.has(item.key))
190
+ this.collapsed.delete(item.key);
191
+ else
192
+ this.collapsed.add(item.key);
193
+ this.refresh();
194
+ }
195
+ else {
196
+ this.openStagesForRow(item.row);
197
+ }
198
+ });
199
+ this.widget.key("left", () => {
200
+ const item = this.selected();
201
+ if (!item)
202
+ return;
203
+ if (item.kind === "group" && item.isExpanded) {
204
+ this.collapsed.add(item.key);
205
+ this.refresh();
206
+ }
207
+ else {
208
+ const parentKey = item.key.split("-").slice(0, -1).join("-");
209
+ if (parentKey) {
210
+ const idx = this.flatItems.findIndex(i => i.kind === "group" && i.key === parentKey);
211
+ if (idx >= 0) {
212
+ this.widget.select(idx);
213
+ this.screen.render();
214
+ }
215
+ }
216
+ }
217
+ });
218
+ }
219
+ }
220
+ exports.EnvironmentsScreen = EnvironmentsScreen;
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MappingScreen = void 0;
37
+ const blessed = __importStar(require("blessed"));
38
+ const api_js_1 = require("../lib/api.js");
39
+ const format_js_1 = require("../lib/format.js");
40
+ class MappingScreen {
41
+ screen;
42
+ ctx;
43
+ envScreen;
44
+ leftWidget;
45
+ rightWidget;
46
+ configFile;
47
+ snapshot = [];
48
+ get footerText() {
49
+ return (" {cyan-fg}Tab{/} Switch panels {cyan-fg}Space{/} Link env→pipeline " +
50
+ "{cyan-fg}d{/} Delete {cyan-fg}s{/} Save {cyan-fg}Esc{/} Discard");
51
+ }
52
+ constructor(screen, ctx, envScreen, configFile) {
53
+ this.screen = screen;
54
+ this.ctx = ctx;
55
+ this.envScreen = envScreen;
56
+ this.configFile = configFile;
57
+ this.leftWidget = blessed.list({
58
+ parent: screen, top: 1, left: 0, width: "50%", height: "100%-3",
59
+ border: { type: "line" }, label: " Environments ",
60
+ tags: true, keys: true, vi: true, scrollable: true,
61
+ scrollbar: { ch: "│", style: { fg: "blue" } },
62
+ style: { border: { fg: "gray" }, selected: { bg: "blue", fg: "white" }, focus: { border: { fg: "white" } } },
63
+ items: [], hidden: true,
64
+ });
65
+ this.rightWidget = blessed.list({
66
+ parent: screen, top: 1, left: "50%", width: "50%", height: "100%-3",
67
+ border: { type: "line" }, label: " Pipeline Definitions ",
68
+ tags: true, keys: true, vi: true, scrollable: true,
69
+ scrollbar: { ch: "│", style: { fg: "blue" } },
70
+ style: { border: { fg: "gray" }, selected: { bg: "blue", fg: "white" }, focus: { border: { fg: "white" } } },
71
+ items: [], hidden: true,
72
+ });
73
+ this.registerKeys();
74
+ }
75
+ show() {
76
+ if (this.ctx.state.pipelines.length === 0) {
77
+ this.ctx.setStatus("Pipeline definitions still loading…");
78
+ this.ctx.navigate({ view: "environments" });
79
+ return;
80
+ }
81
+ this.snapshot = this.ctx.config.mappings.map(m => ({ ...m }));
82
+ this.populateLeft();
83
+ this.rightWidget.setItems(this.ctx.state.pipelines.map(p => {
84
+ const nm = (0, format_js_1.padEnd)(p.name, 50);
85
+ const pt = p.path !== "\\" ? ` {gray-fg}${p.path}{/}` : "";
86
+ return ` ${(0, format_js_1.padEnd)(String(p.id), 6)} ${nm}${pt}`;
87
+ }));
88
+ this.leftWidget.show();
89
+ this.rightWidget.show();
90
+ this.leftWidget.focus();
91
+ this.screen.render();
92
+ }
93
+ hide() { this.leftWidget.hide(); this.rightWidget.hide(); }
94
+ populateLeft() {
95
+ const rows = this.envScreen.getRows();
96
+ this.leftWidget.setItems(rows.map(row => {
97
+ const name = (0, format_js_1.padEnd)(row.env.name, 32);
98
+ let tag;
99
+ if (row.mapping) {
100
+ tag = `{cyan-fg}[${(0, format_js_1.padEnd)(row.mapping.pipelineName, 30)}]{/}`;
101
+ }
102
+ else if (row.deploy) {
103
+ tag = `{gray-fg}[auto: ${(0, format_js_1.padEnd)(row.deploy.definition.name, 25)}]{/}`;
104
+ }
105
+ else {
106
+ tag = "{gray-fg}[-]{/}";
107
+ }
108
+ return `${name} ${tag}`;
109
+ }));
110
+ }
111
+ link() {
112
+ const rows = this.envScreen.getRows();
113
+ const envSel = this.leftWidget.selected ?? 0;
114
+ const pipSel = this.rightWidget.selected ?? 0;
115
+ const row = rows[envSel];
116
+ const pip = this.ctx.state.pipelines[pipSel];
117
+ if (!row || !pip)
118
+ return;
119
+ const m = {
120
+ environmentId: row.env.id, environmentName: row.env.name,
121
+ pipelineId: pip.id, pipelineName: pip.name,
122
+ };
123
+ this.ctx.config.mappings = this.ctx.config.mappings.filter(x => x.environmentId !== row.env.id);
124
+ this.ctx.config.mappings.push(m);
125
+ row.mapping = m;
126
+ this.populateLeft();
127
+ this.ctx.setStatus(`Linked "${row.env.name}" → "${pip.name}"`, 0);
128
+ this.screen.render();
129
+ }
130
+ discard() {
131
+ this.ctx.config.mappings = this.snapshot;
132
+ const rows = this.envScreen.getRows();
133
+ rows.forEach(row => {
134
+ row.mapping = this.ctx.config.mappings.find(m => m.environmentId === row.env.id);
135
+ });
136
+ }
137
+ save() {
138
+ (0, api_js_1.saveConfig)(this.configFile, this.ctx.config);
139
+ this.ctx.navigate({ view: "environments" });
140
+ this.ctx.setStatus("Config saved");
141
+ }
142
+ registerKeys() {
143
+ this.leftWidget.key("tab", () => { this.rightWidget.focus(); this.screen.render(); });
144
+ this.rightWidget.key("tab", () => { this.leftWidget.focus(); this.screen.render(); });
145
+ this.leftWidget.key(["space", "enter"], () => this.link());
146
+ this.rightWidget.key(["space", "enter"], () => this.link());
147
+ this.leftWidget.key("d", () => {
148
+ const rows = this.envScreen.getRows();
149
+ const sel = this.leftWidget.selected ?? 0;
150
+ const row = rows[sel];
151
+ if (!row)
152
+ return;
153
+ this.ctx.config.mappings = this.ctx.config.mappings.filter(x => x.environmentId !== row.env.id);
154
+ row.mapping = undefined;
155
+ this.populateLeft();
156
+ this.ctx.setStatus(`Deleted mapping for "${row.env.name}"`, 0);
157
+ this.screen.render();
158
+ });
159
+ this.leftWidget.key("s", () => this.save());
160
+ this.rightWidget.key("s", () => this.save());
161
+ this.leftWidget.key("escape", () => { this.discard(); this.ctx.navigate({ view: "environments" }); });
162
+ this.rightWidget.key("escape", () => { this.discard(); this.ctx.navigate({ view: "environments" }); });
163
+ }
164
+ }
165
+ exports.MappingScreen = MappingScreen;