((resolve) => {
134 | const quickPick = vscode.window.createQuickPick();
135 | const items = [
136 | {
137 | label: 'Push a local tag...',
138 | showLocal: true,
139 | } as vscode.QuickPickItem,
140 | {
141 | label: 'Remote tags',
142 | kind: -1,
143 | } as vscode.QuickPickItem,
144 | ...tags.map((label) => ({ label })),
145 | {
146 | label: '',
147 | kind: -1,
148 | } as vscode.QuickPickItem,
149 | ];
150 |
151 | quickPick.items = items;
152 | quickPick.placeholder =
153 | 'Select an existing tag or enter a name for a new one';
154 |
155 | quickPick.onDidChangeValue(() => {
156 | if (quickPick.value && !tags.includes(quickPick.value)) {
157 | quickPick.items = [{ label: quickPick.value }, ...items];
158 | } else {
159 | quickPick.items = items;
160 | }
161 | });
162 |
163 | quickPick.onDidAccept(async () => {
164 | const selection = quickPick.activeItems[0];
165 | quickPick.hide();
166 |
167 | if ('showLocal' in selection) {
168 | const localTag = await vscode.window.showQuickPick(
169 | await this.remote!.getLocalTags(),
170 | { placeHolder: 'Select a tag to push' },
171 | );
172 |
173 | if (
174 | !localTag ||
175 | !(await this.remote!.pushLocalTag(localTag))
176 | ) {
177 | resolve('');
178 | return;
179 | }
180 |
181 | resolve(localTag);
182 | return;
183 | }
184 |
185 | resolve(selection.label);
186 | });
187 |
188 | quickPick.show();
189 | });
190 |
191 | if (!tag) return;
192 |
193 | this.webviewView!.webview.postMessage({
194 | type: 'set-state',
195 | tag: {
196 | name: tag,
197 | existing: tags.includes(tag),
198 | },
199 | } satisfies PartialWebviewStateMessage);
200 | }
201 |
202 | async selectTarget() {
203 | const targets = [
204 | {
205 | value: '',
206 | label: 'Branches',
207 | kind: -1,
208 | },
209 | ...(await this.remote!.getBranches()).map((label) => ({
210 | value: label,
211 | label,
212 | })),
213 | {
214 | value: '',
215 | label: 'Commits',
216 | kind: -1,
217 | },
218 | ...(await this.remote!.getCommits()).map((commit) => ({
219 | value: commit.sha,
220 | label: commit.sha.slice(0, 8),
221 | detail: commit.message,
222 | })),
223 | ];
224 |
225 | const target = (await vscode.window.showQuickPick(targets, {
226 | placeHolder: 'Select a target for the release tag',
227 | matchOnDescription: true,
228 | matchOnDetail: true,
229 | })) ?? { value: '', label: '' };
230 |
231 | if (!target.value) return;
232 |
233 | this.webviewView!.webview.postMessage({
234 | type: 'set-state',
235 | target: {
236 | ref: target.value,
237 | display: target.label,
238 | },
239 | } satisfies PartialWebviewStateMessage);
240 | }
241 |
242 | async generateReleaseNotes({
243 | tag,
244 | target,
245 | }: {
246 | tag: string;
247 | target: string;
248 | }) {
249 | const notes = await this.remote!.generateReleaseNotes(tag, target);
250 |
251 | if (!notes) return;
252 |
253 | await this.webviewView!.webview.postMessage({
254 | type: 'set-state',
255 | title: notes.title,
256 | desc: notes.desc,
257 | } satisfies PartialWebviewStateMessage);
258 | }
259 |
260 | async processPublish(data: WebviewState) {
261 | const newRelease = await this.remote!.updateOrPublishRelease({
262 | id: this.baseRelease?.id,
263 | tag: data.tag.name,
264 | target: data.target.ref,
265 | title: data.title,
266 | desc: data.desc,
267 | draft: data.draft,
268 | prerelease: data.prerelease,
269 | makeLatest: data.makeLatest,
270 | });
271 |
272 | if (!newRelease) return;
273 |
274 | if (this.baseRelease) {
275 | for (let [id, name] of data.assets.deleted) {
276 | await this.remote!.tryDeleteReleaseAsset(id, name);
277 | }
278 |
279 | for (let [id, [oldName, newName]] of data.assets.renamed) {
280 | await this.remote!.tryRenameReleaseAsset(id, oldName, newName);
281 | }
282 | }
283 |
284 | for (let asset of data.assets.current) {
285 | if (!asset.new) continue;
286 |
287 | await this.remote!.tryUploadReleaseAsset(
288 | newRelease.id,
289 | asset.name,
290 | asset.path,
291 | );
292 | }
293 |
294 | this.hide();
295 |
296 | vscode.commands.executeCommand('github-releases.refreshReleases');
297 | }
298 |
299 | async processMessage(data: any) {
300 | switch (data.type) {
301 | case 'start': {
302 | this.sendStartMessage();
303 | break;
304 | }
305 | case 'save-state':
306 | this.state = data;
307 | break;
308 | case 'request-asset': {
309 | this.getAsset();
310 | break;
311 | }
312 | case 'name-in-use':
313 | vscode.window.showErrorMessage(
314 | 'A file with that name already exists.',
315 | );
316 | break;
317 | case 'select-tag': {
318 | this.selectTag();
319 | return;
320 | }
321 | case 'select-target': {
322 | this.selectTarget();
323 | return;
324 | }
325 | case 'generate-release-notes': {
326 | this.generateReleaseNotes(data);
327 | return;
328 | }
329 | case 'publish-release': {
330 | this.processPublish(data);
331 | break;
332 | }
333 | case 'cancel':
334 | this.hide();
335 | }
336 | }
337 |
338 | async resolveWebviewView(webviewView: vscode.WebviewView) {
339 | this.webviewView = webviewView;
340 |
341 | webviewView.onDidDispose(() => {
342 | if (this.webviewView === webviewView) {
343 | this.webviewView = undefined;
344 | }
345 | });
346 |
347 | webviewView.webview.options = {
348 | enableScripts: true,
349 | };
350 |
351 | webviewView.webview.html = this.getHtml();
352 |
353 | webviewView.webview.onDidReceiveMessage((data) =>
354 | this.processMessage(data),
355 | );
356 | }
357 |
358 | getHtml() {
359 | const styleURI = this.webviewView!.webview.asWebviewUri(
360 | vscode.Uri.joinPath(this.ctx.extensionUri, 'out', 'webview.css'),
361 | );
362 | const codiconsUri = this.webviewView!.webview.asWebviewUri(
363 | vscode.Uri.joinPath(this.ctx.extensionUri, 'out', 'codicon.css'),
364 | );
365 | const scriptURI = this.webviewView!.webview.asWebviewUri(
366 | vscode.Uri.joinPath(this.ctx.extensionUri, 'out', 'webview.js'),
367 | );
368 |
369 | return `
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 | Generate Notes
389 |
390 |
391 |
392 | Add Files
393 |
394 |
395 | Pre-release
396 | Make latest
397 |
398 |
399 | Draft
400 |
401 |
402 | Cancel
403 | Publish
404 |
405 |
406 |
407 |
408 | `;
409 | }
410 | }
411 |
--------------------------------------------------------------------------------