Skip to content

Commit 52b3860

Browse files
author
azure-pipelines-bot
committed
Modernize release scripts with package updates and compatibility fixes
- Update all npm dependencies to latest versions: * @octokit/rest: v16.43.2 β†’ v22.0.0 * @octokit/graphql: v7.1.1 β†’ v9.0.1 * azure-devops-node-api: v12.0.0 β†’ v15.1.1 * azure-pipelines-task-lib: v4.3.1 β†’ v5.2.1 * node-getopt: v0.2.3 β†’ v0.3.2 - Fix createReleaseBranch.js compatibility with new @octokit versions: * Remove fetch parameter from GraphQL configuration * Implement selective dry-run mode (execute git operations except push) * Improve writeAgentVersionFile to always write file - Optimize createAdoPrs.js dependencies and functionality: * Replace got v14 with Node.js native fetch API * Remove got and node-fetch dependencies completely * Fix Azure DevOps API calls to respect dry-run mode properly - Add comprehensive testing documentation: * Step-by-step testing procedures for all scripts after dependency updates * Environment setup requirements and authentication * Troubleshooting guide for common compatibility issues * Validation checklist for script updates - Comprehensive testing of all release scripts: * Verified fillReleaseNotesTemplate.js functionality * Tested rollrelease.js with GitHub PAT authentication * Validated createAdoPrs.js with improved dry-run behavior * Fixed npm audit security vulnerabilities All scripts now work correctly with updated dependencies and proper dry-run modes for safe testing.
1 parent 349341c commit 52b3860

File tree

5 files changed

+579
-2148
lines changed

5 files changed

+579
-2148
lines changed

