UNPKG

7.41 kBJavaScriptView Raw
1async function valueToTreeObject(octokit, owner, repo, path, value) {
2 // Text files can be changed through the .content key
3 if (typeof value === "string") {
4 return {
5 path,
6 mode: "100644",
7 content: value,
8 };
9 }
10 // Binary files need to be created first using the git blob API,
11 // then changed by referencing in the .sha key
12 const { data } = await octokit.request("POST /repos/{owner}/{repo}/git/blobs", {
13 owner,
14 repo,
15 ...value,
16 });
17 const blobSha = data.sha;
18 return {
19 path,
20 mode: "100644",
21 sha: blobSha,
22 };
23}
24
25async function createTree(state, changes) {
26 const { octokit, owner, repo, fork, latestCommitSha, latestCommitTreeSha, } = state;
27 const tree = (await Promise.all(Object.keys(changes.files).map(async (path) => {
28 const value = changes.files[path];
29 if (value === null) {
30 // Deleting a non-existent file from a tree leads to an "GitRPC::BadObjectState" error,
31 // so we only attempt to delete the file if it exists.
32 try {
33 // https://developer.github.com/v3/repos/contents/#get-contents
34 await octokit.request("HEAD /repos/{owner}/{repo}/contents/:path", {
35 owner: fork,
36 repo,
37 ref: latestCommitSha,
38 path,
39 });
40 return {
41 path,
42 mode: "100644",
43 sha: null,
44 };
45 }
46 catch (error) {
47 return;
48 }
49 }
50 // When passed a function, retrieve the content of the file, pass it
51 // to the function, then return the result
52 if (typeof value === "function") {
53 let result;
54 try {
55 const { data: file } = await octokit.request("GET /repos/{owner}/{repo}/contents/:path", {
56 owner: fork,
57 repo,
58 ref: latestCommitSha,
59 path,
60 });
61 result = await value(Object.assign(file, { exists: true }));
62 }
63 catch (error) {
64 // istanbul ignore if
65 if (error.status !== 404)
66 throw error;
67 // @ts-ignore
68 result = await value({ exists: false });
69 }
70 if (result === null || typeof result === "undefined")
71 return;
72 return valueToTreeObject(octokit, owner, repo, path, result);
73 }
74 return valueToTreeObject(octokit, owner, repo, path, value);
75 }))).filter(Boolean);
76 if (tree.length === 0) {
77 return null;
78 }
79 // https://developer.github.com/v3/git/trees/#create-a-tree
80 const { data: { sha: newTreeSha }, } = await octokit.request("POST /repos/{owner}/{repo}/git/trees", {
81 owner: fork,
82 repo,
83 base_tree: latestCommitTreeSha,
84 tree,
85 });
86 return newTreeSha;
87}
88
89async function createCommit(state, treeCreated, changes) {
90 const { octokit, repo, fork, latestCommitSha } = state;
91 const message = treeCreated
92 ? changes.commit
93 : typeof changes.emptyCommit === "string"
94 ? changes.emptyCommit
95 : changes.commit;
96 // https://developer.github.com/v3/git/commits/#create-a-commit
97 const { data: latestCommit } = await octokit.request("POST /repos/{owner}/{repo}/git/commits", {
98 owner: fork,
99 repo,
100 message,
101 tree: state.latestCommitTreeSha,
102 parents: [latestCommitSha],
103 });
104 return latestCommit.sha;
105}
106
107async function composeCreatePullRequest(octokit, { owner, repo, title, body, base, head, createWhenEmpty, changes: changesOption, draft = false, }) {
108 const changes = Array.isArray(changesOption)
109 ? changesOption
110 : [changesOption];
111 if (changes.length === 0)
112 throw new Error('[octokit-plugin-create-pull-request] "changes" cannot be an empty array');
113 const state = { octokit, owner, repo };
114 // https://developer.github.com/v3/repos/#get-a-repository
115 const { data: repository, headers } = await octokit.request("GET /repos/{owner}/{repo}", {
116 owner,
117 repo,
118 });
119 const isUser = !!headers["x-oauth-scopes"];
120 if (!repository.permissions) {
121 throw new Error("[octokit-plugin-create-pull-request] Missing authentication");
122 }
123 if (!base) {
124 base = repository.default_branch;
125 }
126 state.fork = owner;
127 if (isUser && !repository.permissions.push) {
128 // https://developer.github.com/v3/users/#get-the-authenticated-user
129 const user = await octokit.request("GET /user");
130 // https://developer.github.com/v3/repos/forks/#list-forks
131 const forks = await octokit.request("GET /repos/{owner}/{repo}/forks", {
132 owner,
133 repo,
134 });
135 const hasFork = forks.data.find(
136 /* istanbul ignore next - fork owner can be null, but we don't test that */
137 (fork) => fork.owner?.login === user.data.login);
138 if (!hasFork) {
139 // https://developer.github.com/v3/repos/forks/#create-a-fork
140 await octokit.request("POST /repos/{owner}/{repo}/forks", {
141 owner,
142 repo,
143 });
144 }
145 state.fork = user.data.login;
146 }
147 // https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
148 const { data: [latestCommit], } = await octokit.request("GET /repos/{owner}/{repo}/commits", {
149 owner,
150 repo,
151 sha: base,
152 per_page: 1,
153 });
154 state.latestCommitSha = latestCommit.sha;
155 state.latestCommitTreeSha = latestCommit.commit.tree.sha;
156 const baseCommitTreeSha = latestCommit.commit.tree.sha;
157 for (const change of changes) {
158 let treeCreated = false;
159 if (change.files && Object.keys(change.files).length) {
160 const latestCommitTreeSha = await createTree(state, change);
161 if (latestCommitTreeSha) {
162 state.latestCommitTreeSha = latestCommitTreeSha;
163 treeCreated = true;
164 }
165 }
166 if (treeCreated || change.emptyCommit !== false) {
167 state.latestCommitSha = await createCommit(state, treeCreated, change);
168 }
169 }
170 const hasNoChanges = baseCommitTreeSha === state.latestCommitTreeSha;
171 if (hasNoChanges && createWhenEmpty === false) {
172 return null;
173 }
174 // https://developer.github.com/v3/git/refs/#create-a-reference
175 await octokit.request("POST /repos/{owner}/{repo}/git/refs", {
176 owner: state.fork,
177 repo,
178 sha: state.latestCommitSha,
179 ref: `refs/heads/${head}`,
180 });
181 // https://developer.github.com/v3/pulls/#create-a-pull-request
182 return await octokit.request("POST /repos/{owner}/{repo}/pulls", {
183 owner,
184 repo,
185 head: `${state.fork}:${head}`,
186 base,
187 title,
188 body,
189 draft,
190 });
191}
192
193const VERSION = "3.9.2";
194
195/**
196 * @param octokit Octokit instance
197 */
198function createPullRequest(octokit) {
199 return {
200 createPullRequest: composeCreatePullRequest.bind(null, octokit),
201 };
202}
203createPullRequest.VERSION = VERSION;
204
205export { composeCreatePullRequest, createPullRequest };
206//# sourceMappingURL=index.js.map