Skip to content

Commit c4f887d

Browse files
authored
Merge pull request #40 from DSACMS/create-pr-from-form
Add Button and Functionality to Automatically Create a PR adding code.json to Project
2 parents a7d1a21 + 3f58591 commit c4f887d

File tree

4 files changed

+232
-1
lines changed

4 files changed

+232
-1
lines changed

.github/workflows/snyk-security.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
# Runs Snyk Code (SAST) analysis and uploads result into GitHub.
5555
# Use || true to not fail the pipeline
5656
- name: Snyk Code test
57-
run: snyk code test --sarif > snyk-code.sarif # || true
57+
run: snyk code test -d --sarif > snyk-code.sarif # || true
5858

5959
# Runs Snyk Open Source (SCA) analysis and uploads result to Snyk.
6060
- name: Snyk Open Source monitor

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<textarea class="form-control" rows="10" id="json-result" readonly></textarea>
7878
<button type="button" class="btn btn-outline" href="#" onclick="copyToClipboard(event)">Copy</button>
7979
<button type="button" class="btn btn-outline" href="#" onclick="downloadFile(event)">Download</button>
80+
<button type="button" class="btn btn-outline" href="#" onclick="createProjectPR(event)">Create Pull Request</button>
8081
</div>
8182

8283
</body>

js/formDataToJson.js

+214
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ async function createCodeJson(data) {
7878
delete data.submit;
7979
const codeJson = await populateCodeJson(data);
8080

81+
window.gh_api_key = data['gh_api_key']
82+
console.log("TEST")
83+
console.log(window.gh_api_key)
84+
8185
const jsonString = JSON.stringify(codeJson, null, 2);
8286
document.getElementById("json-result").value = jsonString;
8387
}
@@ -91,6 +95,215 @@ async function copyToClipboard(event){
9195
document.execCommand("copy")
9296
}
9397

