Skip to content

Commit 6b60049

Browse files
authored
Merge pull request #39 from heroku/jw/app-error-handling
Added instructions when no apps are found in the git remotes
2 parents 6d6c552 + ccb64d0 commit 6b60049

17 files changed

+548
-119
lines changed

package-lock.json

+62
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+14-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
{
2626
"view": "heroku:auth:login",
2727
"contents": "%heroku:auth:login:contents%"
28+
},
29+
{
30+
"view": "heroku:getting-started",
31+
"contents": "%heroku:getting-started:contents%"
2832
}
2933
],
3034
"viewsContainers": {
@@ -40,13 +44,18 @@
4044
"heroku": [
4145
{
4246
"id": "heroku:auth:login",
43-
"name": "Heroku",
47+
"name": "%view.heroku.name%",
4448
"when": "!heroku.authenticated"
4549
},
4650
{
4751
"id": "heroku:resource-explorer:treeview",
48-
"name": "Heroku Resource Explorer",
49-
"when": "heroku.authenticated"
52+
"name": "%view.heroku.resourceExplorer%",
53+
"when": "heroku.authenticated && heroku.app-found"
54+
},
55+
{
56+
"id": "heroku:getting-started",
57+
"name": "%view.heroku.name%",
58+
"when": "heroku.authenticated && !heroku.app-found"
5059
}
5160
]
5261
},
@@ -187,6 +196,7 @@
187196
"@types/mocha": "^10.0.7",
188197
"@types/mvdan-sh": "^0.10.9",
189198
"@types/node": "18.x",
199+
"@types/proxyquire": "^1.3.31",
190200
"@types/sinon": "^17.0.3",
191201
"@types/vscode": "1.92.0",
192202
"@types/vscode-webview": "1.57.5",
@@ -203,6 +213,7 @@
203213
"lint-staged": "^15.2.0",
204214
"mocha": "10.7.0",
205215
"prettier": "3.3.3",
216+
"proxyquire": "^2.1.3",
206217
"rewire": "^7.0.0",
207218
"sinon": "^18.0.0",
208219
"typescript": "~5.6.2"

package.nls.json

+8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@
33
"message": "Sign in to your Heroku account\n[Sign in](command:heroku:welcome:signin)"
44
},
55

6+
"heroku:getting-started:contents": {
7+
"message": "It looks like this project is not a Heroku app.\n[Get started on Heroku](https://devcenter.heroku.com/start)"
8+
},
9+
610
"view.heroku.name": {
711
"message": "Heroku"
12+
},
13+
14+
"view.heroku.resourceExplorer": {
15+
"message": "Heroku Resource Explorer"
816
}
917
}

resources/app-28.png

650 Bytes
Loading

