Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "git export" tests #179

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 58 additions & 52 deletions src/gitExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
Uri,
window,
} from 'vscode';
import { Octokit } from '@octokit/rest';
import { Repository } from './repository';
import { Distinct } from './openedRepository';
import { mkdir } from 'fs/promises';
import { Octokit } from '@octokit/rest';
import { RequestError } from '@octokit/request-error';
import type { Repository } from './repository';
import type { Distinct } from './openedRepository';

const GITHUB_AUTH_PROVIDER_ID = 'github';
// https://fossil-scm.org/home/doc/trunk/www/mirrortogithub.md says
Expand All @@ -32,38 +32,36 @@ export class Credentials {
disposables.push(
authentication.onDidChangeSessions(async e => {
if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) {
this.octokit = await this.tryCreateOctokit();
await this.tryCreateOctokit();
}
})
);
}
if (!this.octokit) {
this.octokit = await this.tryCreateOctokit();
await this.tryCreateOctokit();
}
}

private async tryCreateOctokit(
/** @internal */ async tryCreateOctokit(
createIfNone: boolean = false
): Promise<Octokit | undefined> {
const session = (this.session = await authentication.getSession(
const session = await authentication.getSession(
GITHUB_AUTH_PROVIDER_ID,
SCOPES,
{ createIfNone }
));

return session
? new Octokit({
auth: session.accessToken,
})
: undefined;
);
if (session) {
this.octokit = new Octokit({
auth: session.accessToken,
});
this.session = session;
return this.octokit;
}
return;
}

async getOctokit(): Promise<Octokit> {
if (this.octokit) {
return this.octokit;
}
this.octokit = await this.tryCreateOctokit(true);
return this.octokit!;
return this.octokit ?? (await this.tryCreateOctokit(true))!;
}
}

Expand Down Expand Up @@ -93,25 +91,20 @@ export async function inputExportOptions(
disposables: Disposable[]
): Promise<GitExportOptions | undefined> {
// ask: exportParentPath (hardest part, no explicit vscode API for this)
let exportParentPath = await window.showSaveDialog({
title:
'Parent path for intermediate git repository ' +
'outside of fossil repository',
saveLabel: 'Select',
filters: {
Directories: [], // trying to select only directories
},
});
const exportParentPath = (
await window.showOpenDialog({
title:
'Parent path for intermediate git repository ' +
'outside of fossil repository',
openLabel: 'Select',
canSelectFiles: false,
canSelectFolders: true,
})
)?.at(0);

if (!exportParentPath) {
return;
}
if (exportParentPath.path.endsWith('.undefined')) {
// somewhat vscode bug
exportParentPath = exportParentPath.with({
path: exportParentPath.path.slice(0, -10),
});
}

// ask: repository name
const config = await repository.config(
Expand All @@ -123,6 +116,8 @@ export async function inputExportOptions(
prompt: 'The name of the new repository',
ignoreFocusOut: true,
value: config.get('short-project-name') || config.get('project-name'),
validateInput: text =>
/^\w+$/.test(text) ? '' : 'Must be a single word',
});
if (!name) {
return;
Expand Down Expand Up @@ -166,6 +161,16 @@ export async function inputExportOptions(
// github option was chosen
// so, we must authenticate
await credentials.initialize(disposables);
const session = credentials.session;
if (!session) {
await window.showErrorMessage(
`No github session available, fossil won't export ${(
credentials as any
).octokit!}`
);
return;
}

const octokit = await credentials.getOctokit();
const userInfo = await octokit.users.getAuthenticated();
const orgs = await octokit.orgs.listForAuthenticatedUser();
Expand Down Expand Up @@ -207,12 +212,12 @@ export async function inputExportOptions(
}

// ask: privacy
const publicItem: QuickPickItem = {
label: '$(globe) Public',
};
const privateItem: QuickPickItem = {
label: '$(lock) Private',
};
const publicItem: QuickPickItem = {
label: '$(globe) Public',
};
const selectedPrivacy = await window.showQuickPick(
[privateItem, publicItem],
{
Expand Down Expand Up @@ -268,7 +273,6 @@ export async function inputExportOptions(
}
}
// add token to url
const session = credentials.session!;
const remoteUri = Uri.parse(response.data.html_url) as AutoPushURISafe;
const remoteUriWithToken = remoteUri.with({
authority: `${session.account.label}:${session.accessToken}@${remoteUri.authority}`,
Expand All @@ -283,10 +287,10 @@ export async function exportGit(
): Promise<void> {
await window.withProgress(
{
title: `Creating $(github) repository ${options.url}`,
title: `Exporting git repository ${options.url}`,
location: ProgressLocation.Notification,
},
async (progress): Promise<any> => {
async (progress): Promise<void> => {
progress.report({
message: `Setting up fossil with ${options.url}`,
increment: 33,
Expand All @@ -295,29 +299,31 @@ export async function exportGit(
name: 'Fossil git export',
cwd: repository.root,
});
const terminalIsClosed = new Promise<void>(ready => {
const dis = window.onDidCloseTerminal(closedTerminal => {
if (closedTerminal === terminal) {
dis.dispose();
ready();
}
});
});
await mkdir(options.path, { recursive: true, mode: 0o700 });
terminal.sendText(
// space at the start to skip history
` fossil git export ${
options.path
} --mainbranch main --autopush ${options.urlUnsafe.toString()}`
);
terminal.show();
progress.report({
message:
'$(terminal) running export (manually close the terminal to finish)',
'Running export (manually close the terminal to finish)',
increment: 66,
});
await new Promise<void>(ready => {
const dis = window.onDidCloseTerminal(closedTerminal => {
if (closedTerminal === terminal) {
progress.report({
message: 'done',
increment: 100,
});
ready();
dis.dispose();
}
});
await terminalIsClosed;
progress.report({
message: 'done',
increment: 100,
});
}
);
Expand Down
5 changes: 5 additions & 0 deletions src/test/suite/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { OpenedRepository, ResourceStatus } from '../../openedRepository';
import { FossilResourceGroup } from '../../resourceGroups';
import { delay } from '../../util';

export type SinonStubT<T extends (...args: any) => any> = sinon.SinonStub<
Parameters<T>,
ReturnType<T>
>;

export async function cleanRoot(): Promise<void> {
/* c8 ignore next 5 */
if (!vscode.workspace.workspaceFolders) {
Expand Down
2 changes: 2 additions & 0 deletions src/test/suite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { PatchSuite, StageSuite, StashSuite, UpdateSuite } from './stateSuite';
import { RenameSuite } from './renameSuite';
import { BranchSuite } from './branchSuite';
import { RevertSuite } from './revertSuite';
import { GetExportSuite as GitExportSuite } from './gitExportSuite';

suite('Fossil.OpenedRepo', function (this: Suite) {
this.ctx.sandbox = sinon.createSandbox();
Expand Down Expand Up @@ -46,6 +47,7 @@ suite('Fossil.OpenedRepo', function (this: Suite) {
suite('FileSystem', FileSystemSuite);
suite('Diff', DiffSuite);
suite('Quality of Life', QualityOfLifeSuite);
suite('Git Export', GitExportSuite);

afterEach(() => {
this.ctx.sandbox.restore();
Expand Down
Loading