/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { workspace } from 'vscode'; import { RemoteSourceProvider, RemoteSource } from './typings/git-base'; import { getOctokit } from './auth'; import { Octokit } from '@octokit/rest'; function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined { const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(url) || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(url); return match ? { owner: match[1], repo: match[2] } : undefined; } function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined { const match = /^([^/]+)\/([^/]+)$/i.exec(query); return match ? { owner: match[1], repo: match[2] } : undefined; } function asRemoteSource(raw: any): RemoteSource { const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); return { name: `$(github) ${raw.full_name}`, description: `${raw.stargazers_count > 0 ? `$(star-full) ${raw.stargazers_count}` : '' }`, detail: raw.description || undefined, url: protocol === 'https' ? raw.clone_url : raw.ssh_url }; } export class GithubRemoteSourceProvider implements RemoteSourceProvider { readonly name = 'GitHub'; readonly icon = 'github'; readonly supportsQuery = true; private userReposCache: RemoteSource[] = []; async getRemoteSources(query?: string): Promise { const octokit = await getOctokit(); if (query) { const repository = getRepositoryFromUrl(query); if (repository) { const raw = await octokit.repos.get(repository); return [asRemoteSource(raw.data)]; } } const all = await Promise.all([ this.getQueryRemoteSources(octokit, query), this.getUserRemoteSources(octokit, query), ]); const map = new Map(); for (const group of all) { for (const remoteSource of group) { map.set(remoteSource.name, remoteSource); } } return [...map.values()]; } private async getUserRemoteSources(octokit: Octokit, query?: string): Promise { if (!query) { const user = await octokit.users.getAuthenticated({}); const username = user.data.login; const res = await octokit.repos.listForAuthenticatedUser({ username, sort: 'updated', per_page: 100 }); this.userReposCache = res.data.map(asRemoteSource); } return this.userReposCache; } private async getQueryRemoteSources(octokit: Octokit, query?: string): Promise { if (!query) { return []; } const repository = getRepositoryFromQuery(query); if (repository) { query = `user:${repository.owner}+${repository.repo}`; } query += ` fork:true`; const raw = await octokit.search.repos({ q: query, sort: 'stars' }); return raw.data.items.map(asRemoteSource); } async getBranches(url: string): Promise { const repository = getRepositoryFromUrl(url); if (!repository) { return []; } const octokit = await getOctokit(); const branches: string[] = []; let page = 1; while (true) { let res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); if (res.data.length === 0) { break; } branches.push(...res.data.map(b => b.name)); page++; } const repo = await octokit.repos.get(repository); const defaultBranch = repo.data.default_branch; return branches.sort((a, b) => a === defaultBranch ? -1 : b === defaultBranch ? 1 : 0); } }