Skip to content

Commit

Permalink
Update package.json with new name, description, and dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
austenstone committed Feb 7, 2024
1 parent c625502 commit b1321d7
Show file tree
Hide file tree
Showing 8 changed files with 1,048 additions and 175 deletions.
880 changes: 714 additions & 166 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 19 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{
"name": "markdown-interpolation",
"version": "1.5.0",
"description": "Add an issue or pull request to a project",
"main": "lib/markdown.js",
"types": "lib/markdown.d.ts",
"name": "bitbucket-github-migrator",
"version": "1.1.0",
"description": "A tool to migrate repositories from Bitbucket to GitHub",
"bin": {
"bitbucket-github-migrator": "dist/index.js"
},
"type": "commonjs",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "jest",
Expand All @@ -19,7 +23,7 @@
"readme": "README.md",
"repository": {
"type": "git",
"url": "https://github.com/austenstone/markdown-interpolation.git"
"url": "https://github.com/austenstone/bitbucket-github-migrator.git"
},
"license": "MIT",
"devDependencies": {
Expand All @@ -34,5 +38,13 @@
"ts-jest": "^29.1.2",
"ts-node-dev": "^2.0.0",
"typescript": "^5.3.3"
}
},
"dependencies": {
"@octokit/rest": "^20.0.2",
"inquirer": "^8.0.0"
},
"engines": {
"node": ">=18"
},
"support": true
}
147 changes: 147 additions & 0 deletions src/bitbucket.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
type Link = {
href: string;
name: string;
};

type Rendered = {
raw: string;
markup: string;
html: string;
};

type Author = {
type: string;
};

type Reviewer = {
type: string;
};

type Participant = {
type: string;
};

type PullRequest = {
type: string;
links: {
self: Link;
html: Link;
commits: Link;
approve: Link;
diff: Link;
diffstat: Link;
comments: Link;
activity: Link;
merge: Link;
decline: Link;
};
id: number;
title: string;
rendered: {
title: Rendered;
description: Rendered;
reason: Rendered;
};
summary: {
raw: string;
markup: string;
html: string;
};
state: string;
author: Author;
source: {
branch: {
name: string;
};
repository: Repository;
};
destination: {
branch: {
name: string;
};
repository: Repository;
};
merge_commit: {
hash: string;
};
comment_count: number;
task_count: number;
close_source_branch: boolean;
closed_by: {
type: string;
};
reason: string;
created_on: string;
updated_on: string;
reviewers: Reviewer[];
participants: Participant[];
};

type PullRequestResponse = {
size: number;
page: number;
pagelen: number;
next: string;
previous: string;
values: PullRequest[];
};

type Clone = {
href: string;
name: string;
};

type Owner = {
type: string;
};

type Project = {
type: string;
};

type MainBranch = {
type: string;
};

type Repository = {
type: string;
links: {
self: Link;
html: Link;
avatar: Link;
pullrequests: Link;
commits: Link;
forks: Link;
watchers: Link;
downloads: Link;
clone: Clone[];
hooks: Link;
};
uuid: string;
full_name: string;
is_private: boolean;
scm: string;
owner: Owner;
name: string;
description: string;
created_on: string;
updated_on: string;
size: number;
language: string;
has_issues: boolean;
has_wiki: boolean;
fork_policy: string;
project: Project;
mainbranch: MainBranch;
};

type RepositoryResponse = {
size: number;
page: number;
pagelen: number;
next: string;
previous: string;
values: Repository[];
};

export { PullRequest, PullRequestResponse, Repository, RepositoryResponse }
48 changes: 48 additions & 0 deletions src/bitbucket.github.migrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Migrate } from "./migrate";
import inquirer from 'inquirer';

const questions = [] as {
type: string;
name: string;
message: string;
}[];

if (!process.env.GITHUB_TOKEN) {
questions.push({
type: 'input',
name: 'githubToken',
message: 'What is your GitHub token?'
});
}
if (!process.env.GITHUB_ORG) {
questions.push({
type: 'input',
name: 'githubOwner',
message: 'What is the destination GitHub org?'
});
}

if (!process.env.BITBUCKET_TOKEN) {
questions.push({
type: 'input',
name: 'bitbucketToken',
message: 'What is your Bitbucket token?'
});
}
if (!process.env.BITBUCKET_WORKSPACE) {
questions.push({
type: 'input',
name: 'bitbucketWorkspace',
message: 'What is the source Bitbucket workspace?'
});
}

