decap-cms-backend-github 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/decap-cms-backend-github.js +71 -71
- package/dist/decap-cms-backend-github.js.map +1 -1
- package/dist/esm/API.js +23 -11
- package/dist/esm/implementation.js +13 -6
- package/dist/esm/polling.js +391 -0
- package/dist/esm/rateLimitLink.js +24 -0
- package/package.json +2 -2
- package/src/API.ts +133 -109
- package/src/GraphQLAPI.ts +8 -5
- package/src/__tests__/API.spec.js +15 -3
- package/src/implementation.tsx +16 -9
package/src/API.ts
CHANGED
|
@@ -39,14 +39,29 @@ import type {
|
|
|
39
39
|
ApiRequest,
|
|
40
40
|
} from 'decap-cms-lib-util';
|
|
41
41
|
import type { Semaphore } from 'semaphore';
|
|
42
|
-
import type {
|
|
43
|
-
|
|
44
|
-
type GitHubUser =
|
|
45
|
-
type GitCreateTreeParamsTree =
|
|
46
|
-
|
|
47
|
-
type
|
|
48
|
-
|
|
49
|
-
type
|
|
42
|
+
import type { Endpoints } from '@octokit/types';
|
|
43
|
+
|
|
44
|
+
type GitHubUser = Endpoints['GET /user']['response']['data'];
|
|
45
|
+
type GitCreateTreeParamsTree =
|
|
46
|
+
Endpoints['POST /repos/{owner}/{repo}/git/trees']['request']['data']['tree'][0];
|
|
47
|
+
type GitHubCompareCommit =
|
|
48
|
+
Endpoints['GET /repos/{owner}/{repo}/compare/{base}...{head}']['response']['data']['commits'][0];
|
|
49
|
+
type GitHubAuthor = Omit<
|
|
50
|
+
Endpoints['POST /repos/{owner}/{repo}/git/commits']['request']['data']['author'],
|
|
51
|
+
'name' | 'email' | 'date'
|
|
52
|
+
> & { name: string; email: string; date?: string };
|
|
53
|
+
type GitHubCommitter = Omit<
|
|
54
|
+
Endpoints['POST /repos/{owner}/{repo}/git/commits']['request']['data']['committer'],
|
|
55
|
+
'name' | 'email' | 'date'
|
|
56
|
+
> & { name: string; email: string; date?: string };
|
|
57
|
+
type GitHubPull = Omit<
|
|
58
|
+
Endpoints['GET /repos/{owner}/{repo}/pulls']['response']['data'][0],
|
|
59
|
+
'labels'
|
|
60
|
+
> & { labels: GitHubLabel[] };
|
|
61
|
+
type GitHubLabel = Omit<
|
|
62
|
+
Endpoints['GET /repos/{owner}/{repo}/pulls/{pull_number}']['response']['data']['labels'][0],
|
|
63
|
+
'description'
|
|
64
|
+
> & { description: string };
|
|
50
65
|
|
|
51
66
|
export const API_NAME = 'GitHub';
|
|
52
67
|
|
|
@@ -74,13 +89,20 @@ interface TreeFile {
|
|
|
74
89
|
raw?: string;
|
|
75
90
|
}
|
|
76
91
|
|
|
92
|
+
interface TreeFileForUpdate {
|
|
93
|
+
sha: string | null;
|
|
94
|
+
path: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
type Override<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
|
|
78
98
|
|
|
79
99
|
type TreeEntry = Override<GitCreateTreeParamsTree, { sha: string | null }>;
|
|
80
100
|
|
|
81
101
|
type GitHubCompareCommits = GitHubCompareCommit[];
|
|
82
102
|
|
|
83
|
-
type GitHubCompareFile =
|
|
103
|
+
type GitHubCompareFile = NonNullable<
|
|
104
|
+
Endpoints['GET /repos/{owner}/{repo}/compare/{base}...{head}']['response']['data']['files']
|
|
105
|
+
>[0] & {
|
|
84
106
|
previous_filename?: string;
|
|
85
107
|
};
|
|
86
108
|
|
|
@@ -99,9 +121,10 @@ export enum PullRequestState {
|
|
|
99
121
|
All = 'all',
|
|
100
122
|
}
|
|
101
123
|
|
|
102
|
-
type GitHubCommitStatus =
|
|
103
|
-
|
|
104
|
-
|
|
124
|
+
type GitHubCommitStatus =
|
|
125
|
+
Endpoints['GET /repos/{owner}/{repo}/commits/{ref}/statuses']['response']['data'][0] & {
|
|
126
|
+
state: GitHubCommitStatusState;
|
|
127
|
+
};
|
|
105
128
|
|
|
106
129
|
interface MetaDataObjects {
|
|
107
130
|
entry: { path: string; sha: string };
|
|
@@ -164,7 +187,7 @@ function getTreeFiles(files: GitHubCompareFiles) {
|
|
|
164
187
|
arr.push({ sha: file.sha, path: file.filename });
|
|
165
188
|
}
|
|
166
189
|
return arr;
|
|
167
|
-
}, [] as
|
|
190
|
+
}, [] as TreeFileForUpdate[]);
|
|
168
191
|
|
|
169
192
|
return treeFiles;
|
|
170
193
|
}
|
|
@@ -230,19 +253,25 @@ export default class API {
|
|
|
230
253
|
|
|
231
254
|
static DEFAULT_COMMIT_MESSAGE = 'Automatically generated by Decap CMS';
|
|
232
255
|
|
|
233
|
-
user(): Promise<{ name: string; login: string }> {
|
|
256
|
+
user(): Promise<{ name: string; login: string; email?: string }> {
|
|
234
257
|
if (!this._userPromise) {
|
|
235
258
|
this._userPromise = this.getUser({ token: this.token });
|
|
236
259
|
}
|
|
237
|
-
return this._userPromise
|
|
260
|
+
return this._userPromise.then(user => ({
|
|
261
|
+
name: user.name || 'Unknown',
|
|
262
|
+
login: user.login,
|
|
263
|
+
email: user.email ?? undefined,
|
|
264
|
+
}));
|
|
238
265
|
}
|
|
239
266
|
|
|
240
267
|
async hasWriteAccess() {
|
|
241
268
|
try {
|
|
242
|
-
const result:
|
|
269
|
+
const result: Endpoints['GET /repos/{owner}/{repo}']['response']['data'] = await this.request(
|
|
270
|
+
this.repoURL,
|
|
271
|
+
);
|
|
243
272
|
// update config repoOwner to avoid case sensitivity issues with GitHub
|
|
244
273
|
this.repoOwner = result.owner.login;
|
|
245
|
-
return result.permissions
|
|
274
|
+
return result.permissions?.push ?? false;
|
|
246
275
|
} catch (error) {
|
|
247
276
|
console.error('Problem fetching repo data from GitHub');
|
|
248
277
|
throw error;
|
|
@@ -498,17 +527,15 @@ export default class API {
|
|
|
498
527
|
state: PullRequestState,
|
|
499
528
|
predicate: (pr: GitHubPull) => boolean,
|
|
500
529
|
) {
|
|
501
|
-
const pullRequests:
|
|
502
|
-
`${this.originRepoURL}/pulls`,
|
|
503
|
-
{
|
|
530
|
+
const pullRequests: Endpoints['GET /repos/{owner}/{repo}/pulls']['response']['data'] =
|
|
531
|
+
await this.requestAllPages(`${this.originRepoURL}/pulls`, {
|
|
504
532
|
params: {
|
|
505
533
|
...(head ? { head: await this.getHeadReference(head) } : {}),
|
|
506
534
|
base: this.branch,
|
|
507
535
|
state,
|
|
508
536
|
per_page: 100,
|
|
509
537
|
},
|
|
510
|
-
}
|
|
511
|
-
);
|
|
538
|
+
});
|
|
512
539
|
|
|
513
540
|
return pullRequests.filter(
|
|
514
541
|
pr => pr.head.ref.startsWith(`${CMS_BRANCH_PREFIX}/`) && predicate(pr),
|
|
@@ -544,7 +571,7 @@ export default class API {
|
|
|
544
571
|
? { name: statusToLabel(this.initialWorkflowStatus, this.cmsLabelPrefix) }
|
|
545
572
|
: { name: statusToLabel('pending_review', this.cmsLabelPrefix) };
|
|
546
573
|
|
|
547
|
-
pullRequest.labels.push(cmsLabel as
|
|
574
|
+
pullRequest.labels.push(cmsLabel as GitHubLabel);
|
|
548
575
|
return pullRequest;
|
|
549
576
|
}
|
|
550
577
|
}
|
|
@@ -569,9 +596,8 @@ export default class API {
|
|
|
569
596
|
return [];
|
|
570
597
|
}
|
|
571
598
|
try {
|
|
572
|
-
const commits:
|
|
573
|
-
`${this.originRepoURL}/pulls/${number}/commits
|
|
574
|
-
);
|
|
599
|
+
const commits: Endpoints['GET /repos/{owner}/{repo}/pulls/{pull_number}/commits']['response']['data'] =
|
|
600
|
+
await this.request(`${this.originRepoURL}/pulls/${number}/commits`);
|
|
575
601
|
return commits;
|
|
576
602
|
} catch (e) {
|
|
577
603
|
console.log(e);
|
|
@@ -579,7 +605,7 @@ export default class API {
|
|
|
579
605
|
}
|
|
580
606
|
}
|
|
581
607
|
|
|
582
|
-
async getPullRequestAuthor(pullRequest:
|
|
608
|
+
async getPullRequestAuthor(pullRequest: GitHubPull) {
|
|
583
609
|
if (!pullRequest.user?.login) {
|
|
584
610
|
return;
|
|
585
611
|
}
|
|
@@ -600,7 +626,7 @@ export default class API {
|
|
|
600
626
|
this.getDifferences(this.branch, pullRequest.head.sha),
|
|
601
627
|
this.getPullRequestAuthor(pullRequest),
|
|
602
628
|
]);
|
|
603
|
-
const diffs = await Promise.all(files.map(file => this.diffFromFile(file)));
|
|
629
|
+
const diffs = await Promise.all((files || []).map(file => this.diffFromFile(file)));
|
|
604
630
|
const label = pullRequest.labels.find(l => isCMSLabel(l.name, this.cmsLabelPrefix)) as {
|
|
605
631
|
name: string;
|
|
606
632
|
};
|
|
@@ -639,16 +665,14 @@ export default class API {
|
|
|
639
665
|
async readFileMetadata(path: string, sha: string | null | undefined) {
|
|
640
666
|
const fetchFileMetadata = async () => {
|
|
641
667
|
try {
|
|
642
|
-
const result:
|
|
643
|
-
`${this.originRepoURL}/commits`,
|
|
644
|
-
{
|
|
668
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/commits']['response']['data'] =
|
|
669
|
+
await this.request(`${this.originRepoURL}/commits`, {
|
|
645
670
|
params: { path, sha: this.branch },
|
|
646
|
-
}
|
|
647
|
-
);
|
|
671
|
+
});
|
|
648
672
|
const { commit } = result[0];
|
|
649
673
|
return {
|
|
650
|
-
author: commit.author
|
|
651
|
-
updatedOn: commit.author
|
|
674
|
+
author: commit.author?.name || commit.author?.email || '',
|
|
675
|
+
updatedOn: commit.author?.date || '',
|
|
652
676
|
};
|
|
653
677
|
} catch (e) {
|
|
654
678
|
return { author: '', updatedOn: '' };
|
|
@@ -659,9 +683,10 @@ export default class API {
|
|
|
659
683
|
}
|
|
660
684
|
|
|
661
685
|
async fetchBlobContent({ sha, repoURL, parseText }: BlobArgs) {
|
|
662
|
-
const result:
|
|
663
|
-
|
|
664
|
-
|
|
686
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/git/blobs/{file_sha}']['response']['data'] =
|
|
687
|
+
await this.request(`${repoURL}/git/blobs/${sha}`, {
|
|
688
|
+
cache: 'force-cache',
|
|
689
|
+
});
|
|
665
690
|
|
|
666
691
|
if (parseText) {
|
|
667
692
|
// treat content as a utf-8 string
|
|
@@ -685,24 +710,22 @@ export default class API {
|
|
|
685
710
|
): Promise<{ type: string; id: string; name: string; path: string; size: number }[]> {
|
|
686
711
|
const folder = trim(path, '/');
|
|
687
712
|
try {
|
|
688
|
-
const result:
|
|
689
|
-
`${repoURL}/git/trees/${branch}:${folder}`,
|
|
690
|
-
{
|
|
713
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/git/trees/{tree_sha}']['response']['data'] =
|
|
714
|
+
await this.request(`${repoURL}/git/trees/${branch}:${folder}`, {
|
|
691
715
|
// GitHub API supports recursive=1 for getting the entire recursive tree
|
|
692
716
|
// or omitting it to get the non-recursive tree
|
|
693
717
|
params: depth > 1 ? { recursive: 1 } : {},
|
|
694
|
-
}
|
|
695
|
-
);
|
|
718
|
+
});
|
|
696
719
|
return (
|
|
697
720
|
result.tree
|
|
698
721
|
// filter only files and up to the required depth
|
|
699
|
-
.filter(file => file.type === 'blob' && file.path.split('/').length <= depth)
|
|
722
|
+
.filter(file => file.type === 'blob' && file.path && file.path.split('/').length <= depth)
|
|
700
723
|
.map(file => ({
|
|
701
724
|
type: file.type,
|
|
702
725
|
id: file.sha,
|
|
703
726
|
name: basename(file.path),
|
|
704
727
|
path: `${folder}/${file.path}`,
|
|
705
|
-
size: file.size
|
|
728
|
+
size: file.size || 0,
|
|
706
729
|
}))
|
|
707
730
|
);
|
|
708
731
|
} catch (err) {
|
|
@@ -821,9 +844,12 @@ export default class API {
|
|
|
821
844
|
}
|
|
822
845
|
|
|
823
846
|
async getOpenAuthoringBranches() {
|
|
824
|
-
const cmsBranches = await this.requestAllPages<
|
|
825
|
-
|
|
826
|
-
).catch(
|
|
847
|
+
const cmsBranches = await this.requestAllPages<
|
|
848
|
+
Endpoints['GET /repos/{owner}/{repo}/git/matching-refs/{ref}']['response']['data'][0]
|
|
849
|
+
>(`${this.repoURL}/git/refs/heads/cms/${this.repo}`).catch(
|
|
850
|
+
() =>
|
|
851
|
+
[] as Endpoints['GET /repos/{owner}/{repo}/git/matching-refs/{ref}']['response']['data'],
|
|
852
|
+
);
|
|
827
853
|
return cmsBranches;
|
|
828
854
|
}
|
|
829
855
|
|
|
@@ -836,7 +862,7 @@ export default class API {
|
|
|
836
862
|
let branches: string[];
|
|
837
863
|
if (this.useOpenAuthoring) {
|
|
838
864
|
// open authoring branches can exist without a pr
|
|
839
|
-
const cmsBranches:
|
|
865
|
+
const cmsBranches: Endpoints['GET /repos/{owner}/{repo}/git/matching-refs/{ref}']['response']['data'] =
|
|
840
866
|
await this.getOpenAuthoringBranches();
|
|
841
867
|
branches = cmsBranches.map(b => b.ref.slice('refs/heads/'.length));
|
|
842
868
|
// filter irrelevant branches
|
|
@@ -887,7 +913,7 @@ export default class API {
|
|
|
887
913
|
);
|
|
888
914
|
return resp.statuses.map(s => ({
|
|
889
915
|
context: s.context,
|
|
890
|
-
target_url: s.target_url,
|
|
916
|
+
target_url: s.target_url || '',
|
|
891
917
|
state:
|
|
892
918
|
s.state === GitHubCommitStatusState.Success ? PreviewState.Success : PreviewState.Other,
|
|
893
919
|
}));
|
|
@@ -930,7 +956,8 @@ export default class API {
|
|
|
930
956
|
const fileDataPath = encodeURIComponent(directory);
|
|
931
957
|
const fileDataURL = `${repoURL}/git/trees/${branch}:${fileDataPath}`;
|
|
932
958
|
|
|
933
|
-
const result:
|
|
959
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/git/trees/{tree_sha}']['response']['data'] =
|
|
960
|
+
await this.request(fileDataURL);
|
|
934
961
|
const file = result.tree.find(file => file.path === filename);
|
|
935
962
|
if (file) {
|
|
936
963
|
return file.sha;
|
|
@@ -964,11 +991,11 @@ export default class API {
|
|
|
964
991
|
}
|
|
965
992
|
|
|
966
993
|
// async since it is overridden in a child class
|
|
967
|
-
async diffFromFile(diff:
|
|
994
|
+
async diffFromFile(diff: GitHubCompareFile): Promise<Diff> {
|
|
968
995
|
return {
|
|
969
996
|
path: diff.filename,
|
|
970
997
|
newFile: diff.status === 'added',
|
|
971
|
-
sha: diff.sha,
|
|
998
|
+
sha: diff.sha || '',
|
|
972
999
|
// media files diffs don't have a patch attribute, except svg files
|
|
973
1000
|
// renamed files don't have a patch attribute too
|
|
974
1001
|
binary: (diff.status !== 'renamed' && !diff.patch) || diff.filename.endsWith('.svg'),
|
|
@@ -997,7 +1024,10 @@ export default class API {
|
|
|
997
1024
|
commitResponse.sha,
|
|
998
1025
|
options.commitMessage,
|
|
999
1026
|
);
|
|
1000
|
-
await this.setPullRequestStatus(
|
|
1027
|
+
await this.setPullRequestStatus(
|
|
1028
|
+
pr as GitHubPull,
|
|
1029
|
+
options.status || this.initialWorkflowStatus,
|
|
1030
|
+
);
|
|
1001
1031
|
}
|
|
1002
1032
|
} else {
|
|
1003
1033
|
// Entry is already on editorial review workflow - commit to existing branch
|
|
@@ -1006,7 +1036,7 @@ export default class API {
|
|
|
1006
1036
|
await this.getHeadReference(branch),
|
|
1007
1037
|
);
|
|
1008
1038
|
|
|
1009
|
-
const diffs = await Promise.all(diffFiles.map(file => this.diffFromFile(file)));
|
|
1039
|
+
const diffs = await Promise.all((diffFiles || []).map(file => this.diffFromFile(file)));
|
|
1010
1040
|
// mark media files to remove
|
|
1011
1041
|
const mediaFilesToRemove: { path: string; sha: string | null }[] = [];
|
|
1012
1042
|
for (const diff of diffs.filter(d => d.binary)) {
|
|
@@ -1030,9 +1060,8 @@ export default class API {
|
|
|
1030
1060
|
const attempts = this.useOpenAuthoring ? 10 : 1;
|
|
1031
1061
|
for (let i = 1; i <= attempts; i++) {
|
|
1032
1062
|
try {
|
|
1033
|
-
const result:
|
|
1034
|
-
`${this.originRepoURL}/compare/${from}...${to}
|
|
1035
|
-
);
|
|
1063
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/compare/{base}...{head}']['response']['data'] =
|
|
1064
|
+
await this.request(`${this.originRepoURL}/compare/${from}...${to}`);
|
|
1036
1065
|
return result;
|
|
1037
1066
|
} catch (e) {
|
|
1038
1067
|
if (i === attempts) {
|
|
@@ -1061,8 +1090,12 @@ export default class API {
|
|
|
1061
1090
|
message,
|
|
1062
1091
|
tree.sha,
|
|
1063
1092
|
[baseCommit.sha],
|
|
1064
|
-
author
|
|
1065
|
-
|
|
1093
|
+
author
|
|
1094
|
+
? { name: author.name || '', email: author.email || '', date: author.date }
|
|
1095
|
+
: undefined,
|
|
1096
|
+
committer
|
|
1097
|
+
? { name: committer.name || '', email: committer.email || '', date: committer.date }
|
|
1098
|
+
: undefined,
|
|
1066
1099
|
);
|
|
1067
1100
|
return newCommit as unknown as GitHubCompareCommit;
|
|
1068
1101
|
} else {
|
|
@@ -1175,22 +1208,21 @@ export default class API {
|
|
|
1175
1208
|
}
|
|
1176
1209
|
|
|
1177
1210
|
async createRef(type: string, name: string, sha: string) {
|
|
1178
|
-
const result:
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1211
|
+
const result: Endpoints['POST /repos/{owner}/{repo}/git/refs']['response']['data'] =
|
|
1212
|
+
await this.request(`${this.repoURL}/git/refs`, {
|
|
1213
|
+
method: 'POST',
|
|
1214
|
+
body: JSON.stringify({ ref: `refs/${type}/${name}`, sha }),
|
|
1215
|
+
});
|
|
1182
1216
|
return result;
|
|
1183
1217
|
}
|
|
1184
1218
|
|
|
1185
1219
|
async patchRef(type: string, name: string, sha: string, opts: { force?: boolean } = {}) {
|
|
1186
1220
|
const force = opts.force || false;
|
|
1187
|
-
const result:
|
|
1188
|
-
`${this.repoURL}/git/refs/${type}/${encodeURIComponent(name)}`,
|
|
1189
|
-
{
|
|
1221
|
+
const result: Endpoints['PATCH /repos/{owner}/{repo}/git/refs/{ref}']['response']['data'] =
|
|
1222
|
+
await this.request(`${this.repoURL}/git/refs/${type}/${encodeURIComponent(name)}`, {
|
|
1190
1223
|
method: 'PATCH',
|
|
1191
1224
|
body: JSON.stringify({ sha, force }),
|
|
1192
|
-
}
|
|
1193
|
-
);
|
|
1225
|
+
});
|
|
1194
1226
|
return result;
|
|
1195
1227
|
}
|
|
1196
1228
|
|
|
@@ -1201,16 +1233,14 @@ export default class API {
|
|
|
1201
1233
|
}
|
|
1202
1234
|
|
|
1203
1235
|
async getBranch(branch: string) {
|
|
1204
|
-
const result:
|
|
1205
|
-
`${this.repoURL}/branches/${encodeURIComponent(branch)}
|
|
1206
|
-
);
|
|
1236
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/branches/{branch}']['response']['data'] =
|
|
1237
|
+
await this.request(`${this.repoURL}/branches/${encodeURIComponent(branch)}`);
|
|
1207
1238
|
return result;
|
|
1208
1239
|
}
|
|
1209
1240
|
|
|
1210
1241
|
async getDefaultBranch() {
|
|
1211
|
-
const result:
|
|
1212
|
-
`${this.originRepoURL}/branches/${encodeURIComponent(this.branch)}
|
|
1213
|
-
);
|
|
1242
|
+
const result: Endpoints['GET /repos/{owner}/{repo}/branches/{branch}']['response']['data'] =
|
|
1243
|
+
await this.request(`${this.originRepoURL}/branches/${encodeURIComponent(this.branch)}`);
|
|
1214
1244
|
return result;
|
|
1215
1245
|
}
|
|
1216
1246
|
|
|
@@ -1285,61 +1315,56 @@ export default class API {
|
|
|
1285
1315
|
}
|
|
1286
1316
|
|
|
1287
1317
|
async createPR(title: string, head: string) {
|
|
1288
|
-
const result:
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1318
|
+
const result: Endpoints['POST /repos/{owner}/{repo}/pulls']['response']['data'] =
|
|
1319
|
+
await this.request(`${this.originRepoURL}/pulls`, {
|
|
1320
|
+
method: 'POST',
|
|
1321
|
+
body: JSON.stringify({
|
|
1322
|
+
title,
|
|
1323
|
+
body: DEFAULT_PR_BODY,
|
|
1324
|
+
head: await this.getHeadReference(head),
|
|
1325
|
+
base: this.branch,
|
|
1326
|
+
}),
|
|
1327
|
+
});
|
|
1297
1328
|
|
|
1298
1329
|
return result;
|
|
1299
1330
|
}
|
|
1300
1331
|
|
|
1301
1332
|
async openPR(number: number) {
|
|
1302
1333
|
console.log('%c Re-opening PR', 'line-height: 30px;text-align: center;font-weight: bold');
|
|
1303
|
-
const result:
|
|
1304
|
-
`${this.originRepoURL}/pulls/${number}`,
|
|
1305
|
-
{
|
|
1334
|
+
const result: Endpoints['PATCH /repos/{owner}/{repo}/pulls/{pull_number}']['response']['data'] =
|
|
1335
|
+
await this.request(`${this.originRepoURL}/pulls/${number}`, {
|
|
1306
1336
|
method: 'PATCH',
|
|
1307
1337
|
body: JSON.stringify({
|
|
1308
1338
|
state: PullRequestState.Open,
|
|
1309
1339
|
}),
|
|
1310
|
-
}
|
|
1311
|
-
);
|
|
1340
|
+
});
|
|
1312
1341
|
return result;
|
|
1313
1342
|
}
|
|
1314
1343
|
|
|
1315
1344
|
async closePR(number: number) {
|
|
1316
1345
|
console.log('%c Deleting PR', 'line-height: 30px;text-align: center;font-weight: bold');
|
|
1317
|
-
const result:
|
|
1318
|
-
`${this.originRepoURL}/pulls/${number}`,
|
|
1319
|
-
{
|
|
1346
|
+
const result: Endpoints['PATCH /repos/{owner}/{repo}/pulls/{pull_number}']['response']['data'] =
|
|
1347
|
+
await this.request(`${this.originRepoURL}/pulls/${number}`, {
|
|
1320
1348
|
method: 'PATCH',
|
|
1321
1349
|
body: JSON.stringify({
|
|
1322
1350
|
state: PullRequestState.Closed,
|
|
1323
1351
|
}),
|
|
1324
|
-
}
|
|
1325
|
-
);
|
|
1352
|
+
});
|
|
1326
1353
|
return result;
|
|
1327
1354
|
}
|
|
1328
1355
|
|
|
1329
1356
|
async mergePR(pullrequest: GitHubPull) {
|
|
1330
1357
|
console.log('%c Merging PR', 'line-height: 30px;text-align: center;font-weight: bold');
|
|
1331
1358
|
try {
|
|
1332
|
-
const result:
|
|
1333
|
-
`${this.originRepoURL}/pulls/${pullrequest.number}/merge`,
|
|
1334
|
-
{
|
|
1359
|
+
const result: Endpoints['PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge']['response']['data'] =
|
|
1360
|
+
await this.request(`${this.originRepoURL}/pulls/${pullrequest.number}/merge`, {
|
|
1335
1361
|
method: 'PUT',
|
|
1336
1362
|
body: JSON.stringify({
|
|
1337
1363
|
commit_message: MERGE_COMMIT_MESSAGE,
|
|
1338
1364
|
sha: pullrequest.head.sha,
|
|
1339
1365
|
merge_method: this.mergeMethod,
|
|
1340
1366
|
}),
|
|
1341
|
-
}
|
|
1342
|
-
);
|
|
1367
|
+
});
|
|
1343
1368
|
return result;
|
|
1344
1369
|
} catch (error) {
|
|
1345
1370
|
if (error instanceof APIError && error.status === 405) {
|
|
@@ -1355,7 +1380,7 @@ export default class API {
|
|
|
1355
1380
|
const files = getTreeFiles(result.files as GitHubCompareFiles);
|
|
1356
1381
|
|
|
1357
1382
|
let commitMessage = 'Automatically generated. Merged on Decap CMS\n\nForce merge of:';
|
|
1358
|
-
files.forEach(file => {
|
|
1383
|
+
files.forEach((file: TreeFileForUpdate) => {
|
|
1359
1384
|
commitMessage += `\n* "${file.path}"`;
|
|
1360
1385
|
});
|
|
1361
1386
|
console.log(
|
|
@@ -1439,10 +1464,11 @@ export default class API {
|
|
|
1439
1464
|
}
|
|
1440
1465
|
|
|
1441
1466
|
async createTree(baseSha: string, tree: TreeEntry[]) {
|
|
1442
|
-
const result:
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1467
|
+
const result: Endpoints['POST /repos/{owner}/{repo}/git/trees']['response']['data'] =
|
|
1468
|
+
await this.request(`${this.repoURL}/git/trees`, {
|
|
1469
|
+
method: 'POST',
|
|
1470
|
+
body: JSON.stringify({ base_tree: baseSha, tree }),
|
|
1471
|
+
});
|
|
1446
1472
|
return result;
|
|
1447
1473
|
}
|
|
1448
1474
|
|
|
@@ -1458,13 +1484,11 @@ export default class API {
|
|
|
1458
1484
|
author?: GitHubAuthor,
|
|
1459
1485
|
committer?: GitHubCommitter,
|
|
1460
1486
|
) {
|
|
1461
|
-
const result:
|
|
1462
|
-
`${this.repoURL}/git/commits`,
|
|
1463
|
-
{
|
|
1487
|
+
const result: Endpoints['POST /repos/{owner}/{repo}/git/commits']['response']['data'] =
|
|
1488
|
+
await this.request(`${this.repoURL}/git/commits`, {
|
|
1464
1489
|
method: 'POST',
|
|
1465
1490
|
body: JSON.stringify({ message, tree: treeSha, parents, author, committer }),
|
|
1466
|
-
}
|
|
1467
|
-
);
|
|
1491
|
+
});
|
|
1468
1492
|
return result;
|
|
1469
1493
|
}
|
|
1470
1494
|
|
package/src/GraphQLAPI.ts
CHANGED
|
@@ -27,7 +27,7 @@ import type { Config, BlobArgs } from './API';
|
|
|
27
27
|
import type { NormalizedCacheObject } from 'apollo-cache-inmemory';
|
|
28
28
|
import type { QueryOptions, MutationOptions, OperationVariables } from 'apollo-client';
|
|
29
29
|
import type { GraphQLError } from 'graphql';
|
|
30
|
-
import type {
|
|
30
|
+
import type { Endpoints } from '@octokit/types';
|
|
31
31
|
|
|
32
32
|
const NO_CACHE = 'no-cache';
|
|
33
33
|
const CACHE_FIRST = 'cache-first';
|
|
@@ -93,6 +93,7 @@ function transformPullRequest(pr: GraphQLPullRequest) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
type Error = GraphQLError & { type: string };
|
|
96
|
+
type GitHubPull = Endpoints['GET /repos/{owner}/{repo}/pulls']['response']['data'][0];
|
|
96
97
|
|
|
97
98
|
export default class GraphQLAPI extends API {
|
|
98
99
|
client: ApolloClient<NormalizedCacheObject>;
|
|
@@ -281,7 +282,7 @@ export default class GraphQLAPI extends API {
|
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
284
|
|
|
284
|
-
async getPullRequestAuthor(pullRequest:
|
|
285
|
+
async getPullRequestAuthor(pullRequest: GitHubPull) {
|
|
285
286
|
const user = pullRequest.user as unknown as GraphQLPullsListResponseItemUser;
|
|
286
287
|
return user?.name || user?.login;
|
|
287
288
|
}
|
|
@@ -289,7 +290,7 @@ export default class GraphQLAPI extends API {
|
|
|
289
290
|
async getPullRequests(
|
|
290
291
|
head: string | undefined,
|
|
291
292
|
state: PullRequestState,
|
|
292
|
-
predicate: (pr:
|
|
293
|
+
predicate: (pr: GitHubPull) => boolean,
|
|
293
294
|
) {
|
|
294
295
|
const { originRepoOwner: owner, originRepoName: name } = this;
|
|
295
296
|
let states;
|
|
@@ -319,7 +320,7 @@ export default class GraphQLAPI extends API {
|
|
|
319
320
|
|
|
320
321
|
const mapped = pullRequests.nodes.map(transformPullRequest);
|
|
321
322
|
|
|
322
|
-
return (mapped as unknown as
|
|
323
|
+
return (mapped as unknown as GitHubPull[]).filter(
|
|
323
324
|
pr => pr.head.ref.startsWith(`${CMS_BRANCH_PREFIX}/`) && predicate(pr),
|
|
324
325
|
);
|
|
325
326
|
}
|
|
@@ -692,7 +693,9 @@ export default class GraphQLAPI extends API {
|
|
|
692
693
|
},
|
|
693
694
|
});
|
|
694
695
|
const { pullRequest } = data!.createPullRequest;
|
|
695
|
-
return transformPullRequest(
|
|
696
|
+
return transformPullRequest(
|
|
697
|
+
pullRequest,
|
|
698
|
+
) as unknown as Endpoints['POST /repos/{owner}/{repo}/pulls']['response']['data'];
|
|
696
699
|
}
|
|
697
700
|
|
|
698
701
|
async getFileSha(path: string, { repoURL = this.repoURL, branch = this.branch } = {}) {
|
|
@@ -717,8 +717,8 @@ describe('github API', () => {
|
|
|
717
717
|
'message',
|
|
718
718
|
newTree.sha,
|
|
719
719
|
[baseCommit.sha],
|
|
720
|
-
{ name: 'author' },
|
|
721
|
-
{ name: 'committer' },
|
|
720
|
+
{ name: 'author', email: '', date: undefined },
|
|
721
|
+
{ name: 'committer', email: '', date: undefined },
|
|
722
722
|
);
|
|
723
723
|
});
|
|
724
724
|
});
|
|
@@ -756,6 +756,8 @@ describe('github API', () => {
|
|
|
756
756
|
path: 'posts/post.md',
|
|
757
757
|
type: 'blob',
|
|
758
758
|
name: 'post.md',
|
|
759
|
+
id: undefined,
|
|
760
|
+
size: 0,
|
|
759
761
|
},
|
|
760
762
|
]);
|
|
761
763
|
expect(api.request).toHaveBeenCalledTimes(1);
|
|
@@ -769,11 +771,15 @@ describe('github API', () => {
|
|
|
769
771
|
path: 'posts/post.md',
|
|
770
772
|
type: 'blob',
|
|
771
773
|
name: 'post.md',
|
|
774
|
+
id: undefined,
|
|
775
|
+
size: 0,
|
|
772
776
|
},
|
|
773
777
|
{
|
|
774
778
|
path: 'posts/dir1/nested-post.md',
|
|
775
779
|
type: 'blob',
|
|
776
780
|
name: 'nested-post.md',
|
|
781
|
+
id: undefined,
|
|
782
|
+
size: 0,
|
|
777
783
|
},
|
|
778
784
|
]);
|
|
779
785
|
expect(api.request).toHaveBeenCalledTimes(1);
|
|
@@ -787,16 +793,22 @@ describe('github API', () => {
|
|
|
787
793
|
path: 'posts/post.md',
|
|
788
794
|
type: 'blob',
|
|
789
795
|
name: 'post.md',
|
|
796
|
+
id: undefined,
|
|
797
|
+
size: 0,
|
|
790
798
|
},
|
|
791
799
|
{
|
|
792
800
|
path: 'posts/dir1/nested-post.md',
|
|
793
801
|
type: 'blob',
|
|
794
802
|
name: 'nested-post.md',
|
|
803
|
+
id: undefined,
|
|
804
|
+
size: 0,
|
|
795
805
|
},
|
|
796
806
|
{
|
|
797
807
|
path: 'posts/dir1/dir2/nested-post.md',
|
|
798
808
|
type: 'blob',
|
|
799
809
|
name: 'nested-post.md',
|
|
810
|
+
id: undefined,
|
|
811
|
+
size: 0,
|
|
800
812
|
},
|
|
801
813
|
]);
|
|
802
814
|
expect(api.request).toHaveBeenCalledTimes(1);
|
|
@@ -822,7 +834,7 @@ describe('github API', () => {
|
|
|
822
834
|
const slug = 'slug';
|
|
823
835
|
await expect(api.getStatuses(collection, slug)).resolves.toEqual([
|
|
824
836
|
{ context: 'deploy', state: 'success', target_url: 'deploy-url' },
|
|
825
|
-
{ context: 'build', state: 'other' },
|
|
837
|
+
{ context: 'build', state: 'other', target_url: '' },
|
|
826
838
|
]);
|
|
827
839
|
|
|
828
840
|
expect(api.getBranchPullRequest).toHaveBeenCalledTimes(1);
|