Skip to content

Commit

Permalink
feat: add recent cipe view & notifications (#2322)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKless authored Nov 29, 2024
1 parent 438659d commit fef23e3
Show file tree
Hide file tree
Showing 31 changed files with 1,935 additions and 127 deletions.
11 changes: 11 additions & 0 deletions apps/nxls/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
NxProjectFolderTreeRequest,
NxProjectGraphOutputRequest,
NxProjectsByPathsRequest,
NxRecentCIPEDataRequest,
NxSourceMapFilesToProjectsMapRequest,
NxStartupMessageRequest,
NxStopDaemonRequest,
Expand Down Expand Up @@ -65,6 +66,7 @@ import {
getProjectFolderTree,
getProjectGraphOutput,
getProjectsByPaths,
getRecentCIPEData,
getSourceMapFilesToProjectsMap,
getStartupMessage,
getTargetsForConfigFile,
Expand Down Expand Up @@ -627,6 +629,15 @@ connection.onRequest(NxPDVDataRequest, async (args: { filePath: string }) => {
return getPDVData(WORKING_PATH, args.filePath);
});

connection.onRequest(NxRecentCIPEDataRequest, async () => {
keepAlive();
if (!WORKING_PATH) {
return new ResponseError(1000, 'Unable to get Nx info: no workspace path');
}

return getRecentCIPEData(WORKING_PATH);
});

connection.onNotification(NxWorkspaceRefreshNotification, async () => {
keepAlive();
if (!WORKING_PATH) {
Expand Down
102 changes: 99 additions & 3 deletions apps/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@
},
{
"command": "nxCloud.refresh",
"when": "view == nxCloudOnboarding",
"when": "view == nxCloudOnboarding || view == nxCloudLoading || view == nxCloudRecentCIPE",
"group": "navigation"
},
{
"command": "nxCloud.openApp",
"when": "view == nxCloudRecentCIPE",
"group": "navigation"
}
],
Expand Down Expand Up @@ -202,6 +207,21 @@
"command": "nx.generate.quickpick",
"when": "view == nxCommands && viewItem == generate && isNxWorkspace",
"group": "inline@1"
},
{
"command": "nxCloud.showCIPEInApp",
"when": "view == nxCloudRecentCIPE && viewItem =~ /^cipe/",
"group": "inline@1"
},
{
"command": "nxCloud.showCommitForCIPE",
"when": "view == nxCloudRecentCIPE && viewItem == cipe-commit",
"group": "inline"
},
{
"command": "nxCloud.showRunInApp",
"when": "view == nxCloudRecentCIPE && viewItem == run",
"group": "inline@1"
}
],
"editor/title": [
Expand Down Expand Up @@ -293,6 +313,18 @@
"command": "nx.remove.projectView",
"when": "false"
},
{
"command": "nxCloud.showCIPEInApp",
"when": "false"
},
{
"command": "nxCloud.showCommitForCIPE",
"when": "false"
},
{
"command": "nxCloud.showRunInApp",
"when": "false"
},
{
"command": "nx.run-many",
"when": "isNxWorkspace"
Expand Down Expand Up @@ -610,6 +642,26 @@
"title": "Refresh Nx Cloud View",
"command": "nxCloud.refresh",
"icon": "$(refresh)"
},
{
"title": "Show CI Pipeline Execution in Nx Cloud",
"command": "nxCloud.showCIPEInApp",
"icon": "$(link-external)"
},
{
"title": "Show Git Commit for this CI Pipeline Execution",
"command": "nxCloud.showCommitForCIPE",
"icon": "$(git-commit)"
},
{
"title": "Show Run in Nx Cloud",
"command": "nxCloud.showRunInApp",
"icon": "$(link-external)"
},
{
"title": "Open Nx Cloud App",
"command": "nxCloud.openApp",
"icon": "$(cloud)"
}
],
"configuration": {
Expand Down Expand Up @@ -711,6 +763,21 @@
"nxConsole.nxWorkspacePath": {
"type": "string",
"description": "Specifies the relative path to the root directory of the Nx workspace. Can be configured on a user or workspace level."
},
"nxConsole.nxCloudNotifications": {
"type": "string",
"enum": [
"all",
"errors",
"none"
],
"enumDescriptions": [
"Show all Nx Cloud notifications",
"Show only error Nx Cloud notifications",
"Show no Nx Cloud notifications"
],
"default": "all",
"description": "Controls which notifications are shown for Nx Cloud."
}
}
},
Expand Down Expand Up @@ -791,6 +858,25 @@
"view": "nxProjects",
"contents": "Nx caught one or more errors while computing your project graph. \n [View Errors](command:workbench.actions.view.problems)\n If the problems persist, you can try running `nx reset` to clear all caches and then [refresh the workspace](command:nxConsole.refreshWorkspace)\n For more information, view the [Nx Language Server logs](command:nxConsole.showNxlsLogs) and refer to the [Nx Troubleshooting Guide](https://nx.dev/troubleshooting/troubleshoot-nx-install-issues?utm_source=nxconsole) and the [Nx Console Troubleshooting Guide](https://nx.dev/recipes/nx-console/console-troubleshooting?utm_source=nxconsole).",
"when": "nxConsole.hasWorkspaceErrors"
},
{
"view": "nxCloudRecentCIPE",
"contents": "No CI Pipeline Executions from local branches have run in the past hour. You can view all CI Pipeline Executions in the Nx Cloud application. \n [Open Nx Cloud App](command:nxCloud.openApp)",
"when": "nxCloudView.error == false"
},
{
"view": "nxCloudRecentCIPE",
"contents": "You don't have access to this Nx Cloud workspace. \n [Login to Nx Cloud](command:nxCloud.login)",
"when": "nxCloudView.error == authentication"
},
{
"view": "nxCloudRecentCIPE",
"contents": "An error occured while accessing the Nx Cloud API. \n [View Error Output](command:nxCloud.viewRecentError)",
"when": "nxCloudView.error == other"
},
{
"view": "nxCloudLoading",
"contents": "Loading..."
}
],
"walkthroughs": [
Expand Down Expand Up @@ -866,10 +952,20 @@
"when": "isNxWorkspace"
},
{
"type": "webview",
"id": "nxCloudLoading",
"name": "Nx Cloud",
"when": "isNxWorkspace && nxCloudView.visible.loading"
},
{
"id": "nxCloudOnboarding",
"type": "webview",
"name": "Nx Cloud :: CI Optimizations",
"when": "isNxWorkspace"
"when": "isNxWorkspace && nxCloudView.visible.onboarding"
},
{
"id": "nxCloudRecentCIPE",
"name": "Nx Cloud :: Recent CI Pipeline Executions",
"when": "isNxWorkspace && nxCloudView.visible.recent-cipe"
},
{
"id": "nxCommands",
Expand Down
9 changes: 9 additions & 0 deletions libs/language-server/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
TaskExecutionSchema,
} from '@nx-console/shared/schema';
import {
CIPEInfo,
CIPEInfoError,
CloudOnboardingInfo,
NxWorkspace,
PDVData,
Expand Down Expand Up @@ -178,6 +180,7 @@ export const NxCloudStatusRequest: RequestType<
{
isConnected: boolean;
nxCloudUrl?: string;
nxCloudId?: string;
},
unknown
> = new RequestType('nx/cloudStatus');
Expand All @@ -193,3 +196,9 @@ export const NxPDVDataRequest: RequestType<
PDVData,
unknown
> = new RequestType('nx/pdvData');

export const NxRecentCIPEDataRequest: RequestType<
undefined,
{ info?: CIPEInfo[]; error?: CIPEInfoError; workspaceUrl?: string },
unknown
> = new RequestType('nx/recentCIPEData');
1 change: 1 addition & 0 deletions libs/language-server/workspace/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export * from './lib/nx-stop-daemon';
export * from './lib/get-nx-cloud-status';
export * from './lib/get-cloud-onboarding-info';
export * from './lib/get-pdv-data';
export * from './lib/get-recent-cipe-data';
export { getNxDaemonClient } from './lib/get-nx-workspace-package';
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'fs';
import * as os from 'node:os';
import { join } from 'path';

import { lspLogger } from '@nx-console/language-server/utils';
import {
getNxAccessToken,
getNxCloudId,
Expand All @@ -11,7 +12,6 @@ import {
import { CloudOnboardingInfo } from '@nx-console/shared/types';
import { parse } from 'ini';
import { xhr } from 'request-light';
import { lspLogger } from '@nx-console/language-server/utils';

export async function getCloudOnboardingInfo(
workspacePath: string
Expand Down Expand Up @@ -91,7 +91,7 @@ function getCommonCIFileContents(workspacePath: string): string[] {
return fileContents;
}

function getNxCloudConfigIni(): any | undefined {
export function getNxCloudConfigIni(): any | undefined {
const iniLocation = findExistingNxCloudConfigFile();

if (iniLocation && existsSync(iniLocation)) {
Expand Down Expand Up @@ -195,6 +195,7 @@ async function getNxCloudWorkspaceClaimed(
});
return JSON.parse(response.responseText);
} catch (e) {
e;
lspLogger.log(
`Error from ${nxCloudUrl}/nx-cloud/is-workspace-claimed: ${e.responseText}`
);
Expand Down
9 changes: 4 additions & 5 deletions libs/language-server/workspace/src/lib/get-nx-cloud-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ import {

export async function getNxCloudStatus(
workspaceRoot: string
): Promise<{ isConnected: boolean; nxCloudUrl: string }> {
): Promise<{ isConnected: boolean; nxCloudUrl: string; nxCloudId?: string }> {
const nxJsonPath = join(workspaceRoot, 'nx.json');
if (!existsSync(nxJsonPath)) {
return { isConnected: false, nxCloudUrl: defaultCloudUrl };
}
if (
(await getNxAccessToken(workspaceRoot)) ||
(await getNxCloudId(workspaceRoot))
) {
const nxCloudId = await getNxCloudId(workspaceRoot);
if ((await getNxAccessToken(workspaceRoot)) || nxCloudId) {
return {
isConnected: true,
nxCloudUrl: await getNxCloudUrl(workspaceRoot),
nxCloudId,
};
}

Expand Down
118 changes: 118 additions & 0 deletions libs/language-server/workspace/src/lib/get-recent-cipe-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { lspLogger } from '@nx-console/language-server/utils';
import {
getNxAccessToken,
getNxCloudId,
getNxCloudUrl,
} from '@nx-console/shared/npm';
import { CIPEInfo, CIPEInfoError } from '@nx-console/shared/types';
import { execSync } from 'child_process';
import { xhr } from 'request-light';
import { getNxCloudConfigIni } from './get-cloud-onboarding-info';

export async function getRecentCIPEData(workspacePath: string): Promise<{
info?: CIPEInfo[];
error?: CIPEInfoError;
workspaceUrl?: string;
}> {
const branches = getRecentlyCommittedGitBranches(workspacePath);

const nxCloudUrl = await getNxCloudUrl(workspacePath);
const nxCloudId = await getNxCloudId(workspacePath);
const nxCloudConfigIni = getNxCloudConfigIni();

const personalAccessToken =
nxCloudConfigIni?.[nxCloudUrl]?.personalAccessToken;

const data = JSON.stringify({
branches: branches.map((branch) => branch.name),
});

const url = `${nxCloudUrl}/nx-cloud/nx-console/ci-pipeline-executions`;
const headers: any = {
'Content-Type': 'application/json',
};

if (nxCloudId) {
headers['Nx-Cloud-Id'] = nxCloudId;
}
if (personalAccessToken) {
headers['Nx-Cloud-Personal-Access-Token'] = personalAccessToken;
}

const accessToken = await getNxAccessToken(workspacePath);
if (accessToken) {
headers['Authorization'] = accessToken;
}

try {
const response = await xhr({
type: 'POST',
url,
headers,
data,
timeout: 5000,
});
const responseData = JSON.parse(response.responseText) as {
ciPipelineExecutions: CIPEInfo[];
workspaceUrl: string;
};
return {
info: responseData.ciPipelineExecutions,
workspaceUrl: responseData.workspaceUrl,
};
} catch (e) {
if (e.status === 401) {
lspLogger.log(`Authentication error: ${e.responseText}`);
return {
error: {
type: 'authentication',
message: e.responseText,
},
};
}
lspLogger.log(`Error: ${JSON.stringify(e)}`);
return {
error: {
type: 'other',
message: e.responseText ?? e.message,
},
};
}
}

function getRecentlyCommittedGitBranches(
workspacePath: string
): { name: string; time: string }[] {
try {
const localUserEmail = execSync('git config user.email').toString().trim();
const oneHourAgo = new Date(Date.now() - 60 * 60 * 24 * 1000).toISOString();

const res = execSync(
'git for-each-ref --count=10 --sort=-committerdate refs/heads/ --format="%(refname) - %(committerdate:iso-strict) - %(authoremail)"',
{
cwd: workspacePath,
}
).toString();

const branches = res
.split('\n')
.filter((line) => line.trim() !== '')
.map((line) => {
const [refname, time, email] = line
.split(' - ')
.map((item) => item.trim());
return {
name: refname.replace('refs/heads/', ''),
time,
email: email,
};
})
.filter((item) => {
return item.email.includes(localUserEmail) && item.time >= oneHourAgo;
});

return branches;
} catch (e) {
return [];
}
}
2 changes: 1 addition & 1 deletion libs/shared/types/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './lib/nx-workspace';
export * from './lib/project-folder-tree';
export * from './lib/cloud-onboarding-info';
export * from './lib/cloud-info';
export * from './lib/pdv-data';
Loading

0 comments on commit fef23e3

Please sign in to comment.