From dbc615e33f21211f5ecc0c8997d5713a7c4b8162 Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Feb 2024 08:10:40 +0000
Subject: [PATCH 1/9] workflow to upgrade JupyterLab dependencies

---
 .../upgrade-juypterlab-dependencies.yml       | 86 +++++++++++++++++++
 scripts/get-latest-lab-version.py             | 44 ++++++++++
 scripts/upgrade-lab-dependencies.py           | 82 ++++++++++++++++++
 3 files changed, 212 insertions(+)
 create mode 100644 .github/workflows/upgrade-juypterlab-dependencies.yml
 create mode 100644 scripts/get-latest-lab-version.py
 create mode 100644 scripts/upgrade-lab-dependencies.py

diff --git a/.github/workflows/upgrade-juypterlab-dependencies.yml b/.github/workflows/upgrade-juypterlab-dependencies.yml
new file mode 100644
index 0000000000..39e380f164
--- /dev/null
+++ b/.github/workflows/upgrade-juypterlab-dependencies.yml
@@ -0,0 +1,86 @@
+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 requests
+          sudo apt-get install hub
+
+      - name: Install Node
+        uses: actions/setup-node@v2
+        with:
+          node-version: '20.x'
+
+      - name: Install npm dependencies
+        run: |
+          npm install --global yarn
+          yarn install
+
+      - name: Check for new releases and update
+        shell: bash
+        run: |
+          set -eux
+          for version in ${{ inputs.version || env.version_tag }} 
+          do 
+            export LATEST=$(python scripts/get-latest-lab-version.py --set-version $version)
+          done
+
+          echo "latest=${LATEST}" >> $GITHUB_ENV
+          python scripts/upgrade-lab-dependencies.py --set-version ${LATEST}
+          if [[ ! -z "$(git status --porcelain package.json)" ]]; then
+            yarn install
+          fi
+
+      - name: Create a PR
+        shell: bash
+        env:
+          GITHUB_USER: ${{ secrets.G_USER }}
+          GITHUB_TOKEN: ${{ secrets.G_TOKEN }}
+        run: |
+          set -eux
+          # if resulted in any change:
+          export LATEST=${{ env.latest }}
+          if [[ ! -z "$(git status --porcelain package.json)" ]]; then
+            export BRANCH_NAME=update-to-v${LATEST}
+
+            # this will fail if the branch already exists which means we won't have duplicate PRs
+            git checkout -b "${BRANCH_NAME}"
+            git config user.name "JupyterLab Desktop 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
diff --git a/scripts/get-latest-lab-version.py b/scripts/get-latest-lab-version.py
new file mode 100644
index 0000000000..b74be46169
--- /dev/null
+++ b/scripts/get-latest-lab-version.py
@@ -0,0 +1,44 @@
+from urllib.request import urlopen
+import json
+import argparse
+
+
+REPOSITORY = "jupyterlab"
+ORGANIZATION = "jupyterlab"
+
+
+def extract_version_from_releases(releases, version_tag):
+    for release in releases:
+        tag_name = release['tag_name']
+        if (version_tag == "latest"):
+            if(not release['prerelease'] and not release['draft']):
+                return tag_name
+        
+        elif (version_tag == tag_name):
+            return tag_name
+    return None
+
+
+def find_version(owner, repository, version_tag):
+    """Find latest stable release on GitHub for given repository."""
+    endpoint = f"https://api.github.com/repos/{owner}/{repository}/releases"
+    releases = json.loads(urlopen(endpoint).read())
+
+    version = extract_version_from_releases(releases,version_tag)
+
+    if version is None:
+        raise ValueError('Invalid release tag')
+    if not version.startswith('v'):
+        raise ValueError('Unexpected release tag name format: does not start with v')
+    return version[1:]
+
+def main():
+    parser = argparse.ArgumentParser(description='Update dependencies in package.json.')
+    parser.add_argument('--set-version', dest='version_tag', type=str, required=True, help='Set version tag')
+    
+    args = parser.parse_args()
+    print(find_version(owner=ORGANIZATION,repository=REPOSITORY,version_tag=args.version_tag))
+    
+
+if __name__ == '__main__':   
+    main()
\ No newline at end of file
diff --git a/scripts/upgrade-lab-dependencies.py b/scripts/upgrade-lab-dependencies.py
new file mode 100644
index 0000000000..11dfa92106
--- /dev/null
+++ b/scripts/upgrade-lab-dependencies.py
@@ -0,0 +1,82 @@
+import json
+import argparse
+import requests
+
+PACKAGE_JSON_PATHS = [
+    "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",
+]
+
+DEPENDENCY_NAME = "@jupyterlab"
+
+def update_package_json(new_version):
+    url = f'https://api.github.com/repos/{owner}/{repository}/releases'
+    
+    response = requests.get(url)
+    
+    if response.status_code != 200:
+        print(f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}")
+        return
+    
+    new_package_json = response.json()
+
+    for path in PACKAGE_JSON_PATHS:
+        with open(path, 'r') as package_json_file:
+            existing_package_json = json.load(package_json_file)
+        
+        new_dependencies = {**new_package_json.get('devDependencies', {}), **new_package_json.get('resolutions', {})}
+        update_dependencies(existing_package_json, new_dependencies)
+
+        with open(path, 'w') as file:
+            json.dump(existing_package_json, file, indent=2)
+            file.write('\n') 
+
+
+def update_dependencies(existing_json, new_json):
+    if(existing_json == None):
+        return
+    
+    section_paths = ['resolutions','dependencies', 'devDependencies']
+
+    for section in section_paths:
+        if(existing_json.get(section) == None):
+            continue
+
+        updated = existing_json.get(section)
+        for package, version in existing_json.get(section).items():
+            if(package.startswith(DEPENDENCY_NAME) and package in new_json):
+                # To maintaing the existing prefix ~ or ^
+                if version[0] in ('^','~'):
+                    updated[package] = version[0] + absolute_version(new_json[package])
+                else:
+                    updated[package] = absolute_version(new_json[package])
+
+def absolute_version(version):
+    if len(version) > 0 and version[0] in ('^','~'):
+        return version[1:];
+    return version;
+
+
+def main():
+    parser = argparse.ArgumentParser(description='Update dependencies in package.json.')
+    parser.add_argument('--set-version', dest='new_version', type=str, required=True, help='New version to set for JupyterLab dependencies')
+    
+    args = parser.parse_args()
+    update_package_json(args.new_version)
+
+
+if(__name__ == "__main__"):
+    main()
\ No newline at end of file

