Skip to content

Commit c0ddf01

Browse files
itsmevichukrassowskijtpio
authored
Workflow to update JupyterLab dependencies automatically (jupyter#7281)
* workflow to upgrade JupyterLab dependencies * Update upgrade-lab-dependencies.py * Prettier code for Test Lint check * Prettier code for Test Lint checks * Prettier code for Test Lint check * added ts scripts to upgrade lab dependencies * Prettier code for test lint check * Update buildutils/src/upgrade-lab-dependencies.ts Co-authored-by: Michał Krassowski <5832902+krassowski@users.noreply.github.com> * Lint --------- Co-authored-by: Michał Krassowski <5832902+krassowski@users.noreply.github.com> Co-authored-by: Jeremy Tuloup <jeremy.tuloup@gmail.com>
1 parent d9119b8 commit c0ddf01

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Check for latest JupyterLab releases
2+
3+
on:
4+
schedule:
5+
- cron: 30 17 * * *
6+
workflow_dispatch:
7+
inputs:
8+
version:
9+
description: 'JupyterLab version'
10+
default: latest
11+
required: true
12+
type: string
13+
14+
env:
15+
version_tag: 'latest'
16+
17+
permissions:
18+
contents: write
19+
pull-requests: write
20+
21+
jobs:
22+
check_for_lab_updates:
23+
runs-on: ubuntu-latest
24+
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v3
28+
29+
- name: Set up Python
30+
uses: actions/setup-python@v4
31+
with:
32+
python-version: '3.11'
33+
34+
- name: Install required dependencies
35+
run: |
36+
python -m pip install jupyterlab
37+
sudo apt-get install hub
38+
39+
- name: Install Node
40+
uses: actions/setup-node@v2
41+
with:
42+
node-version: '20.x'
43+
44+
- name: Install npm dependencies and build buildutils
45+
run: |
46+
jlpm install
47+
jlpm run build:utils
48+
49+
- name: Check for new releases and update
50+
shell: bash
51+
run: |
52+
set -eux
53+
for version in ${{ inputs.version || env.version_tag }}
54+
do
55+
export LATEST=$(node buildutils/lib/get-latest-lab-version.js --set-version $version)
56+
done
57+
58+
echo "latest=${LATEST}" >> $GITHUB_ENV
59+
node buildutils/lib/upgrade-lab-dependencies.js --set-version ${LATEST}
60+
if [[ ! -z "$(git status --porcelain package.json)" ]]; then
61+
jlpm install
62+
fi
63+
64+
- name: Create a PR
65+
shell: bash
66+
env:
67+
GITHUB_USER: ${{ secrets.G_USER }}
68+
GITHUB_TOKEN: ${{ secrets.G_TOKEN }}
69+
run: |
70+
set -eux
71+
72+
export LATEST=${{ env.latest }}
73+
export BRANCH_NAME=update-to-v${LATEST}
74+
75+
# if resulted in any change:
76+
if [[ ! -z "$(git status --porcelain package.json)" ]]; then
77+
# if branch already exists.
78+
if git ls-remote --heads origin | grep "refs/heads/${BRANCH_NAME}$" > /dev/null; then
79+
echo "Branch '${BRANCH_NAME}' exists."
80+
else
81+
# new branch is created
82+
git checkout -b "${BRANCH_NAME}"
83+
git config user.name "Jupyter Bot"
84+
git config user.email 'jupyterlab-bot@users.noreply.github.com'
85+
86+
git commit . -m "Update to JupyterLab v${LATEST}"
87+
88+
git push --set-upstream origin "${BRANCH_NAME}"
89+
hub pull-request -m "Update to JupyterLab v${LATEST}" \
90+
-m "New JupyterLab release [v${LATEST}](https://github.com/jupyterlab/jupyterlab/releases/tag/v${LATEST}) is available. Please review the lock file carefully.".
91+
fi
92+
fi
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function extractVersionFromReleases(
2+
releases: any,
3+
versionTag: string
4+
): string | null {
5+
for (const release of releases) {
6+
const tagName: string = release['tag_name'];
7+
if (versionTag === 'latest') {
8+
if (!release['prerelease'] && !release['draft']) {
9+
return tagName;
10+
}
11+
} else if (versionTag === tagName) {
12+
return tagName;
13+
}
14+
}
15+
return null;
16+
}
17+
18+
async function findVersion(versionTag: string): Promise<string> {
19+
const url = 'https://api.github.com/repos/jupyterlab/jupyterlab/releases';
20+
const response = await fetch(url);
21+
if (!response.ok) {
22+
const error_message = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`;
23+
throw new Error(error_message);
24+
}
25+
const releases: any = await response.json();
26+
const version: string | null = extractVersionFromReleases(
27+
releases,
28+
versionTag
29+
);
30+
if (version === null) {
31+
const error_message = 'Invalid release tag';
32+
throw new Error(error_message);
33+
}
34+
return version.substring(1);
35+
}
36+
37+
async function getLatestLabVersion(): Promise<void> {
38+
const args: string[] = process.argv.slice(2);
39+
if (args.length !== 2 || args[0] !== '--set-version') {
40+
console.error('Usage: node script.js --set-version <version>');
41+
process.exit(1);
42+
}
43+
const version_tag: string = args[1];
44+
45+
try {
46+
const result: string = await findVersion(version_tag);
47+
console.log(result);
48+
} catch (error: any) {
49+
console.error('Error:', error.message);
50+
process.exit(1);
51+
}
52+
}
53+
54+
getLatestLabVersion();
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
const PACKAGE_JSON_PATHS: string[] = [
5+
'app/package.json',
6+
'buildutils/package.json',
7+
'package.json',
8+
'packages/application-extension/package.json',
9+
'packages/application/package.json',
10+
'packages/console-extension/package.json',
11+
'packages/docmanager-extension/package.json',
12+
'packages/documentsearch-extension/package.json',
13+
'packages/help-extension/package.json',
14+
'packages/lab-extension/package.json',
15+
'packages/notebook-extension/package.json',
16+
'packages/terminal-extension/package.json',
17+
'packages/tree-extension/package.json',
18+
'packages/tree/package.json',
19+
'packages/ui-components/package.json',
20+
];
21+
22+
const DEPENDENCY_GROUP = '@jupyterlab';
23+
24+
async function updatePackageJson(newVersion: string): Promise<void> {
25+
const url = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`;
26+
const response = await fetch(url);
27+
28+
if (!response.ok) {
29+
const errorMessage = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`;
30+
throw new Error(errorMessage);
31+
}
32+
33+
const newPackageJson = await response.json();
34+
35+
for (const packageJsonPath of PACKAGE_JSON_PATHS) {
36+
const filePath: string = path.resolve(packageJsonPath);
37+
const existingPackageJson = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
38+
39+
const newDependencies = {
40+
...newPackageJson.devDependencies,
41+
...newPackageJson.resolutions,
42+
};
43+
44+
updateDependencyVersion(existingPackageJson, newDependencies);
45+
46+
fs.writeFileSync(
47+
filePath,
48+
JSON.stringify(existingPackageJson, null, 2) + '\n'
49+
);
50+
}
51+
}
52+
53+
function updateDependencyVersion(existingJson: any, newJson: any): void {
54+
if (!existingJson) {
55+
return;
56+
}
57+
58+
const sectionPaths: string[] = [
59+
'resolutions',
60+
'dependencies',
61+
'devDependencies',
62+
];
63+
64+
for (const section of sectionPaths) {
65+
if (!existingJson[section]) {
66+
continue;
67+
}
68+
69+
const updated = existingJson[section];
70+
71+
for (const [pkg, version] of Object.entries<string>(
72+
existingJson[section]
73+
)) {
74+
if (pkg.startsWith(DEPENDENCY_GROUP) && pkg in newJson) {
75+
if (version[0] === '^' || version[0] === '~') {
76+
updated[pkg] = version[0] + absoluteVersion(newJson[pkg]);
77+
} else {
78+
updated[pkg] = absoluteVersion(newJson[pkg]);
79+
}
80+
}
81+
}
82+
}
83+
}
84+
85+
function absoluteVersion(version: string): string {
86+
if (version.length > 0 && (version[0] === '^' || version[0] === '~')) {
87+
return version.substring(1);
88+
}
89+
return version;
90+
}
91+
92+
async function upgradeLabDependencies(): Promise<void> {
93+
const args: string[] = process.argv.slice(2);
94+
95+
if (args.length !== 2 || args[0] !== '--set-version') {
96+
console.error('Usage: node script.js --set-version <version>');
97+
process.exit(1);
98+
}
99+
100+
const newVersion: string = args[1];
101+
await updatePackageJson(newVersion);
102+
}
103+
104+
upgradeLabDependencies();

0 commit comments

Comments
 (0)