diff --git a/.github/workflows/upgrade-juypterlab-dependencies.yml b/.github/workflows/upgrade-jupyterlab-dependencies.yml similarity index 68% rename from .github/workflows/upgrade-juypterlab-dependencies.yml rename to .github/workflows/upgrade-jupyterlab-dependencies.yml index 987d16c85e..735a808c55 100644 --- a/.github/workflows/upgrade-juypterlab-dependencies.yml +++ b/.github/workflows/upgrade-jupyterlab-dependencies.yml @@ -10,11 +10,17 @@ on: default: latest required: true type: string + branch: + description: 'The branch to target' + default: main + required: false + type: string env: version_tag: 'latest' permissions: + actions: write contents: write pull-requests: write @@ -52,20 +58,23 @@ jobs: set -eux for version in ${{ inputs.version || env.version_tag }} do - export LATEST=$(node buildutils/lib/get-latest-lab-version.js --set-version $version) + if [[ "${version}" == "latest" ]]; then + export LATEST=$(jlpm run get:lab:version --set-version ${version}) + else + export LATEST=${version} + fi done echo "latest=${LATEST}" >> $GITHUB_ENV - node buildutils/lib/upgrade-lab-dependencies.js --set-version ${LATEST} + jlpm upgrade:lab:dependencies --set-version ${LATEST} if [[ ! -z "$(git status --porcelain package.json)" ]]; then jlpm install + jlpm deduplicate fi - name: Create a PR - shell: bash env: - GITHUB_USER: ${{ secrets.G_USER }} - GITHUB_TOKEN: ${{ secrets.G_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -eux @@ -80,13 +89,15 @@ jobs: 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 config user.name "github-actions[bot]" + git config user.email 'github-actions[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.". + gh pr create \ + --base ${{ inputs.branch || 'main' }} \ + --title "Update to JupyterLab v${LATEST}" \ + --body "New JupyterLab release [v${LATEST}](https://github.com/jupyterlab/jupyterlab/releases/tag/v${LATEST}) is available. Please review the lock file carefully." fi fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e580deb68..550c15874c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -82,4 +82,4 @@ repos: name: integrity entry: 'npm run integrity --force' language: node - stages: [push] + stages: [pre-push] diff --git a/buildutils/src/upgrade-lab-dependencies.ts b/buildutils/src/upgrade-lab-dependencies.ts index 36242c8af8..9460f3f857 100644 --- a/buildutils/src/upgrade-lab-dependencies.ts +++ b/buildutils/src/upgrade-lab-dependencies.ts @@ -21,6 +21,54 @@ const PACKAGE_JSON_PATHS: string[] = [ const DEPENDENCY_GROUP = '@jupyterlab'; +interface IVersion { + major: number; + minor: number; + patch: number; + preRelease?: string; +} + +function parseVersion(version: string): IVersion { + const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:(a|b|rc)(\d+))?$/); + if (!match) { + throw new Error(`Invalid version format: ${version}`); + } + + const [, major, minor, patch, type, preVersion] = match; + const baseVersion = { + major: parseInt(major, 10), + minor: parseInt(minor, 10), + patch: parseInt(patch, 10), + }; + + if (type && preVersion) { + return { + ...baseVersion, + preRelease: `${type}${preVersion}`, + }; + } + + return baseVersion; +} + +function getVersionRange(version: IVersion): string { + const baseVersion = `${version.major}.${version.minor}.${version.patch}${ + version.preRelease ?? '' + }`; + return `>=${baseVersion},<${version.major}.${version.minor + 1}`; +} + +function updateVersionInFile( + filePath: string, + pattern: RegExp, + version: IVersion +): void { + const content = fs.readFileSync(filePath, 'utf-8'); + const versionRange = getVersionRange(version); + const updatedContent = content.replace(pattern, `$1${versionRange}`); + fs.writeFileSync(filePath, updatedContent); +} + async function updatePackageJson(newVersion: string): Promise { const url = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`; const response = await fetch(url); @@ -89,6 +137,18 @@ function absoluteVersion(version: string): string { return version; } +async function updatePyprojectToml(version: IVersion): Promise { + const filePath = path.resolve('pyproject.toml'); + const pattern = /(jupyterlab>=)[\d.]+(?:a|b|rc\d+)?,<[\d.]+/g; + updateVersionInFile(filePath, pattern, version); +} + +async function updatePreCommitConfig(version: IVersion): Promise { + const filePath = path.resolve('.pre-commit-config.yaml'); + const pattern = /(jupyterlab)(?:>=|==)[\d.]+(?:,<[\d.]+)?(?="|,|\s|$)/; + updateVersionInFile(filePath, pattern, version); +} + async function upgradeLabDependencies(): Promise { const args: string[] = process.argv.slice(2); @@ -97,8 +157,10 @@ async function upgradeLabDependencies(): Promise { process.exit(1); } - const newVersion: string = args[1]; - await updatePackageJson(newVersion); + const version = parseVersion(args[1]); + await updatePackageJson(args[1]); // Keep original string version for package.json + await updatePyprojectToml(version); + await updatePreCommitConfig(version); } upgradeLabDependencies(); diff --git a/package.json b/package.json index bfc3fd2858..2c976b4f63 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "eslint": "eslint . --ext .ts,.tsx --fix", "eslint:check": "eslint . --ext .ts,.tsx", "eslint:files": "eslint --fix", + "get:lab:version": "node ./buildutils/lib/get-latest-lab-version.js", "integrity": "node buildutils/lib/ensure-repo.js", "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", "prettier:check": "prettier --list-different \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", @@ -39,6 +40,7 @@ "release:patch": "node ./buildutils/lib/release-patch.js", "test": "lerna run test", "update:dependency": "node ./node_modules/@jupyterlab/buildutils/lib/update-dependency.js --lerna", + "upgrade:lab:dependencies": "node ./buildutils/lib/upgrade-lab-dependencies.js", "watch": "run-p watch:lib watch:app", "watch:app": "lerna exec --stream --scope \"@jupyter-notebook/app\" jlpm watch", "watch:lib": "lerna exec --stream --scope @jupyter-notebook/metapackage jlpm watch"