-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Workflow to update JupyterLab dependencies automatically (#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>
- Loading branch information
1 parent
d9119b8
commit c0ddf01
Showing
3 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
name: Check for latest JupyterLab releases | ||
|
||
on: | ||
schedule: | ||
- cron: 30 17 * * * | ||
workflow_dispatch: | ||
inputs: | ||
version: | ||
description: 'JupyterLab version' | ||
default: latest | ||
required: true | ||
type: string | ||
|
||
env: | ||
version_tag: 'latest' | ||
|
||
permissions: | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
check_for_lab_updates: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.11' | ||
|
||
- name: Install required dependencies | ||
run: | | ||
python -m pip install jupyterlab | ||
sudo apt-get install hub | ||
- name: Install Node | ||
uses: actions/setup-node@v2 | ||
with: | ||
node-version: '20.x' | ||
|
||
- name: Install npm dependencies and build buildutils | ||
run: | | ||
jlpm install | ||
jlpm run build:utils | ||
- name: Check for new releases and update | ||
shell: bash | ||
run: | | ||
set -eux | ||
for version in ${{ inputs.version || env.version_tag }} | ||
do | ||
export LATEST=$(node buildutils/lib/get-latest-lab-version.js --set-version $version) | ||
done | ||
echo "latest=${LATEST}" >> $GITHUB_ENV | ||
node buildutils/lib/upgrade-lab-dependencies.js --set-version ${LATEST} | ||
if [[ ! -z "$(git status --porcelain package.json)" ]]; then | ||
jlpm install | ||
fi | ||
- name: Create a PR | ||
shell: bash | ||
env: | ||
GITHUB_USER: ${{ secrets.G_USER }} | ||
GITHUB_TOKEN: ${{ secrets.G_TOKEN }} | ||
run: | | ||
set -eux | ||
export LATEST=${{ env.latest }} | ||
export BRANCH_NAME=update-to-v${LATEST} | ||
# if resulted in any change: | ||
if [[ ! -z "$(git status --porcelain package.json)" ]]; then | ||
# if branch already exists. | ||
if git ls-remote --heads origin | grep "refs/heads/${BRANCH_NAME}$" > /dev/null; then | ||
echo "Branch '${BRANCH_NAME}' exists." | ||
else | ||
# new branch is created | ||
git checkout -b "${BRANCH_NAME}" | ||
git config user.name "Jupyter Bot" | ||
git config user.email 'jupyterlab-bot@users.noreply.github.com' | ||
git commit . -m "Update to JupyterLab v${LATEST}" | ||
git push --set-upstream origin "${BRANCH_NAME}" | ||
hub pull-request -m "Update to JupyterLab v${LATEST}" \ | ||
-m "New JupyterLab release [v${LATEST}](https://github.com/jupyterlab/jupyterlab/releases/tag/v${LATEST}) is available. Please review the lock file carefully.". | ||
fi | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
function extractVersionFromReleases( | ||
releases: any, | ||
versionTag: string | ||
): string | null { | ||
for (const release of releases) { | ||
const tagName: string = release['tag_name']; | ||
if (versionTag === 'latest') { | ||
if (!release['prerelease'] && !release['draft']) { | ||
return tagName; | ||
} | ||
} else if (versionTag === tagName) { | ||
return tagName; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
async function findVersion(versionTag: string): Promise<string> { | ||
const url = 'https://api.github.com/repos/jupyterlab/jupyterlab/releases'; | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
const error_message = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`; | ||
throw new Error(error_message); | ||
} | ||
const releases: any = await response.json(); | ||
const version: string | null = extractVersionFromReleases( | ||
releases, | ||
versionTag | ||
); | ||
if (version === null) { | ||
const error_message = 'Invalid release tag'; | ||
throw new Error(error_message); | ||
} | ||
return version.substring(1); | ||
} | ||
|
||
async function getLatestLabVersion(): Promise<void> { | ||
const args: string[] = process.argv.slice(2); | ||
if (args.length !== 2 || args[0] !== '--set-version') { | ||
console.error('Usage: node script.js --set-version <version>'); | ||
process.exit(1); | ||
} | ||
const version_tag: string = args[1]; | ||
|
||
try { | ||
const result: string = await findVersion(version_tag); | ||
console.log(result); | ||
} catch (error: any) { | ||
console.error('Error:', error.message); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
getLatestLabVersion(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
const PACKAGE_JSON_PATHS: string[] = [ | ||
'app/package.json', | ||
'buildutils/package.json', | ||
'package.json', | ||
'packages/application-extension/package.json', | ||
'packages/application/package.json', | ||
'packages/console-extension/package.json', | ||
'packages/docmanager-extension/package.json', | ||
'packages/documentsearch-extension/package.json', | ||
'packages/help-extension/package.json', | ||
'packages/lab-extension/package.json', | ||
'packages/notebook-extension/package.json', | ||
'packages/terminal-extension/package.json', | ||
'packages/tree-extension/package.json', | ||
'packages/tree/package.json', | ||
'packages/ui-components/package.json', | ||
]; | ||
|
||
const DEPENDENCY_GROUP = '@jupyterlab'; | ||
|
||
async function updatePackageJson(newVersion: string): Promise<void> { | ||
const url = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`; | ||
const response = await fetch(url); | ||
|
||
if (!response.ok) { | ||
const errorMessage = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`; | ||
throw new Error(errorMessage); | ||
} | ||
|
||
const newPackageJson = await response.json(); | ||
|
||
for (const packageJsonPath of PACKAGE_JSON_PATHS) { | ||
const filePath: string = path.resolve(packageJsonPath); | ||
const existingPackageJson = JSON.parse(fs.readFileSync(filePath, 'utf-8')); | ||
|
||
const newDependencies = { | ||
...newPackageJson.devDependencies, | ||
...newPackageJson.resolutions, | ||
}; | ||
|
||
updateDependencyVersion(existingPackageJson, newDependencies); | ||
|
||
fs.writeFileSync( | ||
filePath, | ||
JSON.stringify(existingPackageJson, null, 2) + '\n' | ||
); | ||
} | ||
} | ||
|
||
function updateDependencyVersion(existingJson: any, newJson: any): void { | ||
if (!existingJson) { | ||
return; | ||
} | ||
|
||
const sectionPaths: string[] = [ | ||
'resolutions', | ||
'dependencies', | ||
'devDependencies', | ||
]; | ||
|
||
for (const section of sectionPaths) { | ||
if (!existingJson[section]) { | ||
continue; | ||
} | ||
|
||
const updated = existingJson[section]; | ||
|
||
for (const [pkg, version] of Object.entries<string>( | ||
existingJson[section] | ||
)) { | ||
if (pkg.startsWith(DEPENDENCY_GROUP) && pkg in newJson) { | ||
if (version[0] === '^' || version[0] === '~') { | ||
updated[pkg] = version[0] + absoluteVersion(newJson[pkg]); | ||
} else { | ||
updated[pkg] = absoluteVersion(newJson[pkg]); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
function absoluteVersion(version: string): string { | ||
if (version.length > 0 && (version[0] === '^' || version[0] === '~')) { | ||
return version.substring(1); | ||
} | ||
return version; | ||
} | ||
|
||
async function upgradeLabDependencies(): Promise<void> { | ||
const args: string[] = process.argv.slice(2); | ||
|
||
if (args.length !== 2 || args[0] !== '--set-version') { | ||
console.error('Usage: node script.js --set-version <version>'); | ||
process.exit(1); | ||
} | ||
|
||
const newVersion: string = args[1]; | ||
await updatePackageJson(newVersion); | ||
} | ||
|
||
upgradeLabDependencies(); |