-
Notifications
You must be signed in to change notification settings - Fork 395
Expand file tree
/
Copy pathcreate_pull_request.cjs
More file actions
168 lines (136 loc) · 5.59 KB
/
create_pull_request.cjs
File metadata and controls
168 lines (136 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/** @type {typeof import("fs")} */
const fs = require("fs");
/** @type {typeof import("crypto")} */
const crypto = require("crypto");
const { execSync } = require("child_process");
async function main() {
// Environment validation - fail early if required variables are missing
const workflowId = process.env.GITHUB_AW_WORKFLOW_ID;
if (!workflowId) {
throw new Error('GITHUB_AW_WORKFLOW_ID environment variable is required');
}
const baseBranch = process.env.GITHUB_AW_BASE_BRANCH;
if (!baseBranch) {
throw new Error('GITHUB_AW_BASE_BRANCH environment variable is required');
}
const outputContent = process.env.GITHUB_AW_AGENT_OUTPUT || "";
if (outputContent.trim() === '') {
console.log('Agent output content is empty');
}
// Check if patch file exists and has valid content
if (!fs.existsSync('/tmp/aw.patch')) {
throw new Error('No patch file found - cannot create pull request without changes');
}
const patchContent = fs.readFileSync('/tmp/aw.patch', 'utf8');
if (!patchContent || !patchContent.trim() || patchContent.includes('Failed to generate patch')) {
throw new Error('Patch file is empty or contains error message - cannot create pull request without changes');
}
console.log('Agent output content length:', outputContent.length);
console.log('Patch content validation passed');
// Parse the output to extract title and body
const lines = outputContent.split('\n');
let title = '';
let bodyLines = [];
let foundTitle = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Skip empty lines until we find the title
if (!foundTitle && line === '') {
continue;
}
// First non-empty line becomes the title
if (!foundTitle && line !== '') {
// Remove markdown heading syntax if present
title = line.replace(/^#+\s*/, '').trim();
foundTitle = true;
continue;
}
// Everything else goes into the body
if (foundTitle) {
bodyLines.push(lines[i]); // Keep original formatting
}
}
// If no title was found, use a default
if (!title) {
title = 'Agent Output';
}
// Apply title prefix if provided via environment variable
const titlePrefix = process.env.GITHUB_AW_PR_TITLE_PREFIX;
if (titlePrefix && !title.startsWith(titlePrefix)) {
title = titlePrefix + title;
}
// Add AI disclaimer with run id, run htmlurl
const runId = context.runId;
const runUrl = context.payload.repository
? `${context.payload.repository.html_url}/actions/runs/${runId}`
: `https://github.com/actions/runs/${runId}`;
bodyLines.push(``, ``, `> Generated by Agentic Workflow Run [${runId}](${runUrl})`, '');
// Prepare the body content
const body = bodyLines.join('\n').trim();
// Parse labels from environment variable (comma-separated string)
const labelsEnv = process.env.GITHUB_AW_PR_LABELS;
const labels = labelsEnv ? labelsEnv.split(',').map(/** @param {string} label */ label => label.trim()).filter(/** @param {string} label */ label => label) : [];
// Parse draft setting from environment variable (defaults to true)
const draftEnv = process.env.GITHUB_AW_PR_DRAFT;
const draft = draftEnv ? draftEnv.toLowerCase() === 'true' : true;
console.log('Creating pull request with title:', title);
console.log('Labels:', labels);
console.log('Draft:', draft);
console.log('Body length:', body.length);
// Generate unique branch name using cryptographic random hex
const randomHex = crypto.randomBytes(8).toString('hex');
const branchName = `${workflowId}/${randomHex}`;
console.log('Generated branch name:', branchName);
console.log('Base branch:', baseBranch);
// Create a new branch using git CLI
// Configure git (required for commits)
execSync('git config --global user.email "action@github.com"', { stdio: 'inherit' });
execSync('git config --global user.name "GitHub Action"', { stdio: 'inherit' });
// Create and checkout new branch
execSync(`git checkout -b ${branchName}`, { stdio: 'inherit' });
console.log('Created and checked out branch:', branchName);
// Apply the patch using git CLI
console.log('Applying patch...');
// Apply the patch using git apply
execSync('git apply /tmp/aw.patch', { stdio: 'inherit' });
console.log('Patch applied successfully');
// Commit and push the changes
execSync('git add .', { stdio: 'inherit' });
execSync(`git commit -m "Add agent output: ${title}"`, { stdio: 'inherit' });
execSync(`git push origin ${branchName}`, { stdio: 'inherit' });
console.log('Changes committed and pushed');
// Create the pull request
const { data: pullRequest } = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: body,
head: branchName,
base: baseBranch,
draft: draft
});
console.log('Created pull request #' + pullRequest.number + ': ' + pullRequest.html_url);
// Add labels if specified
if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
labels: labels
});
console.log('Added labels to pull request:', labels);
}
// Set output for other jobs to use
core.setOutput('pull_request_number', pullRequest.number);
core.setOutput('pull_request_url', pullRequest.html_url);
core.setOutput('branch_name', branchName);
// Write summary to GitHub Actions summary
await core.summary
.addRaw(`
## Pull Request
- **Pull Request**: [#${pullRequest.number}](${pullRequest.html_url})
- **Branch**: \`${branchName}\`
- **Base Branch**: \`${baseBranch}\`
`).write();
}
await main();