From 2fb9d893040f3a1bea93a32b41f15d9d23f911ee Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Feb 2024 08:28:56 +0000
Subject: [PATCH 2/9] Update upgrade-lab-dependencies.py

---
 scripts/upgrade-lab-dependencies.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/upgrade-lab-dependencies.py b/scripts/upgrade-lab-dependencies.py
index 11dfa92106..31b10fc309 100644
--- a/scripts/upgrade-lab-dependencies.py
+++ b/scripts/upgrade-lab-dependencies.py
@@ -23,7 +23,7 @@
 DEPENDENCY_NAME = "@jupyterlab"
 
 def update_package_json(new_version):
-    url = f'https://api.github.com/repos/{owner}/{repository}/releases'
+    url = f'https://raw.githubusercontent.com/jupyterlab/jupyterlab/v{new_version}/jupyterlab/staging/package.json'
     
     response = requests.get(url)
     

From 0538550ffb47b5bf5f4526f33e6dfe83d80a9bbc Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Feb 2024 18:15:42 +0000
Subject: [PATCH 3/9] Prettier code for Test Lint check

---
 .../upgrade-juypterlab-dependencies.yml       |  7 ++---
 scripts/get-latest-lab-version.py             | 29 +++++++++++--------
 scripts/upgrade-lab-dependencies.py           | 20 +++++++------
 3 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/.github/workflows/upgrade-juypterlab-dependencies.yml b/.github/workflows/upgrade-juypterlab-dependencies.yml
index 39e380f164..5edfb91a18 100644
--- a/.github/workflows/upgrade-juypterlab-dependencies.yml
+++ b/.github/workflows/upgrade-juypterlab-dependencies.yml
@@ -33,7 +33,7 @@ jobs:
 
       - name: Install required dependencies
         run: |
-          python -m pip install requests
+          python -m pip install requests jupyterlab
           sudo apt-get install hub
 
       - name: Install Node
@@ -43,8 +43,7 @@ jobs:
 
       - name: Install npm dependencies
         run: |
-          npm install --global yarn
-          yarn install
+          jlpm install
 
       - name: Check for new releases and update
         shell: bash
@@ -58,7 +57,7 @@ jobs:
           echo "latest=${LATEST}" >> $GITHUB_ENV
           python scripts/upgrade-lab-dependencies.py --set-version ${LATEST}
           if [[ ! -z "$(git status --porcelain package.json)" ]]; then