inquirer.prompt(questions).then(answers => {
const migrate = new Migrate({
bitbucketToken: process.env.BITBUCKET_TOKEN || answers.bitbucketToken,
bitbucketWorkspace: process.env.BITBUCKET_WORKSPACE || answers.bitbucketWorkspace,
githubToken: process.env.GITHUB_TOKEN || answers.githubToken,
githubOwner: process.env.GITHUB_ORG || answers.githubOwner
});
migrate.run();
});
53 changes: 53 additions & 0 deletions src/bitbucket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { PullRequest, PullRequestResponse, Repository, RepositoryResponse } from './bitbucket.d';

export class BitBucket {
init = {
headers: {
'Authorization': 'Bearer <access_token>',
'Accept': 'application/json'
}
};
repos: Repository[] = [];
prs: {
[repo: string]: PullRequest[]
} = {};

constructor(token: string) {
this.init.headers.Authorization = `Bearer ${token}`
}

async listAllPullRequests(workspace: string) {
if (this.repos.length === 0) {
this.repos = await this.listRepositories(workspace);
}
for (const repo of this.repos) {
this.prs[repo.name] = await this.listPullRequests(workspace, repo.name);
}
return this.prs;
}

async listPullRequests(workspace: string, repo_slug: string) {
if (this.prs && this.prs[repo_slug]) return this.prs[repo_slug];
let response: PullRequestResponse;
let prs: PullRequest[] = [];

Check failure on line 32 in src/bitbucket.ts

View workflow job for this annotation

GitHub Actions / CI 🔨🧪🧹 (20)

'prs' is never reassigned. Use 'const' instead
do {
response = await fetch(`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo_slug}/pullrequests`, this.init)
.then(res => res.json() as Promise<PullRequestResponse>);
prs.push(...response.values);
} while (response.next)
return prs;
}

async listRepositories(workspace: string) {
if (this.repos) return this.repos;
let response: RepositoryResponse;
let repos: Repository[] = [];

Check failure on line 44 in src/bitbucket.ts

View workflow job for this annotation

GitHub Actions / CI 🔨🧪🧹 (20)

'repos' is never reassigned. Use 'const' instead
do {
response = await fetch(`https://api.bitbucket.org/2.0/repositories/${workspace}`, this.init)
.then(res => res.json() as Promise<RepositoryResponse>);
repos.push(...response.values);
} while (response.next)
this.repos = repos;
return repos;
}
}
16 changes: 16 additions & 0 deletions src/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Octokit } from '@octokit/rest';
import { Endpoints } from '@octokit/types';

export class GitHub {
octokit: Octokit;

constructor(token: string) {
this.octokit = new Octokit({
auth: token,
});
}

async createPullRequest(request: Endpoints["POST /repos/{owner}/{repo}/pulls"]["parameters"]) {
return this.octokit.pulls.create(request);
}
}
2 changes: 0 additions & 2 deletions src/index.ts

This file was deleted.

51 changes: 51 additions & 0 deletions src/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BitBucket } from "./bitbucket";
import { GitHub } from "./github";

export interface MigrateOptions {
bitbucketWorkspace?: string;
githubOwner?: string;
bitbucketToken: string;
githubToken: string;
}

export class Migrate {
bitbucket: BitBucket;
github: GitHub;
options: MigrateOptions;

constructor(options: MigrateOptions) {
this.options = options;
this.bitbucket = new BitBucket(this.options.bitbucketToken);
this.github = new GitHub(this.options.githubToken);
}

async run() {
if (!this.options.bitbucketWorkspace) throw new Error('bitbucketWorkspace input is required');
if (!this.options.githubOwner) throw new Error('githubOwner input is required');
const pullRequests = await this.bitbucket.listAllPullRequests(this.options.bitbucketWorkspace);
for (const [repo, prs] of Object.entries(pullRequests)) {
for (const pr of prs) {
console.log(`Creating PR for ${repo} - ${pr.title}`);
try {
await this.github.createPullRequest({
owner: this.options.githubOwner,
repo,
title: pr.title,
head: pr.source.branch.name,
base: pr.destination.branch.name,
body: pr.summary.markup,
head_repo: pr.source?.repository?.full_name,
draft: pr.state === 'OPEN'
});
} catch (err) {
if ((err as any).status === 422) {

Check failure on line 41 in src/migrate.ts

View workflow job for this annotation

GitHub Actions / CI 🔨🧪🧹 (20)

Unexpected any. Specify a different type
console.log(`PR for ${repo} - ${pr.title} already exists`);
} else {
throw err;
}
}
}
}
console.log(`Finished migrating ${Object.keys(pullRequests).length} repositories and ${Object.values(pullRequests).flat().length} pull requests`);
}
}

0 comments on commit b1321d7

Please sign in to comment.