β€Žrelease/README.mdβ€Ž

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Azure Pipelines Agent Release Scripts
2+
3+
This directory contains the release automation scripts for the Azure Pipelines Agent. These scripts are used in the Azure DevOps release pipelines to automate various aspects of the release process.
4+
5+
## Scripts Overview
6+
7+
| Script | Purpose | Dependencies Used |
8+
|--------|---------|-------------------|
9+
| `createReleaseBranch.js` | Creates release branches and generates release notes from PRs | `@octokit/rest`, `@octokit/graphql`, `azure-devops-node-api` |
10+
| `createAdoPrs.js` | Creates Azure DevOps pull requests for integration files | `azure-devops-node-api`, `azure-pipelines-task-lib` |
11+
| `fillReleaseNotesTemplate.js` | Fills release notes template with version and hash values | `util.js` (local) |
12+
| `rollrelease.js` | Manages GitHub releases (marks as non-prerelease) | `@octokit/rest` |
13+
| `util.js` | Utility functions for file operations and git commands | Node.js built-ins |
14+
15+
## Testing Scripts After Dependency Updates
16+
17+
When updating npm dependencies in `package.json`, follow these steps to ensure all scripts continue working:
18+
19+
### 1. Update Dependencies
20+
21+
```bash
22+
cd release/
23+
npm update
24+
# or for major version updates:
25+
npm install package@latest
26+
npm audit fix --force
27+
```
28+
29+
### 2. Test Each Script
30+
31+
#### A. Test `fillReleaseNotesTemplate.js`
32+
33+
```bash
34+
# Create mock hash files for testing (required - scripts expect these to exist)
35+
# Note: In production builds, these are generated automatically by the build process
36+
mkdir -p ../_hashes/hash
37+
echo "abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234" > ../_hashes/hash/vsts-agent-win-x64-3.999.999.zip.sha256
38+
echo "efgh1234567890efgh1234567890efgh1234567890efgh1234567890efgh1234" > ../_hashes/hash/vsts-agent-osx-x64-3.999.999.tar.gz.sha256
39+
40+
# Test the script
41+
node fillReleaseNotesTemplate.js 3.999.999
42+
43+
# Check if releaseNote.md was modified correctly
44+
git diff ../releaseNote.md
45+
46+
# Restore original file
47+
git restore ../releaseNote.md
48+
```
49+
50+
#### B. Test `rollrelease.js`
51+
52+
```bash
53+
# Test with dry-run and a real release version (requires GitHub PAT)
54+
PAT="your_github_pat" node rollrelease.js --dryrun --stage="Ring 5" --ghpat="${PAT}" 3.246.0
55+
56+
# Expected: Should connect to GitHub and show what it would do without errors
57+
```
58+
59+
#### C. Test `createReleaseBranch.js`
60+
61+
```bash
62+
# Test with dry-run mode (requires GitHub PAT)
63+
PAT="your_github_pat" node createReleaseBranch.js 4.262.0 --derivedFrom=lastMinorRelease --targetCommitId=$(git rev-parse HEAD) --dryrun
64+
65+
# Expected: Should execute git operations but skip the push step
66+
# Look for: "Dry run mode: skipping push" message
67+
```
68+
69+
#### D. Test `createAdoPrs.js`
70+
71+
```bash
72+
# Test with dry-run mode (requires Azure DevOps PAT)
73+
PAT="your_azdo_pat" node createAdoPrs.js --dryrun=true 3.999.999
74+
75+
# Expected: Should create integration files and show "Dry run: Skipping Azure DevOps API calls"
76+
# Should NOT show authentication errors (401)
77+
```
78+
79+
### 3. Common Issues and Solutions
80+
81+
#### Package Compatibility Issues
82+
83+
**Symptom**: `TypeError: got.get is not a function` or similar method errors
84+
**Solution**: Check if the package changed its API. Update the code to use the new API.
85+
86+
**Example**: `got` library changed from `got.get()` to `got()` in v12+
87+
```javascript
88+
// Old (v11 and earlier)
89+
const response = await got.get(url, options);
90+
91+
// New (v12+)
92+
const response = await got(url, options);
93+
```
94+
95+
#### Missing Dependencies
96+
97+
**Symptom**: `Cannot find module 'package-name'`
98+
**Solution**: Ensure the package is listed in `package.json` and run `npm install`
99+
100+
#### Authentication Issues
101+
102+
**Symptom**: `401 Unauthorized` errors during dry-run
103+
**Solution**: Ensure API calls are properly wrapped in dry-run checks:
104+
```javascript
105+
if (dryrun) {
106+
console.log('Dry run: Skipping API calls');
107+
return mockResponse;
108+
}
109+
// Make actual API calls here
110+
```
111+
112+
### 4. Required Environment Setup
113+
114+
#### For Testing with Real APIs
115+
116+
1. **GitHub PAT**: Required for `rollrelease.js` and `createReleaseBranch.js`
117+
- Set `PAT` environment variable
118+
- Needs `repo` scope permissions
119+
120+
2. **Azure DevOps PAT**: Required for `createAdoPrs.js`
121+
- Set `PAT` environment variable
122+
- Needs `Code (read & write)` and `Pull Request (read & write)` permissions
123+
124+
3. **Git Configuration**: Required for all scripts that make commits
125+
```bash
126+
git config --global user.email "[email protected]"
127+
git config --global user.name "Your Name"
128+
```
129+
130+
#### Mock Data Setup
131+
132+
Some scripts expect certain directories/files to exist:
133+
134+
```bash
135+
# For hash-related scripts (REQUIRED - scripts will fail without these)
136+
mkdir -p ../_hashes/hash
137+
# Create mock hash files for testing as shown in fillReleaseNotesTemplate.js section
138+
139+
# For integration file generation
140+
mkdir -p ../_layout/integrations
141+
```
142+
143+
### 5. Validation Checklist
144+
145+
After testing all scripts, verify:
146+
147+
- [ ] All scripts run without syntax errors
148+
- [ ] Dry-run modes work correctly (no unintended API calls)
149+
- [ ] Scripts handle missing files/directories gracefully
150+
- [ ] Updated packages don't introduce security vulnerabilities (`npm audit`)
151+
- [ ] Git operations execute properly in dry-run mode
152+
- [ ] API authentication works with provided PATs
153+
- [ ] Generated files (integration files, release notes) are correct
154+
155+
### 6. Pipeline Integration
156+
157+
These scripts are used in `.vsts.release.yml`:
158+
159+
- `fillReleaseNotesTemplate.js` - Line ~309
160+
- `createAdoPrs.js` - Line ~450
161+
- `createReleaseBranch.js` - Line ~200
162+
163+
After testing locally, verify the pipeline still works by running a test build.
164+
165+
## Troubleshooting
166+
167+
### Common Error Messages
168+
169+
1. **"ENOENT: no such file or directory, scandir"**
170+
- Missing `_hashes` directory or hash files
171+
- **For testing**: Create mock hash files as shown above
172+
- **For production**: Hash files should be generated by the build process - this indicates a build failure
173+
174+
2. **"got.get is not a function"**
175+
- Package API changed
176+
- Solution: Update code to use new API
177+
178+
3. **"Failed request: (401)"**
179+
- Authentication issue or API calls in dry-run
180+
- Solution: Check PAT and dry-run logic
181+
182+
4. **Node.js version warnings**
183+
- Using outdated Node.js version
184+
- Solution: Ensure Node.js 18+ for native fetch support
185+
186+
### Getting Help
187+
188+
- Check the Azure DevOps pipeline logs for real-world usage examples
189+
- Review git history to see how similar issues were resolved
190+
- Test with minimal reproduction cases before updating production dependencies

β€Žrelease/createAdoPrs.jsβ€Ž

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const fs = require('fs');
33
const path = require('path');
44
const tl = require('azure-pipelines-task-lib/task');
55
const util = require('./util');
6-
const got = require('got');
76