-            yarn install
+            jlpm install
           fi
 
       - name: Create a PR
diff --git a/scripts/get-latest-lab-version.py b/scripts/get-latest-lab-version.py
index b74be46169..e94eb4e74c 100644
--- a/scripts/get-latest-lab-version.py
+++ b/scripts/get-latest-lab-version.py
@@ -1,7 +1,6 @@
-from urllib.request import urlopen
-import json
 import argparse
-
+import requests
+import sys
 
 REPOSITORY = "jupyterlab"
 ORGANIZATION = "jupyterlab"
@@ -20,16 +19,20 @@ def extract_version_from_releases(releases, version_tag):
 
 
 def find_version(owner, repository, version_tag):
-    """Find latest stable release on GitHub for given repository."""
-    endpoint = f"https://api.github.com/repos/{owner}/{repository}/releases"
-    releases = json.loads(urlopen(endpoint).read())
+    url = f"https://api.github.com/repos/{owner}/{repository}/releases"
+
+    response = requests.get(url, timeout=10)
+    
+    if response.status_code != 200:
+        error_message = f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        raise requests.HTTPError(error_message)
 
-    version = extract_version_from_releases(releases,version_tag)
+    releases = response.json()
+    version = extract_version_from_releases(releases, version_tag)
 
     if version is None:
-        raise ValueError('Invalid release tag')
-    if not version.startswith('v'):
-        raise ValueError('Unexpected release tag name format: does not start with v')
+        error_message = 'Invalid release tag'
+        raise ValueError(error_message)
     return version[1:]
 
 def main():
@@ -37,8 +40,10 @@ def main():
     parser.add_argument('--set-version', dest='version_tag', type=str, required=True, help='Set version tag')
     
     args = parser.parse_args()
-    print(find_version(owner=ORGANIZATION,repository=REPOSITORY,version_tag=args.version_tag))
-    
+
+    result = find_version(owner=ORGANIZATION, repository=REPOSITORY, version_tag=args.version_tag)
+    sys.stdout.write(result)
+    sys.stdout.flush()
 
 if __name__ == '__main__':   
     main()
\ No newline at end of file
diff --git a/scripts/upgrade-lab-dependencies.py b/scripts/upgrade-lab-dependencies.py
index 31b10fc309..787e468df5 100644
--- a/scripts/upgrade-lab-dependencies.py
+++ b/scripts/upgrade-lab-dependencies.py
@@ -1,5 +1,6 @@
-import json
 import argparse
