Skip to content

Commit 8d5c807

Browse files
committed
Addressed PR feedback. I was also tired of making prettier updates so I just enabled formatonsave for typescript.
1 parent 4d71475 commit 8d5c807

File tree

4 files changed

+213
-139
lines changed

4 files changed

+213
-139
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"vsix/": true
1313
},
1414
"[typescript]": {
15-
"editor.defaultFormatter": "esbenp.prettier-vscode"
15+
"editor.defaultFormatter": "esbenp.prettier-vscode",
16+
"editor.formatOnSave": true
1617
},
1718
"csharp.suppressDotnetRestoreNotification": true,
1819
"typescript.tsdk": "./node_modules/typescript/lib",

azure-pipelines/publish-roslyn-copilot.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ extends:
6565
npm install -g gulp
6666
displayName: 'Install tools'
6767
68-
- pwsh: gulp 'publish roslyn copilot' --userName dotnet-bot --email [email protected]
68+
- pwsh: gulp 'publish roslyn copilot' --userName dotnet-bot --email [email protected] --stagingDirectory '$(Build.ArtifactStagingDirectory)/staging'
6969
displayName: 'Create component update PR'
7070
env:
7171
GitHubPAT: $(GitHubPAT)
72-
STAGING_DIRECTORY: $(Build.ArtifactStagingDirectory)/staging

tasks/componentUpdateTasks.ts

Lines changed: 62 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -8,179 +8,105 @@ import * as process from 'node:process';
88
import * as fs from 'fs';
99
import * as path from 'path';
1010
import minimist from 'minimist';
11-
import { spawnSync } from 'child_process';
12-
import { getPackageJSON } from './packageJson';
13-
import { Octokit } from '@octokit/rest';
11+
import {
12+
configureGitUser,
13+
createCommit,
14+
pushBranch,
15+
createPullRequest,
16+
doesBranchExist,
17+
findPRByTitle,
18+
} from './gitTasks';
19+
import { updatePackageDependencies } from '../src/tools/updatePackageDependencies';
1420

1521
type Options = {
1622
userName?: string;
1723
email?: string;
1824
};
1925

