Skip to content

Commit a9271b8

Browse files
committed
Initial Commit
0 parents  commit a9271b8

File tree

6 files changed

+5887
-0
lines changed

6 files changed

+5887
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# octokit-commit-multiple-files
2+
3+
This plugin is an alternative to using `octokit.repos.createOrUpdateFile` which allows you to edit the contents of a single file.
4+
5+
## Installation
6+
7+
```bash
8+
npm install octokit-commit-multiple-files --save
9+
```
10+
11+
## Usage
12+
13+
This plugin accepts `owner`, `repo`, `path`, `branch` and `message` like `createOrUpdateFile`.
14+
15+
In addition, it accepts `changes` which is an array of objects containing a `path` and the file `contents`.
16+
17+
It also accepts a `newBranch` parameter which will commit these changes to a different branch than the one provided. You can set `overwriteBranch` to `true` to use `branch` as the base commit, or `false` to use the latest commit on `newBranch` as the base commit.
18+
19+
```javascript
20+
const Octokit = require("@octokit/rest").plugin(require("."));
21+
const octokit = new Octokit();
22+
23+
const branchName = await octokit.repos.createOrUpdateFiles({
24+
owner,
25+
repo,
26+
path,
27+
branch,
28+
message,
29+
changes: [
30+
{
31+
path: "test.md",
32+
contents: "One"
33+
},
34+
{
35+
path: "test2.md",
36+
contents: "Two"
37+
}
38+
]
39+
})
40+
```
41+

create-or-update-files.js

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
module.exports = function(octokit, opts) {
2+
return new Promise(async (resolve, reject) => {
3+
// Up front validation
4+
try {
5+
for (let req of ["owner", "repo", "branch", "message"]) {
6+
if (!opts[req]) {
7+
return reject(`'${req}' is a required parameter`);
8+
}
9+
}
10+
11+
if (!opts.changes || !opts.changes.length) {
12+
return reject("No changes provided");
13+
}
14+
15+
// Destructuring for easier access later
16+
let {
17+
owner,
18+
repo,
19+
newBranch,
20+
branch: branchName,
21+
overwriteBranch,
22+
changes,
23+
message
24+
} = opts;
25+
26+
if (overwriteBranch && !newBranch) {
27+
return reject(
28+
"You can only overwrite a branch if you provide a 'newBranch'"
29+
);
30+
}
31+
32+
let branchAlreadyExists = true;
33+
let targetBranch = branchName;
34+
let baseTree;
35+
36+
// Does the target branch already exist?
37+
if (newBranch) {
38+
baseTree = await loadRef(octokit, owner, repo, newBranch);
39+
targetBranch = newBranch;
40+
if (!baseTree) {
41+
branchAlreadyExists = false;
42+
}
43+
}
44+
45+
// If it doesn't exist, or we want to replace the branch then
46+
// we grab the base sha from our target branch
47+
if (overwriteBranch || !baseTree) {
48+
baseTree = await loadRef(octokit, owner, repo, branchName);
49+
if (!baseTree) {
50+
return reject(
51+
`Unable to load branch. '${branchName}' does not exist`
52+
);
53+
}
54+
}
55+
56+
// Create blobs
57+
const treeItems = [];
58+
for (let change of changes) {
59+
if (!change.contents) {
60+
return reject(
61+
`No file contents provided for ${change.path || "Un-named file"}`
62+
);
63+
}
64+
65+
if (!change.path) {
66+
return reject(
67+
`No file path provided for the following contents: ${change.contents.substr(
68+
0,
69+
30
70+
)}...`
71+
);
72+
}
73+
74+
let file = (
75+
await octokit.git.createBlob({
76+
owner,
77+
repo,
78+
content: Buffer.from(change.contents).toString("base64"),
79+
encoding: "base64"
80+
})
81+
).data;
82+
83+
treeItems.push({
84+
path: change.path,
85+
sha: file.sha,
86+
mode: "100644",
87+
type: "blob"
88+
});
89+
}
90+
91+
// Add those blobs to a tree
92+
let tree = (
93+
await octokit.git.createTree({
94+
owner,
95+
repo,
96+
tree: treeItems,
97+
base_tree: baseTree
98+
})
99+
).data;
100+
101+
// Create a commit that points to that tree
102+
let commit = (
103+
await octokit.git.createCommit({
104+
owner,
105+
repo,
106+
message,
107+
tree: tree.sha,
108+
parents: [baseTree]
109+
})
110+
).data;
111+
112+
// Create a ref that points to that tree
113+
let action = "createRef";
114+
let updateRefBase = "refs/";
115+
116+
// Or if it already exists, we'll update that existing ref
117+
if (branchAlreadyExists) {
118+
action = "updateRef";
119+
updateRefBase = "";
120+
}
121+
122+
const branch = (
123+
await octokit.git[action]({
124+
owner,
125+
repo,
126+
force: true,
127+
ref: `${updateRefBase}heads/${targetBranch}`,
128+
sha: commit.sha
129+
})
130+
).data;
131+
132+
// Return the new branch name so that we can use it later
133+
// e.g. to create a pull request
134+
return resolve(targetBranch);
135+
} catch (e) {
136+
return reject(e);
137+
}
138+
});
139+
};
140+
141+
async function loadRef(octokit, owner, repo, ref) {
142+
try {
143+
return (
144+
await octokit.git.getRef({
145+
owner,
146+
repo,
147+
ref: `heads/${ref}`
148+
})
149+
).data.object.sha;
150+
} catch (e) {}
151+
}

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const plugin = require("./create-or-update-files");
2+
3+
module.exports = function(octokit) {
4+
octokit.repos.createOrUpdateFiles = plugin.bind(null, octokit);
5+
};

0 commit comments

Comments
 (0)