+import json
+from pathlib import Path
 import requests
 
 PACKAGE_JSON_PATHS = [
@@ -25,40 +26,41 @@
 def update_package_json(new_version):
     url = f'https://raw.githubusercontent.com/jupyterlab/jupyterlab/v{new_version}/jupyterlab/staging/package.json'
     
-    response = requests.get(url)
+    response = requests.get(url, timeout=10)
     
     if response.status_code != 200:
-        print(f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}")
-        return
+        error_message = f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        raise requests.HTTPError(error_message)
     
     new_package_json = response.json()
 
     for path in PACKAGE_JSON_PATHS:
-        with open(path, 'r') as package_json_file:
+        file_path = Path(path)
+        with file_path.open(mode='r') as package_json_file:
             existing_package_json = json.load(package_json_file)
         
         new_dependencies = {**new_package_json.get('devDependencies', {}), **new_package_json.get('resolutions', {})}
         update_dependencies(existing_package_json, new_dependencies)
 
-        with open(path, 'w') as file:
+        with file_path.open(mode='w') as file:
             json.dump(existing_package_json, file, indent=2)
             file.write('\n') 
 
 
 def update_dependencies(existing_json, new_json):
-    if(existing_json == None):
+    if(existing_json is None):
         return
     
     section_paths = ['resolutions','dependencies', 'devDependencies']
 
     for section in section_paths:
-        if(existing_json.get(section) == None):
+        if(existing_json.get(section) is None):
             continue
 
         updated = existing_json.get(section)
         for package, version in existing_json.get(section).items():
             if(package.startswith(DEPENDENCY_NAME) and package in new_json):
-                # To maintaing the existing prefix ~ or ^
+                # To maintain the existing prefix ~ or ^
                 if version[0] in ('^','~'):
                     updated[package] = version[0] + absolute_version(new_json[package])
                 else:

From 4de74363e653be90f6fb88c67a576aa4e7920ffa Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Feb 2024 18:25:23 +0000
Subject: [PATCH 4/9] Prettier code for Test Lint checks

---
 scripts/get-latest-lab-version.py   | 35 +++++++++-------
 scripts/upgrade-lab-dependencies.py | 62 ++++++++++++++++++-----------
 2 files changed, 59 insertions(+), 38 deletions(-)

diff --git a/scripts/get-latest-lab-version.py b/scripts/get-latest-lab-version.py
index e94eb4e74c..f4772c2d7d 100644
--- a/scripts/get-latest-lab-version.py
+++ b/scripts/get-latest-lab-version.py
@@ -1,19 +1,20 @@
 import argparse
-import requests
 import sys
 
+import requests
+
 REPOSITORY = "jupyterlab"
 ORGANIZATION = "jupyterlab"
 
 
 def extract_version_from_releases(releases, version_tag):
     for release in releases:
-        tag_name = release['tag_name']
-        if (version_tag == "latest"):
-            if(not release['prerelease'] and not release['draft']):
+        tag_name = release["tag_name"]
+        if version_tag == "latest":
+            if not release["prerelease"] and not release["draft"]:
                 return tag_name
-        
-        elif (version_tag == tag_name):
+
+        elif version_tag == tag_name:
             return tag_name
     return None
 
@@ -22,28 +23,34 @@ def find_version(owner, repository, version_tag):
     url = f"https://api.github.com/repos/{owner}/{repository}/releases"
 
     response = requests.get(url, timeout=10)
-    
+
     if response.status_code != 200:
-        error_message = f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        error_message = (
+            f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        )
         raise requests.HTTPError(error_message)
 
     releases = response.json()
     version = extract_version_from_releases(releases, version_tag)
 
     if version is None:
-        error_message = 'Invalid release tag'
+        error_message = "Invalid release tag"
         raise ValueError(error_message)
     return version[1:]
 
+
 def main():
-    parser = argparse.ArgumentParser(description='Update dependencies in package.json.')
-    parser.add_argument('--set-version', dest='version_tag', type=str, required=True, help='Set version tag')
-    
+    parser = argparse.ArgumentParser(description="Update dependencies in package.json.")
+    parser.add_argument(
+        "--set-version", dest="version_tag", type=str, required=True, help="Set version tag"
+    )
+
     args = parser.parse_args()
 
     result = find_version(owner=ORGANIZATION, repository=REPOSITORY, version_tag=args.version_tag)
     sys.stdout.write(result)
     sys.stdout.flush()
 
-if __name__ == '__main__':   
-    main()
\ No newline at end of file
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/upgrade-lab-dependencies.py b/scripts/upgrade-lab-dependencies.py
index 787e468df5..14d31231d3 100644
--- a/scripts/upgrade-lab-dependencies.py
+++ b/scripts/upgrade-lab-dependencies.py
@@ -1,6 +1,7 @@
 import argparse
 import json
 from pathlib import Path
+
 import requests
 
 PACKAGE_JSON_PATHS = [
@@ -23,62 +24,75 @@
 
 DEPENDENCY_NAME = "@jupyterlab"
 
+
 def update_package_json(new_version):
-    url = f'https://raw.githubusercontent.com/jupyterlab/jupyterlab/v{new_version}/jupyterlab/staging/package.json'
-    
+    url = f"https://raw.githubusercontent.com/jupyterlab/jupyterlab/v{new_version}/jupyterlab/staging/package.json"
+
     response = requests.get(url, timeout=10)
-    
+
     if response.status_code != 200:
-        error_message = f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        error_message = (
+            f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
+        )
         raise requests.HTTPError(error_message)
-    
+
     new_package_json = response.json()
 
     for path in PACKAGE_JSON_PATHS:
         file_path = Path(path)
-        with file_path.open(mode='r') as package_json_file:
+        with file_path.open(mode="r") as package_json_file:
             existing_package_json = json.load(package_json_file)
-        
-        new_dependencies = {**new_package_json.get('devDependencies', {}), **new_package_json.get('resolutions', {})}
+
+        new_dependencies = {
+            **new_package_json.get("devDependencies", {}),
+            **new_package_json.get("resolutions", {}),
+        }
         update_dependencies(existing_package_json, new_dependencies)
 
-        with file_path.open(mode='w') as file:
+        with file_path.open(mode="w") as file:
             json.dump(existing_package_json, file, indent=2)
-            file.write('\n') 
+            file.write("\n")
 
 
 def update_dependencies(existing_json, new_json):
-    if(existing_json is None):
+    if existing_json is None:
         return
-    
-    section_paths = ['resolutions','dependencies', 'devDependencies']
+
+    section_paths = ["resolutions", "dependencies", "devDependencies"]
 
     for section in section_paths:
-        if(existing_json.get(section) is None):
+        if existing_json.get(section) is None:
             continue
 
         updated = existing_json.get(section)
         for package, version in existing_json.get(section).items():
-            if(package.startswith(DEPENDENCY_NAME) and package in new_json):
+            if package.startswith(DEPENDENCY_NAME) and package in new_json:
                 # To maintain the existing prefix ~ or ^
-                if version[0] in ('^','~'):
+                if version[0] in ("^", "~"):
                     updated[package] = version[0] + absolute_version(new_json[package])
                 else:
                     updated[package] = absolute_version(new_json[package])
 
+
 def absolute_version(version):
-    if len(version) > 0 and version[0] in ('^','~'):
-        return version[1:];
-    return version;
+    if len(version) > 0 and version[0] in ("^", "~"):
+        return version[1:]
+    return version
 
 
 def main():
-    parser = argparse.ArgumentParser(description='Update dependencies in package.json.')
-    parser.add_argument('--set-version', dest='new_version', type=str, required=True, help='New version to set for JupyterLab dependencies')
-    
+    parser = argparse.ArgumentParser(description="Update dependencies in package.json.")
+    parser.add_argument(
+        "--set-version",
+        dest="new_version",
+        type=str,
+        required=True,
+        help="New version to set for JupyterLab dependencies",
+    )
+
     args = parser.parse_args()
     update_package_json(args.new_version)
 
 
-if(__name__ == "__main__"):
-    main()
\ No newline at end of file
+if __name__ == "__main__":
+    main()

From 9d7ea27588a88431bd57f19375606163c305e41c Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Feb 2024 18:29:16 +0000
Subject: [PATCH 5/9] Prettier code for Test Lint check

---
 .github/workflows/upgrade-juypterlab-dependencies.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/upgrade-juypterlab-dependencies.yml b/.github/workflows/upgrade-juypterlab-dependencies.yml
index 5edfb91a18..bbf8638e60 100644
--- a/.github/workflows/upgrade-juypterlab-dependencies.yml
+++ b/.github/workflows/upgrade-juypterlab-dependencies.yml
@@ -4,7 +4,7 @@ on:
   schedule:
     - cron: 30 17 * * *
   workflow_dispatch:
-    inputs:        
+    inputs:
       version:
         description: 'JupyterLab version'
         default: latest
@@ -13,7 +13,7 @@ on:
 
 env:
   version_tag: 'latest'
-        
+
 permissions:
   contents: write
   pull-requests: write
@@ -49,8 +49,8 @@ jobs:
         shell: bash
         run: |
           set -eux
-          for version in ${{ inputs.version || env.version_tag }} 
-          do 
+          for version in ${{ inputs.version || env.version_tag }}
+          do
             export LATEST=$(python scripts/get-latest-lab-version.py --set-version $version)
           done
 
@@ -79,7 +79,7 @@ jobs:
 
             git commit . -m "Update to JupyterLab v${LATEST}"
 
-            git push --set-upstream origin "${BRANCH_NAME}"            
+            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

From 81e218a9a1a60b61cf23f0fcc315f22e48336a5e Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Sun, 31 Mar 2024 10:17:22 +0000
Subject: [PATCH 6/9] added ts scripts to upgrade lab dependencies

---
 .../upgrade-juypterlab-dependencies.yml       | 37 ++++---
 buildutils/src/get-latest-lab-version.ts      | 49 ++++++++++
 buildutils/src/upgrade-lab-dependencies.ts    | 95 ++++++++++++++++++
 scripts/get-latest-lab-version.py             | 56 -----------
 scripts/upgrade-lab-dependencies.py           | 98 -------------------
 5 files changed, 166 insertions(+), 169 deletions(-)
 create mode 100644 buildutils/src/get-latest-lab-version.ts
 create mode 100644 buildutils/src/upgrade-lab-dependencies.ts
 delete mode 100644 scripts/get-latest-lab-version.py
 delete mode 100644 scripts/upgrade-lab-dependencies.py

diff --git a/.github/workflows/upgrade-juypterlab-dependencies.yml b/.github/workflows/upgrade-juypterlab-dependencies.yml
index bbf8638e60..987d16c85e 100644
--- a/.github/workflows/upgrade-juypterlab-dependencies.yml
+++ b/.github/workflows/upgrade-juypterlab-dependencies.yml
@@ -33,7 +33,7 @@ jobs:
 
       - name: Install required dependencies
         run: |
-          python -m pip install requests jupyterlab
+          python -m pip install jupyterlab
           sudo apt-get install hub
 
       - name: Install Node
@@ -41,9 +41,10 @@ jobs:
         with:
           node-version: '20.x'
 
-      - name: Install npm dependencies
+      - name: Install npm dependencies and build buildutils
         run: |
           jlpm install
+          jlpm run build:utils
 
       - name: Check for new releases and update
         shell: bash
@@ -51,11 +52,11 @@ jobs:
           set -eux
           for version in ${{ inputs.version || env.version_tag }}
           do
-            export LATEST=$(python scripts/get-latest-lab-version.py --set-version $version)
+            export LATEST=$(node buildutils/lib/get-latest-lab-version.js --set-version $version)
           done
 
           echo "latest=${LATEST}" >> $GITHUB_ENV
-          python scripts/upgrade-lab-dependencies.py --set-version ${LATEST}
+          node buildutils/lib/upgrade-lab-dependencies.js --set-version ${LATEST}
           if [[ ! -z "$(git status --porcelain package.json)" ]]; then
             jlpm install
           fi
@@ -67,19 +68,25 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.G_TOKEN }}
         run: |
           set -eux
