Skip to content

Commit 4ca361e

Browse files
committed
New changes format in request body
This allows us to provide files as an object and paves the way for supporting multiple commits in a single push
1 parent 2e114e5 commit 4ca361e

File tree

3 files changed

+115
-77
lines changed

3 files changed

+115
-77
lines changed

create-or-update-files.js

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module.exports = function(octokit, opts) {
22
return new Promise(async (resolve, reject) => {
33
// Up front validation
44
try {
5-
for (let req of ["owner", "repo", "branch", "message"]) {
5+
for (let req of ["owner", "repo", "branch"]) {
66
if (!opts[req]) {
77
return reject(`'${req}' is a required parameter`);
88
}
@@ -19,8 +19,7 @@ module.exports = function(octokit, opts) {
1919
base,
2020
branch: branchName,
2121
createBranch,
22-
changes,
23-
message
22+
changes
2423
} = opts;
2524

2625
let branchAlreadyExists = true;
@@ -59,58 +58,65 @@ module.exports = function(octokit, opts) {
5958
// Create blobs
6059
const treeItems = [];
6160
for (let change of changes) {
62-
if (!change.contents) {
63-
return reject(
64-
`No file contents provided for ${change.path || "Un-named file"}`
65-
);
61+
let message = change.message;
62+
if (!message) {
63+
return reject(`changes[].message is a required parameter`);
64+
}
65+
if (!change.files || Object.keys(change.files).length === 0) {
66+
return reject(`changes[].files is a required parameter`);
6667
}
6768

68-
if (!change.path) {
69-
return reject(
70-
`No file path provided for the following contents: ${change.contents.substr(
71-
0,
72-
30
73-
)}...`
74-
);
69+
for (let fileName in change.files) {
70+
let properties = change.files[fileName] || "";
71+
72+
let contents = properties.contents || properties;
73+
let mode = properties.mode || "100644";
74+
75+
if (!contents) {
76+
return reject(`No file contents provided for ${fileName}`);
77+
}
78+
79+
let file = (
80+
await octokit.git.createBlob({
81+
owner,
82+
repo,
83+
content: Buffer.from(contents).toString("base64"),
84+
encoding: "base64"
85+
})
86+
).data;
87+
88+
treeItems.push({
89+
path: fileName,
90+
sha: file.sha,
91+
mode: mode,
92+
type: "blob"
93+
});
7594
}
7695

77-
let file = (
78-
await octokit.git.createBlob({
96+
// Add those blobs to a tree
97+
let tree = (
98+
await octokit.git.createTree({
7999
owner,
80100
repo,
81-
content: Buffer.from(change.contents).toString("base64"),
82-
encoding: "base64"
101+
tree: treeItems,
102+
base_tree: baseTree
83103
})
84104
).data;
85105

86-
treeItems.push({
87-
path: change.path,
88-
sha: file.sha,
89-
mode: "100644",
90-
type: "blob"
91-
});
92-
}
93-
94-
// Add those blobs to a tree
95-
let tree = (
96-
await octokit.git.createTree({
97-
owner,
98-
repo,
99-
tree: treeItems,
100-
base_tree: baseTree
101-
})
102-
).data;
106+
// Create a commit that points to that tree
107+
let commit = (
108+
await octokit.git.createCommit({
109+
owner,
110+
repo,
111+
message,
112+
tree: tree.sha,
113+
parents: [baseTree]
114+
})
115+
).data;
103116

104-
// Create a commit that points to that tree
105-
let commit = (
106-
await octokit.git.createCommit({
107-
owner,
108-
repo,
109-
message,
110-
tree: tree.sha,
111-
parents: [baseTree]
112-
})
113-
).data;
117+
// Update the base tree if we have another commit to make
118+
baseTree = commit.sha;
119+
}
114120

115121
// Create a ref that points to that tree
116122
let action = "createRef";
@@ -128,7 +134,7 @@ module.exports = function(octokit, opts) {
128134
repo,
129135
force: true,
130136
ref: `${updateRefBase}heads/${branchName}`,
131-
sha: commit.sha
137+
sha: baseTree
132138
})
133139
).data;
134140

create-or-update-files.test.js

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,25 @@ const validRequest = {
1515
branch: "new-branch-name",
1616
createBranch: true,
1717
base: "base-branch-name",
18-
message: "Your commit message",
1918
changes: [
2019
{
21-
path: "test.md",
22-
contents: `# This is a test
23-
24-
I hope it works`
25-
},
26-
{
27-
path: "test2.md",
28-
contents: `Something else`
20+
message: "Your commit message",
21+
files: {
22+
"test.md": `# This is a test
23+
24+
I hope it works`,
25+
"test2.md": {
26+
contents: `Something else`
27+
}
28+
}
2929
}
3030
]
3131
};
3232

3333
// Destructuring for easier access later
34-
let {
35-
owner,
36-
repo,
37-
base,
38-
branch,
39-
createBranch,
40-
changes,
41-
message
42-
} = validRequest;
43-
44-
for (let req of ["owner", "repo", "branch", "message"]) {
34+
let { owner, repo, base, branch, createBranch, changes } = validRequest;
35+
36+
for (let req of ["owner", "repo", "branch"]) {
4537
const body = { ...validRequest };
4638
delete body[req];
4739
test(`missing parameter (${req})`, () => {
@@ -79,35 +71,73 @@ test(`branch does not exist, provided base does not exist`, async () => {
7971
);
8072
});
8173

82-
test(`no file contents provided (no title)`, async () => {
74+
test(`no commit message`, async () => {
75+
const repoDefaultBranch = "master";
76+
mockGetRef(branch, `sha-${branch}`, true);
77+
mockGetRef(base, `sha-${base}`, true);
78+
mockGetRef(repoDefaultBranch, `sha-${repoDefaultBranch}`, true);
79+
80+
const body = {
81+
...validRequest,
82+
changes: [
83+
{
84+
files: {
85+
"test.md": null
86+
}
87+
}
88+
]
89+
};
90+
await expect(run(body)).rejects.toEqual(
91+
`changes[].message is a required parameter`
92+
);
93+
});
94+
95+
test(`no files provided (empty object)`, async () => {
96+
const repoDefaultBranch = "master";
8397
mockGetRef(branch, `sha-${branch}`, true);
8498
mockGetRef(base, `sha-${base}`, true);
85-
const body = { ...validRequest, changes: [{}] };
99+
mockGetRef(repoDefaultBranch, `sha-${repoDefaultBranch}`, true);
100+
101+
const body = {
102+
...validRequest,
103+
changes: [{ message: "Test Commit", files: {} }]
104+
};
86105
await expect(run(body)).rejects.toEqual(
87-
`No file contents provided for Un-named file`
106+
`changes[].files is a required parameter`
88107
);
89108
});
90109

91-
test(`no file contents provided (with title)`, async () => {
110+
test(`no files provided (missing object)`, async () => {
111+
const repoDefaultBranch = "master";
92112
mockGetRef(branch, `sha-${branch}`, true);
93113
mockGetRef(base, `sha-${base}`, true);
94-
const body = { ...validRequest, changes: [{ path: "test.md" }] };
114+
mockGetRef(repoDefaultBranch, `sha-${repoDefaultBranch}`, true);
115+
116+
const body = { ...validRequest, changes: [{ message: "Test Commit" }] };
95117
await expect(run(body)).rejects.toEqual(
96-
`No file contents provided for test.md`
118+
`changes[].files is a required parameter`
97119
);
98120
});
99121

100-
test(`no file path provided`, async () => {
122+
test(`no file contents provided`, async () => {
123+
const repoDefaultBranch = "master";
101124
mockGetRef(branch, `sha-${branch}`, true);
102125
mockGetRef(base, `sha-${base}`, true);
126+
mockGetRef(repoDefaultBranch, `sha-${repoDefaultBranch}`, true);
127+
103128
const body = {
104129
...validRequest,
105130
changes: [
106-
{ contents: "I wonder how long this text can be before it gets cut off" }
131+
{
132+
message: "This is a test",
133+
files: {
134+
"test.md": null
135+
}
136+
}
107137
]
108138
};
109139
await expect(run(body)).rejects.toEqual(
110-
`No file path provided for the following contents: I wonder how long this text ca...`
140+
`No file contents provided for test.md`
111141
);
112142
});
113143

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{
22
"name": "octokit-commit-multiple-files",
3-
"version": "2.0.0",
3+
"version": "3.0.0",
44
"description": "Octokit plugin to create/update multiple files at once",
55
"main": "index.js",
66
"scripts": {
7-
"test": "jest"
7+
"test": "jest",
8+
"lint": "eslint *.js",
9+
"lint-fix": "eslint --fix *.js"
810
},
911
"keywords": [
1012
"github",

0 commit comments

Comments
 (0)