decap-cms-backend-github 3.2.3-beta.0 → 3.3.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 +1 -1
- package/dist/decap-cms-backend-github.js +67 -67
- package/dist/decap-cms-backend-github.js.map +1 -1
- package/dist/esm/API.js +172 -209
- package/dist/esm/AuthenticationPage.js +85 -98
- package/dist/esm/GraphQLAPI.js +125 -121
- package/dist/esm/fragmentTypes.js +0 -2
- package/dist/esm/fragments.js +8 -14
- package/dist/esm/implementation.js +85 -122
- package/dist/esm/index.js +9 -33
- package/dist/esm/mutations.js +10 -17
- package/dist/esm/queries.js +18 -28
- package/dist/esm/types/semaphore.d.js +0 -1
- package/package.json +3 -3
- package/src/API.ts +6 -1
- package/src/AuthenticationPage.js +10 -0
- package/src/GraphQLAPI.ts +2 -1
package/dist/esm/API.js
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var _semaphore = _interopRequireDefault(require("semaphore"));
|
|
15
|
-
var _commonTags = require("common-tags");
|
|
16
|
-
var _decapCmsLibUtil = require("decap-cms-lib-util");
|
|
17
|
-
var _path = require("path");
|
|
18
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
20
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
21
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
22
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
|
|
23
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
24
|
-
const API_NAME = exports.API_NAME = 'GitHub';
|
|
25
|
-
const MOCK_PULL_REQUEST = exports.MOCK_PULL_REQUEST = -1;
|
|
1
|
+
import { Base64 } from 'js-base64';
|
|
2
|
+
import semaphore from 'semaphore';
|
|
3
|
+
import initial from 'lodash/initial';
|
|
4
|
+
import last from 'lodash/last';
|
|
5
|
+
import partial from 'lodash/partial';
|
|
6
|
+
import result from 'lodash/result';
|
|
7
|
+
import trimStart from 'lodash/trimStart';
|
|
8
|
+
import trim from 'lodash/trim';
|
|
9
|
+
import { oneLine } from 'common-tags';
|
|
10
|
+
import { getAllResponses, APIError, EditorialWorkflowError, localForage, basename, readFileMetadata, CMS_BRANCH_PREFIX, generateContentKey, DEFAULT_PR_BODY, MERGE_COMMIT_MESSAGE, PreviewState, parseContentKey, branchFromContentKey, isCMSLabel, labelToStatus, statusToLabel, contentKeyFromBranch, requestWithBackoff, unsentRequest, throwOnConflictingBranches } from 'decap-cms-lib-util';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
export const API_NAME = 'GitHub';
|
|
13
|
+
export const MOCK_PULL_REQUEST = -1;
|
|
26
14
|
var GitHubCommitStatusState = /*#__PURE__*/function (GitHubCommitStatusState) {
|
|
27
15
|
GitHubCommitStatusState["Error"] = "error";
|
|
28
16
|
GitHubCommitStatusState["Failure"] = "failure";
|
|
@@ -30,17 +18,17 @@ var GitHubCommitStatusState = /*#__PURE__*/function (GitHubCommitStatusState) {
|
|
|
30
18
|
GitHubCommitStatusState["Success"] = "success";
|
|
31
19
|
return GitHubCommitStatusState;
|
|
32
20
|
}(GitHubCommitStatusState || {});
|
|
33
|
-
let PullRequestState =
|
|
21
|
+
export let PullRequestState = /*#__PURE__*/function (PullRequestState) {
|
|
34
22
|
PullRequestState["Open"] = "open";
|
|
35
23
|
PullRequestState["Closed"] = "closed";
|
|
36
24
|
PullRequestState["All"] = "all";
|
|
37
25
|
return PullRequestState;
|
|
38
26
|
}({});
|
|
39
27
|
function withCmsLabel(pr, cmsLabelPrefix) {
|
|
40
|
-
return pr.labels.some(l =>
|
|
28
|
+
return pr.labels.some(l => isCMSLabel(l.name, cmsLabelPrefix));
|
|
41
29
|
}
|
|
42
30
|
function withoutCmsLabel(pr, cmsLabelPrefix) {
|
|
43
|
-
return pr.labels.every(l => !
|
|
31
|
+
return pr.labels.every(l => !isCMSLabel(l.name, cmsLabelPrefix));
|
|
44
32
|
}
|
|
45
33
|
function getTreeFiles(files) {
|
|
46
34
|
const treeFiles = files.reduce((arr, file) => {
|
|
@@ -73,56 +61,8 @@ function getTreeFiles(files) {
|
|
|
73
61
|
return treeFiles;
|
|
74
62
|
}
|
|
75
63
|
let migrationNotified = false;
|
|
76
|
-
class API {
|
|
64
|
+
export default class API {
|
|
77
65
|
constructor(config) {
|
|
78
|
-
_defineProperty(this, "apiRoot", void 0);
|
|
79
|
-
_defineProperty(this, "token", void 0);
|
|
80
|
-
_defineProperty(this, "tokenKeyword", void 0);
|
|
81
|
-
_defineProperty(this, "branch", void 0);
|
|
82
|
-
_defineProperty(this, "useOpenAuthoring", void 0);
|
|
83
|
-
_defineProperty(this, "repo", void 0);
|
|
84
|
-
_defineProperty(this, "originRepo", void 0);
|
|
85
|
-
_defineProperty(this, "repoOwner", void 0);
|
|
86
|
-
_defineProperty(this, "repoName", void 0);
|
|
87
|
-
_defineProperty(this, "originRepoOwner", void 0);
|
|
88
|
-
_defineProperty(this, "originRepoName", void 0);
|
|
89
|
-
_defineProperty(this, "repoURL", void 0);
|
|
90
|
-
_defineProperty(this, "originRepoURL", void 0);
|
|
91
|
-
_defineProperty(this, "mergeMethod", void 0);
|
|
92
|
-
_defineProperty(this, "initialWorkflowStatus", void 0);
|
|
93
|
-
_defineProperty(this, "cmsLabelPrefix", void 0);
|
|
94
|
-
_defineProperty(this, "baseUrl", void 0);
|
|
95
|
-
_defineProperty(this, "getUser", void 0);
|
|
96
|
-
_defineProperty(this, "_userPromise", void 0);
|
|
97
|
-
_defineProperty(this, "_metadataSemaphore", void 0);
|
|
98
|
-
_defineProperty(this, "commitAuthor", void 0);
|
|
99
|
-
_defineProperty(this, "filterOpenAuthoringBranches", async branch => {
|
|
100
|
-
try {
|
|
101
|
-
const pullRequest = await this.getBranchPullRequest(branch);
|
|
102
|
-
const {
|
|
103
|
-
state: currentState,
|
|
104
|
-
merged_at: mergedAt
|
|
105
|
-
} = pullRequest;
|
|
106
|
-
if (pullRequest.number !== MOCK_PULL_REQUEST && currentState === PullRequestState.Closed && mergedAt) {
|
|
107
|
-
// pr was merged, delete branch
|
|
108
|
-
await this.deleteBranch(branch);
|
|
109
|
-
return {
|
|
110
|
-
branch,
|
|
111
|
-
filter: false
|
|
112
|
-
};
|
|
113
|
-
} else {
|
|
114
|
-
return {
|
|
115
|
-
branch,
|
|
116
|
-
filter: true
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
} catch (e) {
|
|
120
|
-
return {
|
|
121
|
-
branch,
|
|
122
|
-
filter: false
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
66
|
this.apiRoot = config.apiRoot || 'https://api.github.com';
|
|
127
67
|
this.token = config.token || '';
|
|
128
68
|
this.tokenKeyword = config.tokenKeyword || 'token';
|
|
@@ -144,6 +84,7 @@ class API {
|
|
|
144
84
|
this.baseUrl = config.baseUrl;
|
|
145
85
|
this.getUser = config.getUser;
|
|
146
86
|
}
|
|
87
|
+
static DEFAULT_COMMIT_MESSAGE = 'Automatically generated by Decap CMS';
|
|
147
88
|
user() {
|
|
148
89
|
if (!this._userPromise) {
|
|
149
90
|
this._userPromise = this.getUser({
|
|
@@ -167,9 +108,10 @@ class API {
|
|
|
167
108
|
// no op
|
|
168
109
|
}
|
|
169
110
|
requestHeaders(headers = {}) {
|
|
170
|
-
const baseHeader =
|
|
171
|
-
'Content-Type': 'application/json; charset=utf-8'
|
|
172
|
-
|
|
111
|
+
const baseHeader = {
|
|
112
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
113
|
+
...headers
|
|
114
|
+
};
|
|
173
115
|
if (this.token) {
|
|
174
116
|
baseHeader.Authorization = `${this.tokenKeyword} ${this.token}`;
|
|
175
117
|
return Promise.resolve(baseHeader);
|
|
@@ -210,23 +152,25 @@ class API {
|
|
|
210
152
|
return textPromise;
|
|
211
153
|
}
|
|
212
154
|
handleRequestError(error, responseStatus) {
|
|
213
|
-
throw new
|
|
155
|
+
throw new APIError(error.message, responseStatus, API_NAME);
|
|
214
156
|
}
|
|
215
157
|
buildRequest(req) {
|
|
216
158
|
return req;
|
|
217
159
|
}
|
|
218
160
|
async request(path, options = {}, parser = response => this.parseResponse(response)) {
|
|
219
|
-
options =
|
|
220
|
-
cache: 'no-cache'
|
|
221
|
-
|
|
161
|
+
options = {
|
|
162
|
+
cache: 'no-cache',
|
|
163
|
+
...options
|
|
164
|
+
};
|
|
222
165
|
const headers = await this.requestHeaders(options.headers || {});
|
|
223
166
|
const url = this.urlFor(path, options);
|
|
224
167
|
let responseStatus = 500;
|
|
225
168
|
try {
|
|
226
|
-
const req =
|
|
169
|
+
const req = unsentRequest.fromFetchArguments(url, {
|
|
170
|
+
...options,
|
|
227
171
|
headers
|
|
228
|
-
})
|
|
229
|
-
const response = await
|
|
172
|
+
});
|
|
173
|
+
const response = await requestWithBackoff(this, req);
|
|
230
174
|
responseStatus = response.status;
|
|
231
175
|
const parsedResponse = await parser(response);
|
|
232
176
|
return parsedResponse;
|
|
@@ -238,19 +182,21 @@ class API {
|
|
|
238
182
|
return url => url;
|
|
239
183
|
}
|
|
240
184
|
async requestAllPages(url, options = {}) {
|
|
241
|
-
options =
|
|
242
|
-
cache: 'no-cache'
|
|
243
|
-
|
|
185
|
+
options = {
|
|
186
|
+
cache: 'no-cache',
|
|
187
|
+
...options
|
|
188
|
+
};
|
|
244
189
|
const headers = await this.requestHeaders(options.headers || {});
|
|
245
190
|
const processedURL = this.urlFor(url, options);
|
|
246
|
-
const allResponses = await
|
|
191
|
+
const allResponses = await getAllResponses(processedURL, {
|
|
192
|
+
...options,
|
|
247
193
|
headers
|
|
248
|
-
}
|
|
194
|
+
}, 'next', this.nextUrlProcessor());
|
|
249
195
|
const pages = await Promise.all(allResponses.map(res => this.parseResponse(res)));
|
|
250
196
|
return [].concat(...pages);
|
|
251
197
|
}
|
|
252
198
|
generateContentKey(collectionName, slug) {
|
|
253
|
-
const contentKey =
|
|
199
|
+
const contentKey = generateContentKey(collectionName, slug);
|
|
254
200
|
if (!this.useOpenAuthoring) {
|
|
255
201
|
return contentKey;
|
|
256
202
|
}
|
|
@@ -258,9 +204,9 @@ class API {
|
|
|
258
204
|
}
|
|
259
205
|
parseContentKey(contentKey) {
|
|
260
206
|
if (!this.useOpenAuthoring) {
|
|
261
|
-
return
|
|
207
|
+
return parseContentKey(contentKey);
|
|
262
208
|
}
|
|
263
|
-
return
|
|
209
|
+
return parseContentKey(contentKey.slice(this.repo.length + 1));
|
|
264
210
|
}
|
|
265
211
|
checkMetadataRef() {
|
|
266
212
|
return this.request(`${this.repoURL}/git/refs/meta/_decap_cms`).then(response => response.object).catch(() => {
|
|
@@ -286,65 +232,56 @@ class API {
|
|
|
286
232
|
// calls to storeMetadata are not. concurrent metadata updates
|
|
287
233
|
// will result in the metadata branch being unable to update.
|
|
288
234
|
if (!this._metadataSemaphore) {
|
|
289
|
-
this._metadataSemaphore = (
|
|
235
|
+
this._metadataSemaphore = semaphore(1);
|
|
290
236
|
}
|
|
291
|
-
return new Promise((resolve, reject) => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
reject(err);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
});
|
|
237
|
+
return new Promise((resolve, reject) => this._metadataSemaphore?.take(async () => {
|
|
238
|
+
try {
|
|
239
|
+
const branchData = await this.checkMetadataRef();
|
|
240
|
+
const file = {
|
|
241
|
+
path: `${key}.json`,
|
|
242
|
+
raw: JSON.stringify(data)
|
|
243
|
+
};
|
|
244
|
+
await this.uploadBlob(file);
|
|
245
|
+
const changeTree = await this.updateTree(branchData.sha, [file]);
|
|
246
|
+
const {
|
|
247
|
+
sha
|
|
248
|
+
} = await this.commit(`Updating “${key}” metadata`, changeTree);
|
|
249
|
+
await this.patchRef('meta', '_decap_cms', sha);
|
|
250
|
+
await localForage.setItem(`gh.meta.${key}`, {
|
|
251
|
+
expires: Date.now() + 300000,
|
|
252
|
+
// In 5 minutes
|
|
253
|
+
data
|
|
254
|
+
});
|
|
255
|
+
this._metadataSemaphore?.leave();
|
|
256
|
+
resolve();
|
|
257
|
+
} catch (err) {
|
|
258
|
+
reject(err);
|
|
259
|
+
}
|
|
260
|
+
}));
|
|
319
261
|
}
|
|
320
262
|
deleteMetadata(key) {
|
|
321
263
|
if (!this._metadataSemaphore) {
|
|
322
|
-
this._metadataSemaphore = (
|
|
264
|
+
this._metadataSemaphore = semaphore(1);
|
|
323
265
|
}
|
|
324
|
-
return new Promise(resolve => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
(_this$_metadataSemaph5 = this._metadataSemaphore) === null || _this$_metadataSemaph5 === void 0 ? void 0 : _this$_metadataSemaph5.leave();
|
|
344
|
-
resolve();
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
});
|
|
266
|
+
return new Promise(resolve => this._metadataSemaphore?.take(async () => {
|
|
267
|
+
try {
|
|
268
|
+
const branchData = await this.checkMetadataRef();
|
|
269
|
+
const file = {
|
|
270
|
+
path: `${key}.json`,
|
|
271
|
+
sha: null
|
|
272
|
+
};
|
|
273
|
+
const changeTree = await this.updateTree(branchData.sha, [file]);
|
|
274
|
+
const {
|
|
275
|
+
sha
|
|
276
|
+
} = await this.commit(`Deleting “${key}” metadata`, changeTree);
|
|
277
|
+
await this.patchRef('meta', '_decap_cms', sha);
|
|
278
|
+
this._metadataSemaphore?.leave();
|
|
279
|
+
resolve();
|
|
280
|
+
} catch (err) {
|
|
281
|
+
this._metadataSemaphore?.leave();
|
|
282
|
+
resolve();
|
|
283
|
+
}
|
|
284
|
+
}));
|
|
348
285
|
}
|
|
349
286
|
async retrieveMetadataOld(key) {
|
|
350
287
|
console.log('%c Checking for MetaData files', 'line-height: 30px;text-align: center;font-weight: bold');
|
|
@@ -372,15 +309,16 @@ class API {
|
|
|
372
309
|
}
|
|
373
310
|
async getPullRequests(head, state, predicate) {
|
|
374
311
|
const pullRequests = await this.requestAllPages(`${this.originRepoURL}/pulls`, {
|
|
375
|
-
params:
|
|
376
|
-
head
|
|
377
|
-
|
|
312
|
+
params: {
|
|
313
|
+
...(head ? {
|
|
314
|
+
head: await this.getHeadReference(head)
|
|
315
|
+
} : {}),
|
|
378
316
|
base: this.branch,
|
|
379
317
|
state,
|
|
380
318
|
per_page: 100
|
|
381
|
-
}
|
|
319
|
+
}
|
|
382
320
|
});
|
|
383
|
-
return pullRequests.filter(pr => pr.head.ref.startsWith(`${
|
|
321
|
+
return pullRequests.filter(pr => pr.head.ref.startsWith(`${CMS_BRANCH_PREFIX}/`) && predicate(pr));
|
|
384
322
|
}
|
|
385
323
|
async getOpenAuthoringPullRequest(branch, pullRequests) {
|
|
386
324
|
// we can't use labels when using open authoring
|
|
@@ -388,7 +326,7 @@ class API {
|
|
|
388
326
|
// a branch without a pr (or a closed pr) means a 'draft' entry
|
|
389
327
|
// a branch with an opened pr means a 'pending_review' entry
|
|
390
328
|
const data = await this.getBranch(branch).catch(() => {
|
|
391
|
-
throw new
|
|
329
|
+
throw new EditorialWorkflowError('content is not under editorial workflow', true);
|
|
392
330
|
});
|
|
393
331
|
// since we get all (open and closed) pull requests by branch name, make sure to filter by head sha
|
|
394
332
|
const pullRequest = pullRequests.filter(pr => pr.head.sha === data.commit.sha)[0];
|
|
@@ -401,19 +339,19 @@ class API {
|
|
|
401
339
|
},
|
|
402
340
|
number: MOCK_PULL_REQUEST,
|
|
403
341
|
labels: [{
|
|
404
|
-
name:
|
|
342
|
+
name: statusToLabel(this.initialWorkflowStatus, this.cmsLabelPrefix)
|
|
405
343
|
}],
|
|
406
344
|
state: PullRequestState.Open
|
|
407
345
|
};
|
|
408
346
|
} catch (e) {
|
|
409
|
-
throw new
|
|
347
|
+
throw new EditorialWorkflowError('content is not under editorial workflow', true);
|
|
410
348
|
}
|
|
411
349
|
} else {
|
|
412
|
-
pullRequest.labels = pullRequest.labels.filter(l => !
|
|
350
|
+
pullRequest.labels = pullRequest.labels.filter(l => !isCMSLabel(l.name, this.cmsLabelPrefix));
|
|
413
351
|
const cmsLabel = pullRequest.state === PullRequestState.Closed ? {
|
|
414
|
-
name:
|
|
352
|
+
name: statusToLabel(this.initialWorkflowStatus, this.cmsLabelPrefix)
|
|
415
353
|
} : {
|
|
416
|
-
name:
|
|
354
|
+
name: statusToLabel('pending_review', this.cmsLabelPrefix)
|
|
417
355
|
};
|
|
418
356
|
pullRequest.labels.push(cmsLabel);
|
|
419
357
|
return pullRequest;
|
|
@@ -426,7 +364,7 @@ class API {
|
|
|
426
364
|
} else {
|
|
427
365
|
const pullRequests = await this.getPullRequests(branch, PullRequestState.Open, pr => withCmsLabel(pr, this.cmsLabelPrefix));
|
|
428
366
|
if (pullRequests.length <= 0) {
|
|
429
|
-
throw new
|
|
367
|
+
throw new EditorialWorkflowError('content is not under editorial workflow', true);
|
|
430
368
|
}
|
|
431
369
|
return pullRequests[0];
|
|
432
370
|
}
|
|
@@ -444,8 +382,7 @@ class API {
|
|
|
444
382
|
}
|
|
445
383
|
}
|
|
446
384
|
async getPullRequestAuthor(pullRequest) {
|
|
447
|
-
|
|
448
|
-
if (!((_pullRequest$user = pullRequest.user) !== null && _pullRequest$user !== void 0 && _pullRequest$user.login)) {
|
|
385
|
+
if (!pullRequest.user?.login) {
|
|
449
386
|
return;
|
|
450
387
|
}
|
|
451
388
|
try {
|
|
@@ -460,14 +397,14 @@ class API {
|
|
|
460
397
|
collection,
|
|
461
398
|
slug
|
|
462
399
|
} = this.parseContentKey(contentKey);
|
|
463
|
-
const branch =
|
|
400
|
+
const branch = branchFromContentKey(contentKey);
|
|
464
401
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
465
402
|
const [{
|
|
466
403
|
files
|
|
467
404
|
}, pullRequestAuthor] = await Promise.all([this.getDifferences(this.branch, pullRequest.head.sha), this.getPullRequestAuthor(pullRequest)]);
|
|
468
405
|
const diffs = await Promise.all(files.map(file => this.diffFromFile(file)));
|
|
469
|
-
const label = pullRequest.labels.find(l =>
|
|
470
|
-
const status =
|
|
406
|
+
const label = pullRequest.labels.find(l => isCMSLabel(l.name, this.cmsLabelPrefix));
|
|
407
|
+
const status = labelToStatus(label.name, this.cmsLabelPrefix);
|
|
471
408
|
const updatedAt = pullRequest.updated_at;
|
|
472
409
|
return {
|
|
473
410
|
collection,
|
|
@@ -523,7 +460,7 @@ class API {
|
|
|
523
460
|
};
|
|
524
461
|
}
|
|
525
462
|
};
|
|
526
|
-
const fileMetadata = await
|
|
463
|
+
const fileMetadata = await readFileMetadata(sha, fetchFileMetadata, localForage);
|
|
527
464
|
return fileMetadata;
|
|
528
465
|
}
|
|
529
466
|
async fetchBlobContent({
|
|
@@ -536,11 +473,11 @@ class API {
|
|
|
536
473
|
});
|
|
537
474
|
if (parseText) {
|
|
538
475
|
// treat content as a utf-8 string
|
|
539
|
-
const content =
|
|
476
|
+
const content = Base64.decode(result.content);
|
|
540
477
|
return content;
|
|
541
478
|
} else {
|
|
542
479
|
// treat content as binary and convert to blob
|
|
543
|
-
const content =
|
|
480
|
+
const content = Base64.atob(result.content);
|
|
544
481
|
const byteArray = new Uint8Array(content.length);
|
|
545
482
|
for (let i = 0; i < content.length; i++) {
|
|
546
483
|
byteArray[i] = content.charCodeAt(i);
|
|
@@ -554,7 +491,7 @@ class API {
|
|
|
554
491
|
branch = this.branch,
|
|
555
492
|
depth = 1
|
|
556
493
|
} = {}) {
|
|
557
|
-
const folder = (
|
|
494
|
+
const folder = trim(path, '/');
|
|
558
495
|
try {
|
|
559
496
|
const result = await this.request(`${repoURL}/git/trees/${branch}:${folder}`, {
|
|
560
497
|
// GitHub API supports recursive=1 for getting the entire recursive tree
|
|
@@ -568,7 +505,7 @@ class API {
|
|
|
568
505
|
.filter(file => file.type === 'blob' && file.path.split('/').length <= depth).map(file => ({
|
|
569
506
|
type: file.type,
|
|
570
507
|
id: file.sha,
|
|
571
|
-
name:
|
|
508
|
+
name: basename(file.path),
|
|
572
509
|
path: `${folder}/${file.path}`,
|
|
573
510
|
size: file.size
|
|
574
511
|
}));
|
|
@@ -581,6 +518,33 @@ class API {
|
|
|
581
518
|
}
|
|
582
519
|
}
|
|
583
520
|
}
|
|
521
|
+
filterOpenAuthoringBranches = async branch => {
|
|
522
|
+
try {
|
|
523
|
+
const pullRequest = await this.getBranchPullRequest(branch);
|
|
524
|
+
const {
|
|
525
|
+
state: currentState,
|
|
526
|
+
merged_at: mergedAt
|
|
527
|
+
} = pullRequest;
|
|
528
|
+
if (pullRequest.number !== MOCK_PULL_REQUEST && currentState === PullRequestState.Closed && mergedAt) {
|
|
529
|
+
// pr was merged, delete branch
|
|
530
|
+
await this.deleteBranch(branch);
|
|
531
|
+
return {
|
|
532
|
+
branch,
|
|
533
|
+
filter: false
|
|
534
|
+
};
|
|
535
|
+
} else {
|
|
536
|
+
return {
|
|
537
|
+
branch,
|
|
538
|
+
filter: true
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
} catch (e) {
|
|
542
|
+
return {
|
|
543
|
+
branch,
|
|
544
|
+
filter: false
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
};
|
|
584
548
|
async migrateToVersion1(pullRequest, metadata) {
|
|
585
549
|
// hard code key/branch generation logic to ignore future changes
|
|
586
550
|
const oldContentKey = pullRequest.head.ref.slice(`cms/`.length);
|
|
@@ -595,14 +559,15 @@ class API {
|
|
|
595
559
|
const pr = (await this.getPullRequests(newBranchName, PullRequestState.All, () => true))[0] || (await this.createPR(pullRequest.title, newBranchName));
|
|
596
560
|
|
|
597
561
|
// store new metadata
|
|
598
|
-
const newMetadata =
|
|
562
|
+
const newMetadata = {
|
|
563
|
+
...metadata,
|
|
599
564
|
pr: {
|
|
600
565
|
number: pr.number,
|
|
601
566
|
head: pr.head.sha
|
|
602
567
|
},
|
|
603
568
|
branch: newBranchName,
|
|
604
569
|
version: '1'
|
|
605
|
-
}
|
|
570
|
+
};
|
|
606
571
|
await this.storeMetadata(newContentKey, newMetadata);
|
|
607
572
|
|
|
608
573
|
// remove old data
|
|
@@ -624,7 +589,7 @@ class API {
|
|
|
624
589
|
number
|
|
625
590
|
} = pullRequest;
|
|
626
591
|
console.log(`Migrating Pull Request '${number}' (${countMessage})`);
|
|
627
|
-
const contentKey =
|
|
592
|
+
const contentKey = contentKeyFromBranch(pullRequest.head.ref);
|
|
628
593
|
let metadata = await this.retrieveMetadataOld(contentKey).catch(() => undefined);
|
|
629
594
|
if (!metadata) {
|
|
630
595
|
console.log(`Skipped migrating Pull Request '${number}' (${countMessage})`);
|
|
@@ -676,7 +641,7 @@ class API {
|
|
|
676
641
|
for (const pr of pullRequests) {
|
|
677
642
|
if (!migrationNotified) {
|
|
678
643
|
migrationNotified = true;
|
|
679
|
-
alert(
|
|
644
|
+
alert(oneLine`
|
|
680
645
|
Decap CMS is adding labels to ${pullRequests.length} of your Editorial Workflow
|
|
681
646
|
entries. The "Workflow" tab will be unavailable during this migration. You may use other
|
|
682
647
|
areas of the CMS during this time. Note that closing the CMS will pause the migration.
|
|
@@ -697,14 +662,14 @@ class API {
|
|
|
697
662
|
*/
|
|
698
663
|
async getStatuses(collectionName, slug) {
|
|
699
664
|
const contentKey = this.generateContentKey(collectionName, slug);
|
|
700
|
-
const branch =
|
|
665
|
+
const branch = branchFromContentKey(contentKey);
|
|
701
666
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
702
667
|
const sha = pullRequest.head.sha;
|
|
703
668
|
const resp = await this.request(`${this.originRepoURL}/commits/${sha}/status`);
|
|
704
669
|
return resp.statuses.map(s => ({
|
|
705
670
|
context: s.context,
|
|
706
671
|
target_url: s.target_url,
|
|
707
|
-
state: s.state === GitHubCommitStatusState.Success ?
|
|
672
|
+
state: s.state === GitHubCommitStatusState.Success ? PreviewState.Success : PreviewState.Other
|
|
708
673
|
}));
|
|
709
674
|
}
|
|
710
675
|
async persistFiles(dataFiles, mediaFiles, options) {
|
|
@@ -718,7 +683,7 @@ class API {
|
|
|
718
683
|
sha,
|
|
719
684
|
path
|
|
720
685
|
}) => ({
|
|
721
|
-
path: (
|
|
686
|
+
path: trimStart(path, '/'),
|
|
722
687
|
sha
|
|
723
688
|
}));
|
|
724
689
|
const slug = dataFiles[0].slug;
|
|
@@ -736,8 +701,8 @@ class API {
|
|
|
736
701
|
*/
|
|
737
702
|
|
|
738
703
|
const pathArray = path.split('/');
|
|
739
|
-
const filename = (
|
|
740
|
-
const directory = (
|
|
704
|
+
const filename = last(pathArray);
|
|
705
|
+
const directory = initial(pathArray).join('/');
|
|
741
706
|
const fileDataPath = encodeURIComponent(directory);
|
|
742
707
|
const fileDataURL = `${repoURL}/git/trees/${branch}:${fileDataPath}`;
|
|
743
708
|
const result = await this.request(fileDataURL);
|
|
@@ -745,7 +710,7 @@ class API {
|
|
|
745
710
|
if (file) {
|
|
746
711
|
return file.sha;
|
|
747
712
|
} else {
|
|
748
|
-
throw new
|
|
713
|
+
throw new APIError('Not Found', 404, API_NAME);
|
|
749
714
|
}
|
|
750
715
|
}
|
|
751
716
|
async deleteFiles(paths, message) {
|
|
@@ -787,7 +752,7 @@ class API {
|
|
|
787
752
|
}
|
|
788
753
|
async editorialWorkflowGit(files, slug, mediaFilesList, options) {
|
|
789
754
|
const contentKey = this.generateContentKey(options.collectionName, slug);
|
|
790
|
-
const branch =
|
|
755
|
+
const branch = branchFromContentKey(contentKey);
|
|
791
756
|
const unpublished = options.unpublished || false;
|
|
792
757
|
if (!unpublished) {
|
|
793
758
|
const branchData = await this.getDefaultBranch();
|
|
@@ -841,7 +806,7 @@ class API {
|
|
|
841
806
|
await new Promise(resolve => setTimeout(resolve, i * 500));
|
|
842
807
|
}
|
|
843
808
|
}
|
|
844
|
-
throw new
|
|
809
|
+
throw new APIError('Not Found', 404, API_NAME);
|
|
845
810
|
}
|
|
846
811
|
async rebaseSingleCommit(baseCommit, commit) {
|
|
847
812
|
// first get the diff between the commits
|
|
@@ -875,7 +840,7 @@ class API {
|
|
|
875
840
|
* return commits as is.
|
|
876
841
|
*/
|
|
877
842
|
if (commits.length === 0 || commits[0].parents[0].sha === baseCommit.sha) {
|
|
878
|
-
const head = (
|
|
843
|
+
const head = last(commits);
|
|
879
844
|
return head;
|
|
880
845
|
} else {
|
|
881
846
|
/**
|
|
@@ -909,12 +874,12 @@ class API {
|
|
|
909
874
|
}
|
|
910
875
|
}
|
|
911
876
|
async setPullRequestStatus(pullRequest, newStatus) {
|
|
912
|
-
const labels = [...pullRequest.labels.filter(label => !
|
|
877
|
+
const labels = [...pullRequest.labels.filter(label => !isCMSLabel(label.name, this.cmsLabelPrefix)).map(l => l.name), statusToLabel(newStatus, this.cmsLabelPrefix)];
|
|
913
878
|
await this.updatePullRequestLabels(pullRequest.number, labels);
|
|
914
879
|
}
|
|
915
880
|
async updateUnpublishedEntryStatus(collectionName, slug, newStatus) {
|
|
916
881
|
const contentKey = this.generateContentKey(collectionName, slug);
|
|
917
|
-
const branch =
|
|
882
|
+
const branch = branchFromContentKey(contentKey);
|
|
918
883
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
919
884
|
if (!this.useOpenAuthoring) {
|
|
920
885
|
await this.setPullRequestStatus(pullRequest, newStatus);
|
|
@@ -933,18 +898,17 @@ class API {
|
|
|
933
898
|
await this.openPR(pullRequest.number);
|
|
934
899
|
}
|
|
935
900
|
} else if (newStatus === 'pending_review') {
|
|
936
|
-
|
|
937
|
-
const branch = (0, _decapCmsLibUtil.branchFromContentKey)(contentKey);
|
|
901
|
+
const branch = branchFromContentKey(contentKey);
|
|
938
902
|
// get the first commit message as the pr title
|
|
939
903
|
const diff = await this.getDifferences(this.branch, await this.getHeadReference(branch));
|
|
940
|
-
const title =
|
|
904
|
+
const title = diff.commits[0]?.commit?.message || API.DEFAULT_COMMIT_MESSAGE;
|
|
941
905
|
await this.createPR(title, branch);
|
|
942
906
|
}
|
|
943
907
|
}
|
|
944
908
|
}
|
|
945
909
|
async deleteUnpublishedEntry(collectionName, slug) {
|
|
946
910
|
const contentKey = this.generateContentKey(collectionName, slug);
|
|
947
|
-
const branch =
|
|
911
|
+
const branch = branchFromContentKey(contentKey);
|
|
948
912
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
949
913
|
if (pullRequest.number !== MOCK_PULL_REQUEST) {
|
|
950
914
|
await this.closePR(pullRequest.number);
|
|
@@ -953,7 +917,7 @@ class API {
|
|
|
953
917
|
}
|
|
954
918
|
async publishUnpublishedEntry(collectionName, slug) {
|
|
955
919
|
const contentKey = this.generateContentKey(collectionName, slug);
|
|
956
|
-
const branch =
|
|
920
|
+
const branch = branchFromContentKey(contentKey);
|
|
957
921
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
958
922
|
await this.mergePR(pullRequest);
|
|
959
923
|
await this.deleteBranch(branch);
|
|
@@ -995,7 +959,7 @@ class API {
|
|
|
995
959
|
async backupBranch(branchName) {
|
|
996
960
|
try {
|
|
997
961
|
const existingBranch = await this.getBranch(branchName);
|
|
998
|
-
await this.createBranch(existingBranch.name.replace(new RegExp(`${
|
|
962
|
+
await this.createBranch(existingBranch.name.replace(new RegExp(`${CMS_BRANCH_PREFIX}/`), `${CMS_BRANCH_PREFIX}_${Date.now()}/`), existingBranch.commit.sha);
|
|
999
963
|
} catch (e) {
|
|
1000
964
|
console.warn(e);
|
|
1001
965
|
}
|
|
@@ -1007,8 +971,8 @@ class API {
|
|
|
1007
971
|
} catch (e) {
|
|
1008
972
|
const message = String(e.message || '');
|
|
1009
973
|
if (message === 'Reference update failed') {
|
|
1010
|
-
await
|
|
1011
|
-
} else if (message === 'Reference already exists' && branchName.startsWith(`${
|
|
974
|
+
await throwOnConflictingBranches(branchName, name => this.getBranch(name), API_NAME);
|
|
975
|
+
} else if (message === 'Reference already exists' && branchName.startsWith(`${CMS_BRANCH_PREFIX}/`)) {
|
|
1012
976
|
try {
|
|
1013
977
|
// this can happen if the branch wasn't deleted when the PR was merged
|
|
1014
978
|
// we backup the existing branch just in case and patch it with the new sha
|
|
@@ -1025,7 +989,7 @@ class API {
|
|
|
1025
989
|
}
|
|
1026
990
|
}
|
|
1027
991
|
assertCmsBranch(branchName) {
|
|
1028
|
-
return branchName.startsWith(`${
|
|
992
|
+
return branchName.startsWith(`${CMS_BRANCH_PREFIX}/`);
|
|
1029
993
|
}
|
|
1030
994
|
patchBranch(branchName, sha, opts = {}) {
|
|
1031
995
|
const force = opts.force || false;
|
|
@@ -1056,7 +1020,7 @@ class API {
|
|
|
1056
1020
|
method: 'POST',
|
|
1057
1021
|
body: JSON.stringify({
|
|
1058
1022
|
title,
|
|
1059
|
-
body:
|
|
1023
|
+
body: DEFAULT_PR_BODY,
|
|
1060
1024
|
head: await this.getHeadReference(head),
|
|
1061
1025
|
base: this.branch
|
|
1062
1026
|
})
|
|
@@ -1089,14 +1053,14 @@ class API {
|
|
|
1089
1053
|
const result = await this.request(`${this.originRepoURL}/pulls/${pullrequest.number}/merge`, {
|
|
1090
1054
|
method: 'PUT',
|
|
1091
1055
|
body: JSON.stringify({
|
|
1092
|
-
commit_message:
|
|
1056
|
+
commit_message: MERGE_COMMIT_MESSAGE,
|
|
1093
1057
|
sha: pullrequest.head.sha,
|
|
1094
1058
|
merge_method: this.mergeMethod
|
|
1095
1059
|
})
|
|
1096
1060
|
});
|
|
1097
1061
|
return result;
|
|
1098
1062
|
} catch (error) {
|
|
1099
|
-
if (error instanceof
|
|
1063
|
+
if (error instanceof APIError && error.status === 405) {
|
|
1100
1064
|
return this.forceMergePR(pullrequest);
|
|
1101
1065
|
} else {
|
|
1102
1066
|
throw error;
|
|
@@ -1114,10 +1078,10 @@ class API {
|
|
|
1114
1078
|
return this.getDefaultBranch().then(branchData => this.updateTree(branchData.commit.sha, files)).then(changeTree => this.commit(commitMessage, changeTree)).then(response => this.patchBranch(this.branch, response.sha));
|
|
1115
1079
|
}
|
|
1116
1080
|
toBase64(str) {
|
|
1117
|
-
return Promise.resolve(
|
|
1081
|
+
return Promise.resolve(Base64.encode(str));
|
|
1118
1082
|
}
|
|
1119
1083
|
async uploadBlob(item) {
|
|
1120
|
-
const contentBase64 = await (
|
|
1084
|
+
const contentBase64 = await result(item, 'toBase64', partial(this.toBase64, item.raw));
|
|
1121
1085
|
const response = await this.request(`${this.repoURL}/git/blobs`, {
|
|
1122
1086
|
method: 'POST',
|
|
1123
1087
|
body: JSON.stringify({
|
|
@@ -1132,7 +1096,7 @@ class API {
|
|
|
1132
1096
|
const toMove = [];
|
|
1133
1097
|
const tree = files.reduce((acc, file) => {
|
|
1134
1098
|
const entry = {
|
|
1135
|
-
path: (
|
|
1099
|
+
path: trimStart(file.path, '/'),
|
|
1136
1100
|
mode: '100644',
|
|
1137
1101
|
type: 'blob',
|
|
1138
1102
|
sha: file.sha
|
|
@@ -1153,8 +1117,8 @@ class API {
|
|
|
1153
1117
|
to,
|
|
1154
1118
|
sha
|
|
1155
1119
|
} of toMove) {
|
|
1156
|
-
const sourceDir =
|
|
1157
|
-
const destDir =
|
|
1120
|
+
const sourceDir = dirname(from);
|
|
1121
|
+
const destDir = dirname(to);
|
|
1158
1122
|
const files = await this.listFiles(sourceDir, {
|
|
1159
1123
|
branch,
|
|
1160
1124
|
depth: 100
|
|
@@ -1177,9 +1141,10 @@ class API {
|
|
|
1177
1141
|
}
|
|
1178
1142
|
}
|
|
1179
1143
|
const newTree = await this.createTree(baseSha, tree);
|
|
1180
|
-
return
|
|
1144
|
+
return {
|
|
1145
|
+
...newTree,
|
|
1181
1146
|
parentSha: baseSha
|
|
1182
|
-
}
|
|
1147
|
+
};
|
|
1183
1148
|
}
|
|
1184
1149
|
async createTree(baseSha, tree) {
|
|
1185
1150
|
const result = await this.request(`${this.repoURL}/git/trees`, {
|
|
@@ -1210,10 +1175,8 @@ class API {
|
|
|
1210
1175
|
}
|
|
1211
1176
|
async getUnpublishedEntrySha(collection, slug) {
|
|
1212
1177
|
const contentKey = this.generateContentKey(collection, slug);
|
|
1213
|
-
const branch =
|
|
1178
|
+
const branch = branchFromContentKey(contentKey);
|
|
1214
1179
|
const pullRequest = await this.getBranchPullRequest(branch);
|
|
1215
1180
|
return pullRequest.head.sha;
|
|
1216
1181
|
}
|
|
1217
|
-
}
|
|
1218
|
-
exports.default = API;
|
|
1219
|
-
_defineProperty(API, "DEFAULT_COMMIT_MESSAGE", 'Automatically generated by Decap CMS');
|
|
1182
|
+
}
|