src/extension/commands/add-on/show-addons-view.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,20 @@ export class ShowAddonsViewCommand extends AbortController implements RunnableCo
5454
extensionUri: vscode.Uri,
5555
addonTreeItem: Bindable<vscode.TreeItem>
5656
): Promise<void> {
57-
this.appIdentifier = appIdentifier;
58-
this.addonTreeItem = addonTreeItem;
57+
if (this.appIdentifier !== appIdentifier) {
58+
ShowAddonsViewCommand.addonsPanel?.dispose();
59+
ShowAddonsViewCommand.addonsPanel = undefined;
60+
}
61+
5962
if (ShowAddonsViewCommand.addonsPanel) {
6063
try {
6164
return ShowAddonsViewCommand.addonsPanel.reveal();
6265
} catch {
6366
// panel is disposed.
6467
}
6568
}
66-
69+
this.appIdentifier = appIdentifier;
70+
this.addonTreeItem = addonTreeItem;
6771
const panel = vscode.window.createWebviewPanel('Addons', 'Elements Marketplace', vscode.ViewColumn.One, {
6872
enableScripts: true
6973
});

src/extension/commands/auth/watch-netrc.ts

+46-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
import { watch, stat, FileChangeInfo } from 'node:fs/promises';
1+
import { watch, stat } from 'node:fs/promises';
22
import os from 'node:os';
33
import path from 'node:path';
4+
import * as vscode from 'vscode';
45
import { HerokuCommand } from '../heroku-command';
5-
import { herokuCommand } from '../../meta/command';
6+
import { herokuCommand, HerokuOutputChannel } from '../../meta/command';
7+
import { createSessionObject } from '../../utils/create-session-object';
8+
import { TokenCommand } from './token';
9+
import { WhoAmI } from './whoami';
610

7-
@herokuCommand()
11+
@herokuCommand({ outputChannelId: HerokuOutputChannel.Authentication })
812
/**
913
* Command that creates a watcher for the .netrc file.
1014
*/
11-
export class WatchNetrc extends HerokuCommand<AsyncIterable<FileChangeInfo<string>>> {
15+
export class WatchNetrc extends HerokuCommand<
16+
AsyncIterable<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>
17+
> {
1218
public static COMMAND_ID = 'heroku:watchnetrc' as const;
1319

1420
/**
@@ -50,10 +56,44 @@ export class WatchNetrc extends HerokuCommand<AsyncIterable<FileChangeInfo<strin
5056
* terminal to sign in or out.
5157
*
5258
* @param signal The abort signal used to stop the watcher.
59+
* @param context The extension context used to store and retrieve secrets.
60+
* @param sessionKey The key used to read and store the session.
5361
* @returns a Promise that resolves to an AsyncIterable which will contain file change info on each await.
5462
*/
55-
public async run(signal: AbortSignal): Promise<AsyncIterable<FileChangeInfo<string>>> {
63+
public async run(
64+
signal: AbortSignal,
65+
context: vscode.ExtensionContext,
66+
sessionKey: string
67+
): Promise<AsyncIterable<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>> {
5668
const file = await WatchNetrc.getNetrcFileLocation();
57-
return watch(file, { signal });
69+
const iterator = watch(file, { signal });
70+
const outputChannel = this.outputChannel as vscode.OutputChannel;
71+
72+
return (async function* (): AsyncGenerator<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent> {
73+
for await (const event of iterator) {
74+
if (event.eventType !== 'change') {
75+
continue;
76+
}
77+
78+
const accessToken = await vscode.commands.executeCommand<string>(TokenCommand.COMMAND_ID);
79+
if (!accessToken) {
80+
const sessionJson = await context.secrets.get(sessionKey);
81+
if (sessionJson) {
82+
await context.secrets.delete(sessionKey);
83+
const session = JSON.parse(sessionJson) as vscode.AuthenticationSession;
84+
await vscode.commands.executeCommand('setContext', 'heroku.authenticated', false);
85+
outputChannel.appendLine(`${session.account.label} signed out of Heroku`);
86+
yield { added: undefined, removed: [session], changed: undefined };
87+
}
88+
} else {
89+
const whoami = await vscode.commands.executeCommand<string>(WhoAmI.COMMAND_ID);
90+
const session = createSessionObject(whoami, accessToken, []);
91+
await context.secrets.store(sessionKey, JSON.stringify(session));
92+
await vscode.commands.executeCommand('setContext', 'heroku.authenticated', true);
93+
outputChannel.appendLine(`Logged in to Heroku as ${whoami}`);
94+
yield { added: [session], removed: undefined, changed: undefined };
95+
}
96+
}
97+
})();
5898
}
5999
}

src/extension/commands/auth/welcome-view-sign-in.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import vscode from 'vscode';
22
import { HerokuCommand } from '../heroku-command';
33
import { herokuCommand, HerokuOutputChannel } from '../../meta/command';
4+
import { LoginCommand } from './login';
45

56
@herokuCommand({
67
outputChannelId: HerokuOutputChannel.Authentication
@@ -23,7 +24,7 @@ export class WelcomeViewSignIn extends HerokuCommand<void> {
2324
*/
2425
public async run(): Promise<void> {
2526
try {
26-
const session = await vscode.authentication.getSession('heroku:auth:login', [], { createIfNone: true });
27+
const session = await vscode.authentication.getSession(LoginCommand.COMMAND_ID, [], { createIfNone: true });
2728
if (session?.accessToken) {
2829
this.outputChannel?.appendLine(`Successfully authenticated as ${session.account.label}`);
2930
}

0 commit comments

Comments
 (0)