-          # if resulted in any change:
+
           export LATEST=${{ env.latest }}
-          if [[ ! -z "$(git status --porcelain package.json)" ]]; then
-            export BRANCH_NAME=update-to-v${LATEST}
+          export BRANCH_NAME=update-to-v${LATEST}
 
-            # this will fail if the branch already exists which means we won't have duplicate PRs
-            git checkout -b "${BRANCH_NAME}"
-            git config user.name "JupyterLab Desktop Bot"
-            git config user.email 'jupyterlab-bot@users.noreply.github.com'
+          # 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 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.".
+              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
diff --git a/buildutils/src/get-latest-lab-version.ts b/buildutils/src/get-latest-lab-version.ts
new file mode 100644
index 0000000000..46d99c97f0
--- /dev/null
+++ b/buildutils/src/get-latest-lab-version.ts
@@ -0,0 +1,49 @@
+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: string = `https://api.github.com/repos/jupyterlab/jupyterlab/releases`;
+    const response = await fetch(url);
+    if (!response.ok) {
+        const error_message: string = `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: string = "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();
diff --git a/buildutils/src/upgrade-lab-dependencies.ts b/buildutils/src/upgrade-lab-dependencies.ts
new file mode 100644
index 0000000000..1fde1ce299
--- /dev/null
+++ b/buildutils/src/upgrade-lab-dependencies.ts
@@ -0,0 +1,95 @@
+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: string = "@jupyterlab";
+
+async function updatePackageJson(newVersion: string): Promise<void> {
+    const url: string = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`;
+    const response = await fetch(url);
+
+    if (!response.ok) {
+        const errorMessage: string = `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));
+    }
+}
+
+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();
diff --git a/scripts/get-latest-lab-version.py b/scripts/get-latest-lab-version.py
deleted file mode 100644
index f4772c2d7d..0000000000
--- a/scripts/get-latest-lab-version.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import argparse
-import sys
-
-import requests
-
-REPOSITORY = "jupyterlab"
-ORGANIZATION = "jupyterlab"
-
-
-def extract_version_from_releases(releases, version_tag):
-    for release in releases:
-        tag_name = release["tag_name"]
-        if version_tag == "latest":
-            if not release["prerelease"] and not release["draft"]:
-                return tag_name
-
-        elif version_tag == tag_name:
-            return tag_name
-    return None
-
-
-def find_version(owner, repository, version_tag):
-    url = f"https://api.github.com/repos/{owner}/{repository}/releases"
-
-    response = requests.get(url, timeout=10)
-
-    if response.status_code != 200:
-        error_message = (
-            f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
-        )
-        raise requests.HTTPError(error_message)
-
-    releases = response.json()
-    version = extract_version_from_releases(releases, version_tag)
-
-    if version is None:
-        error_message = "Invalid release tag"
-        raise ValueError(error_message)
-    return version[1:]
-
-
-def main():
-    parser = argparse.ArgumentParser(description="Update dependencies in package.json.")
-    parser.add_argument(
-        "--set-version", dest="version_tag", type=str, required=True, help="Set version tag"
-    )
-
-    args = parser.parse_args()
-
-    result = find_version(owner=ORGANIZATION, repository=REPOSITORY, version_tag=args.version_tag)
-    sys.stdout.write(result)
-    sys.stdout.flush()
-
-
-if __name__ == "__main__":
-    main()
diff --git a/scripts/upgrade-lab-dependencies.py b/scripts/upgrade-lab-dependencies.py
deleted file mode 100644
index 14d31231d3..0000000000
--- a/scripts/upgrade-lab-dependencies.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import argparse
-import json
-from pathlib import Path
-
-import requests
-
-PACKAGE_JSON_PATHS = [
-    "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",
-]
-
-DEPENDENCY_NAME = "@jupyterlab"
-
-
-def update_package_json(new_version):
-    url = f"https://raw.githubusercontent.com/jupyterlab/jupyterlab/v{new_version}/jupyterlab/staging/package.json"
-
-    response = requests.get(url, timeout=10)
-
-    if response.status_code != 200:
-        error_message = (
-            f"Failed to fetch package.json from {url}. HTTP status code: {response.status_code}"
-        )
-        raise requests.HTTPError(error_message)
-
-    new_package_json = response.json()
-
-    for path in PACKAGE_JSON_PATHS:
-        file_path = Path(path)
-        with file_path.open(mode="r") as package_json_file:
-            existing_package_json = json.load(package_json_file)
-
-        new_dependencies = {
-            **new_package_json.get("devDependencies", {}),
-            **new_package_json.get("resolutions", {}),
-        }
-        update_dependencies(existing_package_json, new_dependencies)
-
-        with file_path.open(mode="w") as file:
-            json.dump(existing_package_json, file, indent=2)
-            file.write("\n")
-
-
-def update_dependencies(existing_json, new_json):
-    if existing_json is None:
-        return
-
-    section_paths = ["resolutions", "dependencies", "devDependencies"]
-
-    for section in section_paths:
-        if existing_json.get(section) is None:
-            continue
-
-        updated = existing_json.get(section)
-        for package, version in existing_json.get(section).items():
-            if package.startswith(DEPENDENCY_NAME) and package in new_json:
-                # To maintain the existing prefix ~ or ^
-                if version[0] in ("^", "~"):
-                    updated[package] = version[0] + absolute_version(new_json[package])
-                else:
-                    updated[package] = absolute_version(new_json[package])
-
-
-def absolute_version(version):
-    if len(version) > 0 and version[0] in ("^", "~"):
-        return version[1:]
-    return version
-
-
-def main():
-    parser = argparse.ArgumentParser(description="Update dependencies in package.json.")
-    parser.add_argument(
-        "--set-version",
-        dest="new_version",
-        type=str,
-        required=True,
-        help="New version to set for JupyterLab dependencies",
-    )
-
-    args = parser.parse_args()
-    update_package_json(args.new_version)
-
-
-if __name__ == "__main__":
-    main()

From 2154f0f039e9a5f3b8c42f94934dc8f6023b7ecd Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Sun, 31 Mar 2024 10:32:43 +0000
Subject: [PATCH 7/9] Prettier code for test lint check

---
 buildutils/src/get-latest-lab-version.ts   |  81 +++++++------
 buildutils/src/upgrade-lab-dependencies.ts | 130 +++++++++++----------
 2 files changed, 111 insertions(+), 100 deletions(-)

diff --git a/buildutils/src/get-latest-lab-version.ts b/buildutils/src/get-latest-lab-version.ts
index 46d99c97f0..166e035a59 100644
--- a/buildutils/src/get-latest-lab-version.ts
+++ b/buildutils/src/get-latest-lab-version.ts
@@ -1,49 +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;
-        }
+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;
+  }
+  return null;
 }
 
 async function findVersion(versionTag: string): Promise<string> {
-    const url: string = `https://api.github.com/repos/jupyterlab/jupyterlab/releases`;
-    const response = await fetch(url);
-    if (!response.ok) {
-        const error_message: string = `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: string = "Invalid release tag";
-        throw new Error(error_message);
-    }
-    return version.substring(1);
+  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];
+  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);
-    }
+  try {
+    const result: string = await findVersion(version_tag);
+    console.log(result);
+  } catch (error: any) {
+    console.error('Error:', error.message);
+    process.exit(1);
+  }
 }
 
 getLatestLabVersion();
diff --git a/buildutils/src/upgrade-lab-dependencies.ts b/buildutils/src/upgrade-lab-dependencies.ts
index 1fde1ce299..570f2d2417 100644
--- a/buildutils/src/upgrade-lab-dependencies.ts
+++ b/buildutils/src/upgrade-lab-dependencies.ts
@@ -2,94 +2,100 @@ 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",
+  '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: string = "@jupyterlab";
+const DEPENDENCY_GROUP = '@jupyterlab';
 
 async function updatePackageJson(newVersion: string): Promise<void> {
-    const url: string = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`;
-    const response = await fetch(url);
+  const url = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`;
+  const response = await fetch(url);
 
-    if (!response.ok) {
-        const errorMessage: string = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`;
-        throw new Error(errorMessage);
-    }
+  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();
+  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'));
+  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
-        };
+    const newDependencies = {
+      ...newPackageJson.devDependencies,
+      ...newPackageJson.resolutions,
+    };
 
-        updateDependencyVersion(existingPackageJson, newDependencies);
+    updateDependencyVersion(existingPackageJson, newDependencies);
 
-        fs.writeFileSync(filePath, JSON.stringify(existingPackageJson, null, 2));
-    }
+    fs.writeFileSync(filePath, JSON.stringify(existingPackageJson, null, 2));
+  }
 }
 
 function updateDependencyVersion(existingJson: any, newJson: any): void {
-    if (!existingJson) {
-        return;
+  if (!existingJson) {
+    return;
+  }
+
+  const sectionPaths: string[] = [
+    'resolutions',
+    'dependencies',
+    'devDependencies',
+  ];
+
+  for (const section of sectionPaths) {
+    if (!existingJson[section]) {
+      continue;
     }
 
-    const sectionPaths: string[] = ["resolutions", "dependencies", "devDependencies"];
+    const updated = existingJson[section];
 
-    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]);
-                }
-            }
+    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;
+  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);
+  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);
-    }
+  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);
+  const newVersion: string = args[1];
+  await updatePackageJson(newVersion);
 }
 
 upgradeLabDependencies();