87
const INTEGRATION_DIR = path.join(__dirname, '..', '_layout', 'integrations');
98
const GIT = 'git';
@@ -121,6 +120,11 @@ async function openPR(repo, project, sourceBranch, targetBranch, commitMessage,
121120

122121
const pullRequest = { ...refs, title, description };
123122

123+
if (dryrun) {
124+
console.log('Dry run: Skipping Azure DevOps API calls for PR creation');
125+
return [-1, 'test']; // return without creating PR for test runs
126+
}
127+
124128
console.log('Getting Git API');
125129
const gitApi = await connection.getGitApi();
126130

@@ -129,8 +133,6 @@ async function openPR(repo, project, sourceBranch, targetBranch, commitMessage,
129133

130134
if (PR) {
131135
console.log('PR already exists');
132-
} else if (dryrun) {
133-
return [-1, 'test']; // return without creating PR for test runs
134136
} else {
135137
console.log('PR does not exist; creating PR');
136138
PR = await gitApi.createPullRequest(pullRequest, repo, project);
@@ -149,8 +151,9 @@ async function openPR(repo, project, sourceBranch, targetBranch, commitMessage,
149151
* @returns current sprint version
150152
*/
151153
async function getCurrentSprint() {
152-
const response = await got.get('https://whatsprintis.it/?json', { responseType: 'json' });
153-
const sprint = response.body.sprint;
154+
const response = await fetch('https://whatsprintis.it/?json');
155+
const data = await response.json();
156+
const sprint = data.sprint;
154157
if (!/^\d\d\d$/.test(sprint)) {
155158
throw new Error(`Sprint must be a three-digit number; received: ${sprint}`);
156159
}

β€Žrelease/createReleaseBranch.jsβ€Ž

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@ const util = require('./util');
66

77
const { Octokit } = require("@octokit/rest");
88
const { graphql } = require("@octokit/graphql");
9-
const fetch = require('node-fetch');
109

1110
const OWNER = 'microsoft';
1211
const REPO = 'azure-pipelines-agent';
1312
const GIT = 'git';
1413
const VALID_RELEASE_RE = /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/;
1514
const octokit = new Octokit({}); // only read-only operations, no need to auth
1615

17-
const graphqlWithFetch = graphql.defaults({ // Create a reusable GraphQL instance with fetch
18-
request: {
19-
fetch,
20-
},
16+
const graphqlWithAuth = graphql.defaults({
2117
headers: {
2218
authorization: process.env.PAT ? `token ${process.env.PAT}` : undefined,
2319
}
@@ -63,9 +59,8 @@ async function verifyNewReleaseTagOk(newRelease) {
6359

6460
function writeAgentVersionFile(newRelease) {
6561
console.log('Writing agent version file')
66-
if (!opt.options.dryrun) {
67-
fs.writeFileSync(path.join(__dirname, '..', 'src', 'agentversion'), `${newRelease}\n`);
68-
}
62+
// Always write the agent version file, even in dry-run mode
63+
fs.writeFileSync(path.join(__dirname, '..', 'src', 'agentversion'), `${newRelease}\n`);
6964
return newRelease;
7065
}
7166

@@ -102,7 +97,7 @@ async function fetchPRsForSHAsGraphQL(commitSHAs) {
10297
`;
10398

10499
try {
105-
var response = await graphqlWithFetch(fullQuery, {
100+
var response = await graphqlWithAuth(fullQuery, {
106101
repo: REPO,
107102
owner: OWNER,
108103
});
@@ -293,17 +288,17 @@ function editReleaseNotesFile(body) {
293288
}
294289

295290
function commitAndPush(directory, release, branch) {
296-
util.execInForeground(GIT + " checkout -b " + branch, directory, opt.options.dryrun);
297-
util.execInForeground(`${GIT} commit -m "Agent Release ${release}" `, directory, opt.options.dryrun);
298-
util.execInForeground(`${GIT} -c credential.helper='!f() { echo "username=pat"; echo "password=$PAT"; };f' push --set-upstream origin ${branch}`, directory, opt.options.dryrun);
291+
util.execInForeground(GIT + " checkout -b " + branch, directory, false); // Always execute checkout
292+
util.execInForeground(`${GIT} commit -m "Agent Release ${release}" `, directory, false); // Always execute commit
293+
util.execInForeground(`${GIT} -c credential.helper='!f() { echo "username=pat"; echo "password=$PAT"; };f' push --set-upstream origin ${branch}`, directory, opt.options.dryrun); // Only push respects dryrun
299294
}
300295

301296
function commitAgentChanges(directory, release) {
302297
var newBranch = `releases/${release}`;
303-
util.execInForeground(`${GIT} add ${path.join('src', 'agentversion')}`, directory, opt.options.dryrun);
304-
util.execInForeground(`${GIT} add releaseNote.md`, directory, opt.options.dryrun);
305-
util.execInForeground(`${GIT} config --global user.email "[email protected]"`, null, opt.options.dryrun);
306-
util.execInForeground(`${GIT} config --global user.name "azure-pipelines-bot"`, null, opt.options.dryrun);
298+
util.execInForeground(`${GIT} add ${path.join('src', 'agentversion')}`, directory, false); // Always execute add
299+
util.execInForeground(`${GIT} add releaseNote.md`, directory, false); // Always execute add
300+
util.execInForeground(`${GIT} config --global user.email "[email protected]"`, null, false); // Always execute config
301+
util.execInForeground(`${GIT} config --global user.name "azure-pipelines-bot"`, null, false); // Always execute config
307302
commitAndPush(directory, release, newBranch);
308303
}
309304

0 commit comments

Comments
Β (0)