98+
const NEW_BRANCH = 'code-json-branch' + Math.random().toString(36).substring(2, 10);
99+
100+
function getOrgAndRepoArgsGitHub(url)
101+
{
102+
const pattern = /https:\/\/github\.com\/([^\/]+)\/([^\/]+)/;
103+
const match = url.match(pattern);
104+
105+
if(match)
106+
{
107+
const owner = match[1];
108+
const repo = match[2];
109+
return {owner,repo};
110+
}
111+
else
112+
{
113+
throw new Error('Invalid URL!');
114+
}
115+
}
116+
117+
118+
async function createBranchOnProject(projectURL, token)
119+
{
120+
const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL);
121+
122+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`,
123+
{
124+
method: 'GET',
125+
headers: {
126+
'Authorization': 'token '.concat(token),
127+
},
128+
}
129+
);
130+
131+
const data = await response.json();
132+
133+
if (response.ok)
134+
{
135+
const sha = data.object.sha;
136+
137+
const createBranchApiUrl = `https://api.github.com/repos/${owner}/${repo}/git/refs`;
138+
139+
// Create the new branch from the base branch
140+
const newBranchResponse = await fetch(createBranchApiUrl, {
141+
method: 'POST',
142+
headers: {
143+
'Content-Type': 'application/json',
144+
'Authorization': `token ${token}`,
145+
},
146+
body: JSON.stringify({
147+
ref: `refs/heads/${NEW_BRANCH}`, // Name of the new branch
148+
sha: sha, // SHA of the base branch (main)
149+
}),
150+
});
151+
152+
const newBranchData = await newBranchResponse.json();
153+
154+
if ( newBranchResponse.ok )
155+
{
156+
console.log('New branch created successfully: ', newBranchData);
157+
return true;
158+
}
159+
else
160+
{
161+
console.error('Error creating new branch: ', newBranchData);
162+
alert("Failed to create branch on project! Error code: " + newBranchResponse.status + ". Please check API Key permissions and try again.")
163+
return false;
164+
}
165+
}
166+
else
167+
{
168+
console.error('Error fetching base branch info:', data);
169+
alert('Error fetching base branch info:', data);
170+
return false;
171+
}
172+
}
173+
174+
175+
async function addFileToBranch(projectURL, token, codeJSONObj)
176+
{
177+
const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL);
178+
const FILE_PATH = 'code.json'
179+
const createFileApiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${FILE_PATH}`;
180+
const encodedContent = btoa(codeJSONObj);
181+
console.log("Content: ", encodedContent);
182+
console.log("Branch: ", NEW_BRANCH);
183+
184+
const response = await fetch(createFileApiUrl,
185+
{
186+
method: 'PUT',
187+
headers: {
188+
'Accept': 'application/vnd.github+json',
189+
'Authorization': 'Bearer '.concat(token),
190+
'X-GitHub-Api-Version': "2022-11-28"
191+
},
192+
body: JSON.stringify({
193+
message: "Add codejson to project",
194+
committer: {
195+
name: "codejson-generator form site",
196+
197+
},
198+
content: encodedContent,
199+
branch: NEW_BRANCH,
200+
}),
201+
}
202+
);
203+
204+
const data = await response.json()
205+
206+
if ( response.ok )
207+
{
208+
console.log('File added successfully: ', data);
209+
return true;
210+
}
211+
else
212+
{
213+
console.error('Error adding file: ', data);
214+
alert("Failed to add file on project! Error code: " + response.status + ". Please check API Key permissions and try again.")
215+
return false;
216+
}
217+
}
218+
219+
async function createPR(projectURL, token)
220+
{
221+
const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL);
222+
const createPrApiUrl = `https://api.github.com/repos/${owner}/${repo}/pulls`;
223+
const response = await fetch(createPrApiUrl,
224+
{
225+
method: 'POST',
226+
headers: {
227+
'Content-Type': 'application/json',
228+
'Authorization': 'token '.concat(token),
229+
'X-GitHub-Api-Version': "2022-11-28"
230+
},
231+
body: JSON.stringify({
232+
title: "Add code.json to Project",
233+
body: "Add generated code.json file to project. Code.json was generated via codejson-generator form site.",
234+
head: NEW_BRANCH,
235+
base: 'main',
236+
237+
}),
238+
}
239+
);
240+
241+
const data = await response.json();
242+
243+
if (response.ok)
244+
{
245+
console.log('Pull request created successfully: ', data);
246+
return true;
247+
}
248+
else
249+
{
250+
console.error("Error creating PR!: ", data);
251+
alert("Failed to create PR on project! Error code: " + response.status + ". Please check API Key permissions and try again.")
252+
return false;
253+
}
254+
}
255+
256+
// Creates PR on requested project
257+
async function createProjectPR(event){
258+
event.preventDefault();
259+
260+
var textArea = document.getElementById("json-result");//Step 1
261+
var codeJSONObj = JSON.parse(textArea.value)
262+
263+
if('gh_api_key' in window)
264+
{
265+
var apiKey = window.gh_api_key;
266+
267+
if ('repositoryURL' in codeJSONObj)
268+
{
269+
var prCreated = false;
270+
//Step 1
271+
const branchCreated = await createBranchOnProject(codeJSONObj.repositoryURL,apiKey);
272+
if (branchCreated)
273+
{
274+
const fileAdded = await addFileToBranch(codeJSONObj.repositoryURL, apiKey, textArea.value);
275+
276+
if (fileAdded)
277+
{
278+
prCreated = await createPR(codeJSONObj.repositoryURL, apiKey);
279+
if(prCreated)
280+
{
281+
console.log("PR successfully created!");
282+
alert("PR has been created!");
283+
}
284+
}
285+
}
286+
else
287+
{
288+
console.error("Could not create branch on requested repository with the requested API key!");
289+
alert("Could not create branch on requested repository with the requested API key!");
290+
}
291+
}
292+
else
293+
{
294+
console.error("No URL found!");
295+
alert("No URL given for project! Please provide project URL in repositoryURL text box");
296+
}
297+
298+
}
299+
else
300+
{
301+
console.error("No API key found!");
302+
alert("No API Key in submitted data! Please provide an API key");
303+
}
304+
//console.log(codeJSONObj)
305+
}
306+
94307
// Triggers local file download
95308
async function downloadFile(event) {
96309
event.preventDefault();
@@ -112,3 +325,4 @@ async function downloadFile(event) {
112325
window.createCodeJson = createCodeJson;
113326
window.copyToClipboard = copyToClipboard;
114327
window.downloadFile = downloadFile;
328+
window.createProjectPR = createProjectPR;

js/generateFormComponents.js

+16
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,20 @@ async function createFormComponents() {
302302

303303
components = createAllComponents(jsonData);
304304

305+
//Form text box to input GitHub API Key
306+
components.push({
307+
"label": "GitHub API Key (optional)",
308+
"disableSortingAndFiltering": false,
309+
"tableView": true,
310+
"key": "gh_api_key",
311+
"type": "password",
312+
"input": true,
313+
"description": "Generate a Github API Key from here: https://github.com/settings/tokens/new .\n\
314+
The token should have these permissions: \n\
315+
- Contents: read & write \n- Workflows: read & write\
316+
- Pull requests: read & write"
317+
});
318+
305319
// Add submit button to form
306320
components.push({
307321
type: "button",
@@ -312,6 +326,8 @@ async function createFormComponents() {
312326
tableView: false,
313327
});
314328

329+
330+
315331
console.log(components);
316332

317333
return components;

0 commit comments

Comments
 (0)