20-
async function git(args: string[], printCommand = true): Promise<string> {
21-
if (printCommand) {
22-
console.log(`git ${args.join(' ')}`);
23-
}
24-
25-
const git = spawnSync('git', args);
26-
if (git.status != 0) {
27-
const err = git.stderr ? git.stderr.toString() : '';
28-
if (printCommand) {
29-
console.error(`Failed to execute git ${args.join(' ')}.`);
30-
}
31-
throw new Error(err || `git ${args.join(' ')} failed with code ${git.status}`);
32-
}
33-
34-
const stdout = git.stdout ? git.stdout.toString() : '';
35-
if (printCommand) {
36-
console.log(stdout);
37-
}
38-
return Promise.resolve(stdout);
26+
/**
27+
* Extract version from file name using a provided regex pattern
28+
* @param fileName - The file name to extract version from
29+
* @param pattern - The regex pattern to match and extract version (should have a capture group)
30+
* @returns The extracted version string or null if not found
31+
*/
32+
function extractVersion(fileName: string, pattern: RegExp): string | null {
33+
const match = fileName.match(pattern);
34+
return match && match[1] ? match[1] : null;
3935
}
4036

4137
gulp.task('publish roslyn copilot', async () => {
4238
const parsedArgs = minimist<Options>(process.argv.slice(2));
4339

44-
// Get staging directory from environment variable passed from pipeline
45-
const stagingDir = process.env['STAGING_DIRECTORY'];
46-
if (!stagingDir) {
47-
console.log('STAGING_DIRECTORY environment variable not set; skipping package.json update.');
48-
return;
49-
}
50-
51-
if (!fs.existsSync(stagingDir)) {
52-
console.log(`Staging directory not found at ${stagingDir}; skipping package.json update.`);
53-
return;
40+
if (!parsedArgs.stagingDirectory || !fs.existsSync(parsedArgs.stagingDirectory)) {
41+
throw new Error(`Staging directory not found at ${parsedArgs.stagingDirectory}; skipping package.json update.`);
5442
}
5543

5644
// Find the Roslyn zip file in the staging directory (we know it was copied here)
57-
const files = fs.readdirSync(stagingDir);
45+
const files = fs.readdirSync(parsedArgs.stagingDirectory);
5846
const zipFile = files.find((file) => /Roslyn\.LanguageServer.*\.zip$/i.test(file));
5947

6048
if (!zipFile) {
61-
console.log(`No Roslyn LanguageServer zip file found in ${stagingDir}; skipping package.json update.`);
62-
return;
49+
throw new Error(`
50+
No Roslyn LanguageServer zip file found in ${parsedArgs.stagingDirectory}; skipping package.json update.`);
6351
}
6452

65-
const zipPath = path.join(stagingDir, zipFile);
53+
const zipPath = path.join(parsedArgs.stagingDirectory, zipFile);
6654
console.log(`Using zip file: ${zipPath}`);
6755
const zipName = zipFile;
6856

6957
// Extract version from file name
70-
// Example: "Microsoft.VisualStudio.Copilot.Roslyn.LanguageServer-18.0.671-alpha.zip"
71-
let version: string | null = null;
72-
const m = zipName.match(/Microsoft\.VisualStudio\.Copilot\.Roslyn\.LanguageServer-(.+)\.zip$/i);
73-
if (m && m[1]) {
74-
version = m[1];
75-
}
58+
const version = extractVersion(zipName, /Microsoft\.VisualStudio\.Copilot\.Roslyn\.LanguageServer-(.+)\.zip$/i);
7659

7760
if (!version) {
78-
console.log(`Could not extract version from file name ${zipName}; skipping.`);
79-
return;
61+
throw new Error(`Could not extract version from file name ${zipName}; skipping.`);
8062
}
8163

8264
console.log(`Extracted version: ${version}`);
8365

84-
const pkg = getPackageJSON();
85-
let updated = false;
86-
87-
if (pkg.runtimeDependencies && Array.isArray(pkg.runtimeDependencies)) {
88-
for (let i = 0; i < pkg.runtimeDependencies.length; i++) {
89-
const dep = pkg.runtimeDependencies[i];
90-
if (dep && dep.id === 'RoslynCopilot') {
91-
const oldUrl = dep.url as string;
92-
const newUrl = oldUrl.replace(
93-
/Microsoft\.VisualStudio\.Copilot\.Roslyn\.LanguageServer-[^/]+?\.zip/,
94-
`Microsoft.VisualStudio.Copilot.Roslyn.LanguageServer-${version}.zip`
95-
);
96-
if (oldUrl !== newUrl) {
97-
pkg.runtimeDependencies[i].url = newUrl;
98-
updated = true;
99-
console.log(`Updated RoslynCopilot url:\n ${oldUrl}\n-> ${newUrl}`);
100-
} else {
101-
console.log('RoslynCopilot url already up to date.');
102-
}
103-
break;
104-
}
105-
}
106-
}
107-
108-
if (!updated) {
109-
console.log('No changes required to package.json; aborting PR creation.');
110-
return;
111-
}
112-
113-
// Persist package.json
114-
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n', { encoding: 'utf8' });
115-
116-
// Prepare git
11766
const safeVersion = version.replace(/[^A-Za-z0-9_.-]/g, '-');
11867
const branch = `update/roslyn-copilot-${safeVersion}`;
11968

120-
// Make this optional so it can be tested locally by using dev's information. In real CI user name and email are always supplied.
121-
if (parsedArgs.userName) {
122-
await git(['config', '--local', 'user.name', parsedArgs.userName]);
123-
}
124-
if (parsedArgs.email) {
125-
await git(['config', '--local', 'user.email', parsedArgs.email]);
126-
}
127-
128-
await git(['checkout', '-b', branch]);
129-
await git(['add', 'package.json']);
130-
await git(['commit', '-m', `chore: update RoslynCopilot url to ${version}`]);
131-
13269
const pat = process.env['GitHubPAT'];
13370
if (!pat) {
13471
throw 'No GitHub PAT found.';
13572
}
13673

137-
const remoteRepoAlias = 'targetRepo';
138-
await git(
139-
['remote', 'add', remoteRepoAlias, `https://${parsedArgs.userName}:${pat}@github.com/dotnet/vscode-csharp.git`],
140-
// Note: don't print PAT to console
141-
false
142-
);
143-
await git(['fetch', remoteRepoAlias]);
144-
145-
const lsRemote = await git(['ls-remote', remoteRepoAlias, 'refs/head/' + branch]);
146-
if (lsRemote.trim() !== '') {
147-
// If the localization branch of this commit already exists, don't try to create another one.
148-
console.log(`##vso[task.logissue type=error]${branch} already exists in dotnet/vscode-csharp. Skip pushing.`);
149-
} else {
150-
await git(['push', '-u', remoteRepoAlias]);
151-
}
74+
const owner = 'dotnet';
75+
const repo = 'vscode-csharp';
76+
const title = `Update RoslynCopilot url to ${version}`;
77+
const body = `Automated update of RoslynCopilot url to ${version}`;
15278

153-
// Create PR via Octokit
154-
try {
155-
const octokit = new Octokit({ auth: pat });
156-
const listPullRequest = await octokit.rest.pulls.list({
157-
owner: 'dotnet',
158-
repo: 'vscode-csharp',
159-
});
160-
161-
if (listPullRequest.status != 200) {
162-
throw `Failed get response from GitHub, http status code: ${listPullRequest.status}`;
163-
}
164-
165-
const title = `Update RoslynCopilot url to ${version}`;
166-
if (listPullRequest.data.some((pr) => pr.title === title)) {
167-
console.log('Pull request with the same name already exists. Skip creation.');
168-
return;
169-
}
170-
171-
const body = `Automated update of RoslynCopilot url to ${version}`;
172-
173-
console.log(`Creating PR against dotnet/vscode-csharp...`);
174-
const pullRequest = await octokit.rest.pulls.create({
175-
owner: 'dotnet',
176-
repo: 'vscode-csharp',
177-
title: title,
178-
head: branch,
179-
base: 'main',
180-
body: body,
181-
});
182-
console.log(`Created pull request: ${pullRequest.data.html_url}`);
183-
} catch (e) {
184-
console.warn('Failed to create PR via Octokit:', e);
79+
// Bail out if a branch with the same name already exists or PR already exists for the insertion.
80+
if (await doesBranchExist('origin', branch)) {
81+
console.log(`##vso[task.logissue type=warning]${branch} already exists in origin. Skip pushing.`);
82+
return;
18583
}
84+
const existingPRUrl = await findPRByTitle(pat, owner, repo, title);
85+
if (existingPRUrl) {
86+
console.log(
87+
`##vso[task.logissue type=warning] Pull request with the same name already exists: ${existingPRUrl}`
88+
);
89+
return;
90+
}
91+
92+
// Set environment variables for updatePackageDependencies
93+
process.env['NEW_DEPS_ID'] = 'RoslynCopilot';
94+
process.env['NEW_DEPS_VERSION'] = version;
95+
process.env[
96+
'NEW_DEPS_URLS'
97+
] = `https://roslyn.blob.core.windows.net/releases/Microsoft.VisualStudio.Copilot.Roslyn.LanguageServer-${version}.zip`;
98+
99+
// Update package dependencies using the extracted utility
100+
await updatePackageDependencies();
101+
console.log(`Updated RoslynCopilot dependency to version ${version}`);
102+
103+
// Configure git user if provided
104+
await configureGitUser(parsedArgs.userName, parsedArgs.email);
105+
106+
// Create commit with changes
107+
await createCommit(branch, ['package.json'], `Update RoslynCopilot version to ${version}`);
108+
109+
// Push branch and create PR
110+
await pushBranch(branch, pat, owner, repo);
111+
await createPullRequest(pat, owner, repo, branch, title, body);
186112
});

0 commit comments

Comments
 (0)