Extension: Merge show team relevant repo management buttons (#17)
* Extension: Show team relevant repo management buttons * fix formatting * Fix: Actually delete team repo contents Was passing incomplete path to remove API * Extra logging * Formatting * Fix path on windows * Remove logs --------- Co-authored-by: orosmatthew <orosmatthew@pm.me>
This commit is contained in:
parent
40634d80e6
commit
42a58afbde
@ -68,8 +68,25 @@
|
||||
{
|
||||
"command": "bwcontest.toggleFastPolling",
|
||||
"title": "BWContest Developer: Toggle Fast Polling"
|
||||
},
|
||||
{
|
||||
"command": "bwcontest.refreshState",
|
||||
"title": "Refresh"
|
||||
},
|
||||
{
|
||||
"command": "bwcontest.showTestSubmitPage",
|
||||
"title": "BWContest: Show Test/Submit Page"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "bwcontest.refreshState",
|
||||
"group": "navigation",
|
||||
"when": "view == bwcontest-sidebar"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
|
@ -1,6 +1,15 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { getNonce } from './getNonce';
|
||||
import { cloneAndOpenRepo } from './extension';
|
||||
import {
|
||||
RepoState,
|
||||
clearCachedRepoState,
|
||||
cloneOpenRepo,
|
||||
cloneRepo,
|
||||
openRepo,
|
||||
getCachedRepoState,
|
||||
refreshRepoState,
|
||||
repoStateChanged
|
||||
} from './teamRepoManager';
|
||||
import { BWPanel } from './problemPanel';
|
||||
import urlJoin from 'url-join';
|
||||
import outputPanelLog from './outputPanelLog';
|
||||
@ -22,12 +31,15 @@ import { startTeamStatusPolling, stopTeamStatusPolling } from './contestMonitor/
|
||||
export type WebviewMessageType =
|
||||
| { msg: 'onLogin'; data: TeamData }
|
||||
| { msg: 'onLogout' }
|
||||
| { msg: 'teamStatusUpdated'; data: SidebarTeamStatus | null };
|
||||
| { msg: 'teamStatusUpdated'; data: SidebarTeamStatus | null }
|
||||
| { msg: 'repoStateUpdated'; data: RepoState };
|
||||
|
||||
export type MessageType =
|
||||
| { msg: 'onTestAndSubmit' }
|
||||
| { msg: 'onUIMount' }
|
||||
| { msg: 'onClone'; data: { contestId: number; teamId: number } }
|
||||
| { msg: 'onCloneOpenRepo'; data: { contestId: number; teamId: number } }
|
||||
| { msg: 'onCloneRepo'; data: { contestId: number; teamId: number } }
|
||||
| { msg: 'onOpenRepo'; data: { contestId: number; teamId: number } }
|
||||
| { msg: 'onLogin'; data: { teamName: string; password: string } }
|
||||
| { msg: 'onLogout' };
|
||||
|
||||
@ -75,6 +87,22 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
submissionsChangedEventArgs.changedProblemIds
|
||||
);
|
||||
});
|
||||
|
||||
const currentRepoState = getCachedRepoState();
|
||||
outputPanelLog.info(
|
||||
'When SidebarProvider constructed, cached repo state is: ' + currentRepoState
|
||||
);
|
||||
this.updateRepoStatus(currentRepoState);
|
||||
|
||||
repoStateChanged.add((repoChangedEventArgs) => {
|
||||
outputPanelLog.trace('Repo status updating from event');
|
||||
|
||||
if (!repoChangedEventArgs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateRepoStatus(repoChangedEventArgs.state);
|
||||
});
|
||||
}
|
||||
|
||||
private async handleLogin(
|
||||
@ -129,6 +157,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
'After login, cached submission list is: ' + JSON.stringify(currentSubmissionsList)
|
||||
);
|
||||
this.updateTeamStatus(currentSubmissionsList);
|
||||
|
||||
const currentRepoState = getCachedRepoState();
|
||||
outputPanelLog.info('After login, cached repo state is: ' + currentRepoState);
|
||||
this.updateRepoStatus(currentRepoState);
|
||||
refreshRepoState();
|
||||
}
|
||||
|
||||
private async handleLogout(webviewPostMessage: (m: WebviewMessageType) => void) {
|
||||
@ -189,6 +222,8 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
stopTeamStatusPolling();
|
||||
clearCachedContestTeamState();
|
||||
|
||||
clearCachedRepoState();
|
||||
|
||||
this.context.globalState.update('token', undefined);
|
||||
this.context.globalState.update('teamData', undefined);
|
||||
}
|
||||
@ -236,6 +271,24 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
this.webview.postMessage(message);
|
||||
}
|
||||
|
||||
public updateRepoStatus(state: RepoState) {
|
||||
if (this.webview == null) {
|
||||
outputPanelLog.trace('Not updating sidebar repo state because webview is null');
|
||||
return;
|
||||
}
|
||||
|
||||
const message: WebviewMessageType = {
|
||||
msg: 'repoStateUpdated',
|
||||
data: state
|
||||
};
|
||||
|
||||
outputPanelLog.trace(
|
||||
'Posting repoStateUpdated to webview with message: ' + JSON.stringify(message)
|
||||
);
|
||||
|
||||
this.webview.postMessage(message);
|
||||
}
|
||||
|
||||
public resolveWebviewView(webviewView: vscode.WebviewView) {
|
||||
outputPanelLog.trace('SidebarProvider resolveWebviewView');
|
||||
const webview = webviewView.webview;
|
||||
@ -273,11 +326,23 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
'onUIMount, currentSubmissionsList is ' + JSON.stringify(currentSubmissionsList)
|
||||
);
|
||||
this.updateTeamStatus(currentSubmissionsList);
|
||||
|
||||
const currentRepoState = getCachedRepoState();
|
||||
outputPanelLog.trace('onUIMount, currentRepoState is ' + currentRepoState);
|
||||
this.updateRepoStatus(currentRepoState);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'onClone': {
|
||||
cloneAndOpenRepo(m.data.contestId, m.data.teamId);
|
||||
case 'onCloneOpenRepo': {
|
||||
cloneOpenRepo(m.data.contestId, m.data.teamId);
|
||||
break;
|
||||
}
|
||||
case 'onCloneRepo': {
|
||||
cloneRepo(m.data.contestId, m.data.teamId);
|
||||
break;
|
||||
}
|
||||
case 'onOpenRepo': {
|
||||
openRepo(m.data.contestId, m.data.teamId);
|
||||
break;
|
||||
}
|
||||
case 'onLogin': {
|
||||
|
@ -1,16 +1,17 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { SidebarProvider } from './SidebarProvider';
|
||||
import * as fs from 'fs-extra';
|
||||
import urlJoin from 'url-join';
|
||||
import git from 'isomorphic-git';
|
||||
import path = require('path');
|
||||
import http from 'isomorphic-git/http/node';
|
||||
import outputPanelLog from './outputPanelLog';
|
||||
import {
|
||||
startTeamStatusPollingOnActivation,
|
||||
stopTeamStatusPolling,
|
||||
useFastPolling
|
||||
} from './contestMonitor/pollingService';
|
||||
import {
|
||||
clearCachedRepoState,
|
||||
refreshRepoState,
|
||||
setRepoManagerExtensionContext
|
||||
} from './teamRepoManager';
|
||||
import { BWPanel } from './problemPanel';
|
||||
|
||||
export interface BWContestSettings {
|
||||
repoBaseUrl: string;
|
||||
@ -24,93 +25,6 @@ export function extensionSettings(): BWContestSettings {
|
||||
return vscode.workspace.getConfiguration().get<BWContestSettings>('BWContest')!;
|
||||
}
|
||||
|
||||
function closeAllWorkspaces() {
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolders) {
|
||||
return;
|
||||
}
|
||||
const removedFolders = vscode.workspace.updateWorkspaceFolders(0, workspaceFolders.length);
|
||||
if (!removedFolders) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export async function cloneAndOpenRepo(contestId: number, teamId: number) {
|
||||
const currentSettings = vscode.workspace.getConfiguration().get<BWContestSettings>('BWContest');
|
||||
|
||||
if (!currentSettings || currentSettings.repoBaseUrl == '') {
|
||||
vscode.window.showErrorMessage('BWContest: BWContest.repoBaseURL not set');
|
||||
return;
|
||||
}
|
||||
if (!currentSettings || currentSettings.repoClonePath == '') {
|
||||
vscode.window.showErrorMessage('BWContest: BWContest.repoClonePath not set');
|
||||
return;
|
||||
}
|
||||
if (!currentSettings || currentSettings.webUrl == '') {
|
||||
vscode.window.showErrorMessage('BWContest: BWContest.webUrl not set');
|
||||
return;
|
||||
}
|
||||
|
||||
const repoUrl = urlJoin(
|
||||
currentSettings.repoBaseUrl,
|
||||
contestId.toString(),
|
||||
`${teamId.toString()}.git`
|
||||
);
|
||||
|
||||
const repoName = teamId.toString();
|
||||
|
||||
if (!fs.existsSync(`${currentSettings.repoClonePath}/BWContest`)) {
|
||||
fs.mkdirSync(`${currentSettings.repoClonePath}/BWContest`);
|
||||
}
|
||||
if (!fs.existsSync(`${currentSettings.repoClonePath}/BWContest/${contestId.toString()}`)) {
|
||||
fs.mkdirSync(`${currentSettings.repoClonePath}/BWContest/${contestId.toString()}`);
|
||||
}
|
||||
|
||||
const clonedRepoPath = `${
|
||||
currentSettings.repoClonePath
|
||||
}/BWContest/${contestId.toString()}/${repoName}`;
|
||||
|
||||
if (fs.existsSync(clonedRepoPath)) {
|
||||
const confirm = await vscode.window.showWarningMessage(
|
||||
'The repo already exists. Do you want to replace it?',
|
||||
'Delete and Replace',
|
||||
'Cancel'
|
||||
);
|
||||
if (confirm !== 'Delete and Replace') {
|
||||
return;
|
||||
}
|
||||
closeAllWorkspaces();
|
||||
fs.removeSync(clonedRepoPath);
|
||||
}
|
||||
|
||||
const dir = path.join(currentSettings.repoClonePath, 'BWContest', contestId.toString(), repoName);
|
||||
outputPanelLog.info(`Running 'git clone' to directory: ${dir}`);
|
||||
try {
|
||||
await git.clone({ fs, http, dir, url: repoUrl });
|
||||
} catch (error) {
|
||||
outputPanelLog.error(
|
||||
"Failed to 'git clone'. The git server might be incorrectly configured. Error: " + error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
||||
outputPanelLog.info('Closing workspaces...');
|
||||
closeAllWorkspaces();
|
||||
|
||||
const addedFolder = vscode.workspace.updateWorkspaceFolders(
|
||||
vscode.workspace.workspaceFolders?.length ?? 0,
|
||||
0,
|
||||
{ uri: vscode.Uri.file(clonedRepoPath), name: 'BWContest' }
|
||||
);
|
||||
|
||||
if (!addedFolder) {
|
||||
vscode.window.showErrorMessage('BWContest: Failed to open cloned repo');
|
||||
return;
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage('BWContest: Repo cloned and opened');
|
||||
}
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
outputPanelLog.info('BWContest Extension Activated');
|
||||
|
||||
@ -133,13 +47,27 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
fastPolling = !fastPolling;
|
||||
useFastPolling(fastPolling);
|
||||
}),
|
||||
vscode.commands.registerCommand('bwcontest.showTestSubmitPage', () => {
|
||||
BWPanel.show(context, extensionSettings().webUrl);
|
||||
}),
|
||||
vscode.commands.registerCommand('bwcontest.refreshState', () => {
|
||||
refreshRepoState();
|
||||
})
|
||||
);
|
||||
|
||||
startTeamStatusPollingOnActivation(context);
|
||||
|
||||
setRepoManagerExtensionContext(context);
|
||||
refreshRepoState();
|
||||
|
||||
vscode.workspace.onDidChangeWorkspaceFolders(() => {
|
||||
refreshRepoState();
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
outputPanelLog.info('BWContest Extension Deactivated');
|
||||
stopTeamStatusPolling();
|
||||
clearCachedRepoState();
|
||||
}
|
||||
|
318
extension/bwcontest/src/teamRepoManager.ts
Normal file
318
extension/bwcontest/src/teamRepoManager.ts
Normal file
@ -0,0 +1,318 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs-extra';
|
||||
import urlJoin from 'url-join';
|
||||
import git from 'isomorphic-git';
|
||||
import path = require('path');
|
||||
import http from 'isomorphic-git/http/node';
|
||||
import outputPanelLog from './outputPanelLog';
|
||||
import { BWContestSettings } from './extension';
|
||||
import { LiteEvent } from './utilities/LiteEvent';
|
||||
import { TeamData } from './sharedTypes';
|
||||
import * as os from 'os';
|
||||
|
||||
let latestRepoState: RepoState = 'No Team';
|
||||
|
||||
const onRepoStateChanged = new LiteEvent<RepoChangedEventArgs>();
|
||||
export const repoStateChanged = onRepoStateChanged.expose();
|
||||
|
||||
export type RepoState = 'No Team' | 'No Repo' | 'Repo Exists, Not Open' | 'Repo Open';
|
||||
|
||||
export type RepoChangedEventArgs = {
|
||||
state: RepoState;
|
||||
};
|
||||
|
||||
export function getCachedRepoState(): RepoState {
|
||||
return latestRepoState;
|
||||
}
|
||||
|
||||
export function clearCachedRepoState(): void {
|
||||
outputPanelLog.trace(`Clearing cached repoState`);
|
||||
setRepoState('No Team');
|
||||
}
|
||||
|
||||
export async function refreshRepoState(): Promise<void> {
|
||||
outputPanelLog.trace(`Refreshing repoState`);
|
||||
|
||||
if (!repoManagerExtensionContext) {
|
||||
outputPanelLog.trace(` -> repoState is 'No Team' because something is misconfigured`);
|
||||
return setRepoState('No Team');
|
||||
}
|
||||
|
||||
const teamData = repoManagerExtensionContext.globalState.get<TeamData>('teamData');
|
||||
if (teamData === undefined) {
|
||||
outputPanelLog.trace(` -> repoState is 'No Team' because no globalState for teamData`);
|
||||
return setRepoState('No Team');
|
||||
}
|
||||
|
||||
const repoPaths = getRepoPaths(teamData.contestId, teamData.teamId);
|
||||
if (!repoPaths.success) {
|
||||
outputPanelLog.trace(` -> repoState is 'No Team' can't calculate repo paths`);
|
||||
return setRepoState('No Team');
|
||||
}
|
||||
|
||||
const { clonedRepoPath } = repoPaths;
|
||||
|
||||
outputPanelLog.trace(` -> inspecting local repoPath ${clonedRepoPath}`);
|
||||
if (!fs.existsSync(clonedRepoPath)) {
|
||||
outputPanelLog.trace(` -> repoState is 'No Repo', the local repo path doesn't exist at all`);
|
||||
return setRepoState('No Repo');
|
||||
}
|
||||
|
||||
if (!(await directoryHasGitRepo(clonedRepoPath))) {
|
||||
outputPanelLog.trace(
|
||||
` -> repoState is 'No Repo', the local repo path exists but does not have a git repo`
|
||||
);
|
||||
return setRepoState('No Repo');
|
||||
}
|
||||
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
const existingOpenFolderForRepo = vscode.workspace.workspaceFolders.filter((f) => {
|
||||
const p =
|
||||
os.platform() === 'win32'
|
||||
? path.normalize(f.uri.path.slice(1))
|
||||
: path.normalize(f.uri.path);
|
||||
return p === path.normalize(clonedRepoPath);
|
||||
})[0];
|
||||
if (existingOpenFolderForRepo) {
|
||||
outputPanelLog.trace(
|
||||
` -> repoState is 'Repo Open', we found the repo path in VSCode's workspaceFolders`
|
||||
);
|
||||
return setRepoState('Repo Open');
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceFoldersLogText = vscode.workspace.workspaceFolders
|
||||
? vscode.workspace.workspaceFolders.map((f) => f.uri).join(', ')
|
||||
: '(no workspaceFolders)';
|
||||
outputPanelLog.trace(
|
||||
` -> repoState is 'Repo Exists, Not Open', did not find repoPath (${clonedRepoPath}) in VSCode's workspaceFolders (${workspaceFoldersLogText})`
|
||||
);
|
||||
return setRepoState('Repo Exists, Not Open');
|
||||
}
|
||||
|
||||
function setRepoState(state: RepoState): void {
|
||||
if (state != latestRepoState) {
|
||||
outputPanelLog.trace(`Detected repoState change: ${latestRepoState} -> ${state}`);
|
||||
latestRepoState = state;
|
||||
onRepoStateChanged.trigger({ state });
|
||||
} else {
|
||||
outputPanelLog.trace(`No repoState change, same value: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function cloneOpenRepo(contestId: number, teamId: number): Promise<boolean> {
|
||||
const result = (await cloneRepoWorker(contestId, teamId)) && openRepoWorker(contestId, teamId);
|
||||
refreshRepoState();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function cloneRepo(contestId: number, teamId: number): Promise<boolean> {
|
||||
const result = await cloneRepoWorker(contestId, teamId);
|
||||
refreshRepoState();
|
||||
return result;
|
||||
}
|
||||
|
||||
export function openRepo(contestId: number, teamId: number): boolean {
|
||||
const result = openRepoWorker(contestId, teamId);
|
||||
refreshRepoState();
|
||||
return result;
|
||||
}
|
||||
|
||||
async function cloneRepoWorker(contestId: number, teamId: number): Promise<boolean> {
|
||||
const repoPaths = getRepoPaths(contestId, teamId);
|
||||
if (!repoPaths.success) {
|
||||
vscode.window.showErrorMessage('BWContest: BWContestSettings not configured');
|
||||
return false;
|
||||
}
|
||||
|
||||
const { repoUrl, clonedRepoPath } = repoPaths;
|
||||
|
||||
outputPanelLog.trace(`Trying to cloneRepo`);
|
||||
outputPanelLog.trace(` URL: ${repoUrl}`);
|
||||
outputPanelLog.trace(` Local Directory: ${clonedRepoPath}`);
|
||||
|
||||
if (!fs.existsSync(clonedRepoPath)) {
|
||||
outputPanelLog.trace('Local Directory does not exist, creating it');
|
||||
try {
|
||||
fs.mkdirSync(clonedRepoPath, { recursive: true });
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
`BWContest: Could not create directory '${clonedRepoPath}': ${error}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
outputPanelLog.trace('Local Directory created, starting clone');
|
||||
return await doClone(clonedRepoPath, repoUrl);
|
||||
}
|
||||
|
||||
if (fs.readdirSync(clonedRepoPath).length == 0) {
|
||||
outputPanelLog.trace('Local Directory exists but is empty, starting clone');
|
||||
return await doClone(clonedRepoPath, repoUrl);
|
||||
}
|
||||
|
||||
outputPanelLog.trace('Local Directory exists and is non-empty');
|
||||
|
||||
const gitHasRemote = await directoryHasGitRepo(clonedRepoPath);
|
||||
|
||||
const deleteAndReplacePrompt = gitHasRemote
|
||||
? 'The repository already exists, replacing it will delete local changes. Are you sure?'
|
||||
: 'The repository directory exists with no git repo, deleting it will delete local changes. Are you sure?';
|
||||
|
||||
const confirm = await vscode.window.showWarningMessage(
|
||||
deleteAndReplacePrompt,
|
||||
'Delete and Replace',
|
||||
'Cancel'
|
||||
);
|
||||
|
||||
if (confirm !== 'Delete and Replace') {
|
||||
return false;
|
||||
}
|
||||
|
||||
outputPanelLog.trace(`Team has chosen to delete non-empty Local Directory`);
|
||||
|
||||
try {
|
||||
const existingItemsInDir = fs.readdirSync(clonedRepoPath, {
|
||||
recursive: false,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
outputPanelLog.trace(` Removing ${existingItemsInDir.length} items`);
|
||||
for (const existingItemInDir of existingItemsInDir) {
|
||||
const fullPath = path.join(clonedRepoPath, existingItemInDir);
|
||||
outputPanelLog.trace(` Removing ${fullPath}`);
|
||||
fs.rmSync(fullPath, { recursive: true, force: true });
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
`BWContest: Failed to delete contents of Local Directory '${clonedRepoPath}': ${error}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const itemsInDirAfterDelete = fs.readdirSync(clonedRepoPath, {
|
||||
recursive: false,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
outputPanelLog.trace(
|
||||
`Local Directory should now be empty, there are ${itemsInDirAfterDelete.length} item(s): ${itemsInDirAfterDelete.join(', ')}`
|
||||
);
|
||||
if (itemsInDirAfterDelete.length > 0) {
|
||||
vscode.window.showErrorMessage(`BWContest: Failed to delete contents of Local Directory`);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
`BWContest: Failed to delete contents of Local Directory '${clonedRepoPath}': ${error}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
outputPanelLog.trace(`Local Directory is now empty, starting clone`);
|
||||
return await doClone(clonedRepoPath, repoUrl);
|
||||
}
|
||||
|
||||
async function directoryHasGitRepo(path: string): Promise<boolean> {
|
||||
try {
|
||||
await git.listRemotes({ fs, dir: path });
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function doClone(targetDirectory: string, repoUrl: string): Promise<boolean> {
|
||||
outputPanelLog.trace(`Running 'git clone' from url ${repoUrl} to directory: ${targetDirectory}`);
|
||||
try {
|
||||
await git.clone({ fs, http, dir: targetDirectory, url: repoUrl });
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`BWContest: Failed to git clone: ${error}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage('BWContest: Repo cloned!');
|
||||
return true;
|
||||
}
|
||||
|
||||
function openRepoWorker(contestId: number, teamId: number): boolean {
|
||||
const repoPaths = getRepoPaths(contestId, teamId);
|
||||
if (!repoPaths.success) {
|
||||
vscode.window.showErrorMessage('BWContest: BWContestSettings not configured');
|
||||
return false;
|
||||
}
|
||||
|
||||
const { clonedRepoPath } = repoPaths;
|
||||
|
||||
if (!fs.existsSync(clonedRepoPath)) {
|
||||
vscode.window.showErrorMessage('BWContest: Local repo not found, clone first.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
const existingOpenFolderForRepo = vscode.workspace.workspaceFolders.filter((f) => {
|
||||
const p =
|
||||
os.platform() === 'win32'
|
||||
? path.normalize(f.uri.path.slice(1))
|
||||
: path.normalize(f.uri.path);
|
||||
return p === path.normalize(clonedRepoPath);
|
||||
})[0];
|
||||
if (existingOpenFolderForRepo) {
|
||||
vscode.window.showInformationMessage('BWContest: Repo is already opened');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
outputPanelLog.trace(`Opening local repo from '${clonedRepoPath}'`);
|
||||
const workspaceUpdateValid = vscode.workspace.updateWorkspaceFolders(
|
||||
0,
|
||||
vscode.workspace.workspaceFolders?.length ?? 0,
|
||||
{ uri: vscode.Uri.file(clonedRepoPath), name: 'BWContest' }
|
||||
);
|
||||
|
||||
if (!workspaceUpdateValid) {
|
||||
vscode.window.showErrorMessage('BWContest: Request to open repository in VSCode failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shouldn't happen, or if it does the IDE will restart shortly after
|
||||
vscode.window.showInformationMessage('BWContest: Team repository opened!');
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getRepoPaths(
|
||||
contestId: number,
|
||||
teamId: number
|
||||
): { success: false } | { success: true; repoUrl: string; clonedRepoPath: string } {
|
||||
const currentSettings = vscode.workspace.getConfiguration().get<BWContestSettings>('BWContest');
|
||||
|
||||
if (!currentSettings || currentSettings.repoBaseUrl == '') {
|
||||
// vscode.window.showErrorMessage('BWContest: BWContest.repoBaseURL not set');
|
||||
return { success: false };
|
||||
}
|
||||
if (!currentSettings || currentSettings.repoClonePath == '') {
|
||||
// vscode.window.showErrorMessage('BWContest: BWContest.repoClonePath not set');
|
||||
return { success: false };
|
||||
}
|
||||
if (!currentSettings || currentSettings.webUrl == '') {
|
||||
// vscode.window.showErrorMessage('BWContest: BWContest.webUrl not set');
|
||||
return { success: false };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
repoUrl: urlJoin(currentSettings.repoBaseUrl, contestId.toString(), `${teamId.toString()}.git`),
|
||||
clonedRepoPath: path.join(
|
||||
currentSettings.repoClonePath,
|
||||
'BWContest',
|
||||
contestId.toString(),
|
||||
teamId.toString()
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let repoManagerExtensionContext: vscode.ExtensionContext | null;
|
||||
export function setRepoManagerExtensionContext(context: vscode.ExtensionContext) {
|
||||
repoManagerExtensionContext = context;
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
MessageType,
|
||||
SidebarTeamStatus
|
||||
} from '../../src/SidebarProvider';
|
||||
import type { RepoState } from '../../src/teamRepoManager';
|
||||
|
||||
let teamname: string;
|
||||
let password: string;
|
||||
@ -16,16 +17,36 @@
|
||||
let teamData: TeamData | null = null;
|
||||
let teamStatus: SidebarTeamStatus | null = null;
|
||||
|
||||
let repoState: RepoState | null = null;
|
||||
|
||||
let totalProblems = 0;
|
||||
|
||||
function postMessage(message: MessageType) {
|
||||
vscode.postMessage(message);
|
||||
}
|
||||
|
||||
function onClone() {
|
||||
function onCloneOpenRepo() {
|
||||
if (teamData) {
|
||||
postMessage({
|
||||
msg: 'onClone',
|
||||
msg: 'onCloneOpenRepo',
|
||||
data: { contestId: teamData.contestId, teamId: teamData.teamId }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onCloneRepo() {
|
||||
if (teamData) {
|
||||
postMessage({
|
||||
msg: 'onCloneRepo',
|
||||
data: { contestId: teamData.contestId, teamId: teamData.teamId }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onOpenRepo() {
|
||||
if (teamData) {
|
||||
postMessage({
|
||||
msg: 'onOpenRepo',
|
||||
data: { contestId: teamData.contestId, teamId: teamData.teamId }
|
||||
});
|
||||
}
|
||||
@ -71,6 +92,8 @@
|
||||
teamStatus.incorrectProblems.length +
|
||||
teamStatus.notStartedProblems.length
|
||||
: 0;
|
||||
} else if (m.msg === 'repoStateUpdated') {
|
||||
repoState = m.data;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -111,10 +134,24 @@
|
||||
|
||||
<h2 class="sidebarSectionHeader">Actions</h2>
|
||||
<div class="sidebarSection">
|
||||
{#if repoState == 'No Team'}
|
||||
<span>Team not connected, click Refresh at the top of this panel</span>
|
||||
{:else if repoState == 'No Repo'}
|
||||
<div class="buttonContainer">
|
||||
<button on:click={onClone} class="sidebarButton">Clone and Open Repo</button>
|
||||
<button on:click={onTestAndSubmit} class="sidebarButton">Test & Submit</button>
|
||||
<button on:click={onCloneOpenRepo} class="sidebarButton">Clone and Open Repo</button>
|
||||
</div>
|
||||
{:else if repoState == 'Repo Exists, Not Open'}
|
||||
<div class="buttonContainer">
|
||||
<button on:click={onOpenRepo} class="sidebarButton">Open Repo</button>
|
||||
</div>
|
||||
{:else if repoState == 'Repo Open'}
|
||||
<div class="buttonContainer">
|
||||
<button on:click={onTestAndSubmit} class="sidebarButton">Test & Submit</button>
|
||||
<button on:click={onCloneRepo} class="sidebarButton">Reset Repo</button>
|
||||
</div>
|
||||
{:else}
|
||||
<span>Checking repo state...</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<h2 class="sidebarSectionHeader">Problem Progress</h2>
|
||||
|
Loading…
Reference in New Issue
Block a user