From 026ed8b7e2152a59ee5bbdcef3516c555a8f5d4a Mon Sep 17 00:00:00 2001
From: Vishnutheep B <vishnutheep@gmail.com>
Date: Tue, 27 Aug 2024 17:20:12 +0530
Subject: [PATCH 8/9] Update buildutils/src/upgrade-lab-dependencies.ts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: MichaƂ Krassowski <5832902+krassowski@users.noreply.github.com>
---
 buildutils/src/upgrade-lab-dependencies.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/buildutils/src/upgrade-lab-dependencies.ts b/buildutils/src/upgrade-lab-dependencies.ts
index 570f2d2417..003243f8a8 100644
--- a/buildutils/src/upgrade-lab-dependencies.ts
+++ b/buildutils/src/upgrade-lab-dependencies.ts
@@ -43,7 +43,7 @@ async function updatePackageJson(newVersion: string): Promise<void> {
 
     updateDependencyVersion(existingPackageJson, newDependencies);
 
-    fs.writeFileSync(filePath, JSON.stringify(existingPackageJson, null, 2));
+    fs.writeFileSync(filePath, JSON.stringify(existingPackageJson, null, 2) + '\n');
   }
 }
 

From f4ef24391f0747c84601022eac8a9a98381e8f6d Mon Sep 17 00:00:00 2001
From: Jeremy Tuloup <jeremy.tuloup@gmail.com>
Date: Thu, 19 Dec 2024 20:11:11 +0100
Subject: [PATCH 9/9] Lint

---
 buildutils/src/upgrade-lab-dependencies.ts | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/buildutils/src/upgrade-lab-dependencies.ts b/buildutils/src/upgrade-lab-dependencies.ts
index 003243f8a8..36242c8af8 100644
--- a/buildutils/src/upgrade-lab-dependencies.ts
+++ b/buildutils/src/upgrade-lab-dependencies.ts
@@ -43,7 +43,10 @@ async function updatePackageJson(newVersion: string): Promise<void> {
 
     updateDependencyVersion(existingPackageJson, newDependencies);
 
-    fs.writeFileSync(filePath, JSON.stringify(existingPackageJson, null, 2) + '\n');
+    fs.writeFileSync(
+      filePath,
+      JSON.stringify(existingPackageJson, null, 2) + '\n'
+    );
   }
 }