diff --git a/.github/actions/build-extension/action.yml b/.github/actions/build-extension/action.yml new file mode 100644 index 00000000..d3dc63e2 --- /dev/null +++ b/.github/actions/build-extension/action.yml @@ -0,0 +1,145 @@ +name: 'Build Extension' +description: 'Build and test a JupyterLab extension' +inputs: + example: + description: 'The example to build' + required: true + run-ui-tests: + description: 'Whether to run UI tests' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Path filter + id: filter + uses: dorny/paths-filter@v2 + with: + filters: | + extension: + - '${{ inputs.example }}/**' + - name: Cache lerna + if: steps.filter.outputs.extension == 'true' + uses: actions/cache@v4 + with: + path: '**/node_modules' + key: ${{ runner.os }}-lerna-${{ hashFiles('**/package.json') }} + restore-keys: ${{ runner.os }}-lerna- + - name: Check config files + if: steps.filter.outputs.extension == 'true' + run: | + diff ../hello-world/setup.py setup.py + diff ../hello-world/tsconfig.json tsconfig.json + diff ../hello-world/.yarnrc.yml .yarnrc.yml + diff ../hello-world/ui-tests/jupyter_server_test_config.py ./ui-tests/jupyter_server_test_config.py + diff ../hello-world/ui-tests/playwright.config.js ./ui-tests/playwright.config.js + shell: bash + working-directory: ${{ inputs.example }} + - name: Install node + if: steps.filter.outputs.extension == 'true' + uses: actions/setup-node@v4 + with: + node-version: '18.x' + - name: Install Python + if: steps.filter.outputs.extension == 'true' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + architecture: 'x64' + - name: Get pip cache dir + if: steps.filter.outputs.extension == 'true' + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + shell: bash + - name: Cache pip + if: steps.filter.outputs.extension == 'true' + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('environment.yml') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install the Python dependencies + if: steps.filter.outputs.extension == 'true' + run: | + python -m pip install --upgrade pip "jupyterlab>=4.0.0" + shell: bash + # This is challenging to test in collaboration; + # got trouble with the file ID service and the uncontrolled auto-save + # - if: steps.filter.outputs.extension == 'true' && inputs.example == 'clap-button-message' + # run: | + # python -m pip install jupyter-collaboration + - name: Build the extension + if: steps.filter.outputs.extension == 'true' + run: | + # Same commands as in TL;DR to ensure it works + touch yarn.lock + pip install -e . -v + jupyter labextension develop . --overwrite + shell: bash + working-directory: ${{ inputs.example }} + - name: Lint the files + if: steps.filter.outputs.extension == 'true' + run: jlpm run lint:check + shell: bash + working-directory: ${{ inputs.example }} + - name: Check extension installation + if: steps.filter.outputs.extension == 'true' + run: | + jupyter labextension list 2>&1 | tee labextension.list + cat labextension.list | grep -ie "@jupyterlab-examples/*.*OK" + shell: bash + working-directory: ${{ inputs.example }} + # clap-button-message example will have one plugin failing as it is requiring a + # Jupyter Notebook specific token. This is expected. Hence we need to skip this + # test for that example + - name: Browser check + if: steps.filter.outputs.extension == 'true' && inputs.example != 'clap-button-message' + run: | + python -m jupyterlab.browser_check + shell: bash + + - name: Install galata + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + working-directory: ${{ inputs.example }}/ui-tests + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + run: jlpm install + shell: bash + - name: Set up browser cache + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/pw-browsers + key: ${{ runner.os }}-${{ hashFiles('yarn.lock') }} + - name: Install browser + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + run: jlpm playwright install chromium + working-directory: ${{ inputs.example }}/ui-tests + shell: bash + - name: Install kernel-output dependencies + if: steps.filter.outputs.extension == 'true' && inputs.example == 'kernel-output' + run: pip install numpy pandas + shell: bash + - name: Integration tests + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + working-directory: ${{ inputs.example }}/ui-tests + run: jlpm playwright test + shell: bash + - name: Upload UI Test artifacts + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' && always() + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.example }}-ui-test-output + path: | + ${{ inputs.example }}/ui-tests/test-results + - name: Uninstall extension + if: steps.filter.outputs.extension == 'true' && (runner.os == 'Linux' || runner.os == 'macOS') + run: | + export NAME=`python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['name'])"` + pip uninstall -y ${NAME} + shell: bash + working-directory: ${{ inputs.example }} \ No newline at end of file diff --git a/.github/actions/build-serverextension/action.yml b/.github/actions/build-serverextension/action.yml new file mode 100644 index 00000000..b7d62faa --- /dev/null +++ b/.github/actions/build-serverextension/action.yml @@ -0,0 +1,154 @@ +name: 'Build Server Extension' +description: 'Build and test the JupyterLab server extension' +inputs: + run-ui-tests: + description: 'Whether to run UI tests' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Path filter + id: filter + uses: dorny/paths-filter@v2 + with: + filters: | + extension: + - 'server-extension/**' + - name: Cache lerna + if: steps.filter.outputs.extension == 'true' + uses: actions/cache@v4 + with: + path: '**/node_modules' + key: ${{ runner.os }}-lerna-${{ hashFiles('server-extension/package.json') }} + restore-keys: ${{ runner.os }}-lerna- + - name: Install node + if: steps.filter.outputs.extension == 'true' + uses: actions/setup-node@v4 + with: + node-version: '18.x' + - name: Check config files + if: steps.filter.outputs.extension == 'true' + run: | + diff hello-world/setup.py server-extension/setup.py + diff hello-world/tsconfig.json server-extension/tsconfig.json + diff hello-world/.yarnrc.yml server-extension/.yarnrc.yml + diff hello-world/ui-tests/jupyter_server_test_config.py server-extension/ui-tests/jupyter_server_test_config.py + diff hello-world/ui-tests/playwright.config.js server-extension/ui-tests/playwright.config.js + shell: bash + - name: Install Python + if: steps.filter.outputs.extension == 'true' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + architecture: 'x64' + - name: Get pip cache dir + if: steps.filter.outputs.extension == 'true' + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + shell: bash + - name: Cache pip + if: steps.filter.outputs.extension == 'true' + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('environment.yml') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install the Python dependencies + if: steps.filter.outputs.extension == 'true' + run: | + python -m pip install --upgrade pip jupyterlab~=4.0.0 build + shell: bash + - name: Install the NPM dependencies + if: steps.filter.outputs.extension == 'true' + run: | + cd server-extension + jlpm + shell: bash + - name: Lint the files + if: steps.filter.outputs.extension == 'true' + run: | + cd server-extension + jlpm run lint:check + shell: bash + - name: Build extension as user + if: steps.filter.outputs.extension == 'true' + # Force the usage of the source distribution (good practice) + run: | + cd server-extension + python -m build --sdist + pip install ./dist/jupyterlab_examples_server* --pre --find-links=dist --no-cache-dir + python -m jupyterlab.browser_check + shell: bash + - name: Check extension as dev + if: steps.filter.outputs.extension == 'true' && (runner.os == 'Linux' || runner.os == 'macOS') + run: | + jupyter server extension list 2>&1 | tee serverextension.list + cat serverextension.list | grep -ie "jupyterlab_examples_server.*OK" + jupyter labextension list 2>&1 | tee labextension.list + cat labextension.list | grep -ie "@jupyterlab-examples/server-extension.*OK" + shell: bash + - name: Clean extension installation + if: steps.filter.outputs.extension == 'true' + run: | + pip uninstall -y jupyterlab_examples_server + jupyter lab clean + jupyter server extension list + jupyter labextension list + shell: bash + - name: Build extension as dev + if: steps.filter.outputs.extension == 'true' + run: | + cd server-extension + pip install . + shell: bash + - name: Check extension as dev + if: steps.filter.outputs.extension == 'true' && (runner.os == 'Linux' || runner.os == 'macOS') + run: | + jupyter server extension list 2>&1 | tee serverextension.list + cat serverextension.list | grep -ie "jupyterlab_examples_server.*OK" + jupyter labextension list 2>&1 | tee labextension.list + cat labextension.list | grep -ie "@jupyterlab-examples/server-extension.*OK" + python -m jupyterlab.browser_check + shell: bash + + - name: Install galata + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + working-directory: server-extension/ui-tests + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + run: jlpm install + shell: bash + - name: Set up browser cache + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/pw-browsers + key: ${{ runner.os }}-${{ hashFiles('yarn.lock') }} + - name: Install browser + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + run: jlpm playwright install chromium + working-directory: server-extension/ui-tests + shell: bash + - name: Integration tests + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' + working-directory: server-extension/ui-tests + run: jlpm playwright test + shell: bash + - name: Upload UI Test artifacts + if: steps.filter.outputs.extension == 'true' && inputs.run-ui-tests == 'true' && always() + uses: actions/upload-artifact@v4 + with: + name: server-extension-ui-test-output + path: | + server-extension/ui-tests/test-results + - name: Uninstall extension + if: steps.filter.outputs.extension == 'true' && (runner.os == 'Linux' || runner.os == 'macOS') + run: | + export NAME=`python -c "import tomllib; print(tomllib.load(open('server-extension/pyproject.toml', 'rb'))['project']['name'])"` + pip uninstall -y ${NAME} + shell: bash \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6061ef18..56f0c442 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,13 +7,13 @@ on: branches: '*' jobs: - build_extensions: - runs-on: ${{ matrix.os }} + build_extensions_linux: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - example: + example: - clap-button-message - cell-toolbar - codemirror-extension @@ -43,279 +43,94 @@ jobs: - toolbar-button - toparea-text-widget - widgets - os: [ubuntu-latest, macos-latest, windows-latest] - defaults: - run: - working-directory: ${{ matrix.example }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Path filter - id: filter - uses: dorny/paths-filter@v2 - with: - filters: | - extension: - - '${{ matrix.example }}/**' - - name: Cache lerna - if: steps.filter.outputs.extension == 'true' - uses: actions/cache@v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-lerna-${{ hashFiles('**/package.json') }} - restore-keys: ${{ runner.os }}-lerna- - - name: Check config files - if: steps.filter.outputs.extension == 'true' - run: | - diff ../hello-world/setup.py setup.py - diff ../hello-world/tsconfig.json tsconfig.json - diff ../hello-world/.yarnrc.yml .yarnrc.yml - diff ../hello-world/ui-tests/jupyter_server_test_config.py ./ui-tests/jupyter_server_test_config.py - diff ../hello-world/ui-tests/playwright.config.js ./ui-tests/playwright.config.js - shell: bash - - name: Install node - if: steps.filter.outputs.extension == 'true' - uses: actions/setup-node@v4 - with: - node-version: '18.x' - - name: Install Python - if: steps.filter.outputs.extension == 'true' - uses: actions/setup-python@v5 - with: - python-version: '3.11' - architecture: 'x64' - - name: Get pip cache dir - if: steps.filter.outputs.extension == 'true' - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - shell: bash - - name: Cache pip - if: steps.filter.outputs.extension == 'true' - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('environment.yml') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Install the Python dependencies - if: steps.filter.outputs.extension == 'true' - run: | - python -m pip install --upgrade pip "jupyterlab>=4.0.0" - # This is challenging to test in collaboration; - # got trouble with the file ID service and the uncontrolled auto-save - # - if: steps.filter.outputs.extension == 'true' && matrix.example == 'clap-button-message' - # run: | - # python -m pip install jupyter-collaboration - - name: Build the extension - if: steps.filter.outputs.extension == 'true' - run: | - # Same commands as in TL;DR to ensure it works - touch yarn.lock - pip install -e . -v - jupyter labextension develop . --overwrite - shell: bash - - name: Lint the files - if: steps.filter.outputs.extension == 'true' - run: jlpm run lint:check - - name: Check extension installation - if: steps.filter.outputs.extension == 'true' - run: | - jupyter labextension list 2>&1 | tee labextension.list - cat labextension.list | grep -ie "@jupyterlab-examples/*.*OK" - # clap-button-message example will have one plugin failing as it is requiring a - # Jupyter Notebook specific token. This is expected. Hence we need to skip this - # test for that example - - if: steps.filter.outputs.extension == 'true' && matrix.example != 'clap-button-message' - run: | - python -m jupyterlab.browser_check - - - name: Install galata - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - working-directory: ${{ matrix.example }}/ui-tests - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - run: jlpm install - - name: Set up browser cache - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - uses: actions/cache@v4 + - name: Build extension + uses: ./.github/actions/build-extension with: - path: | - ${{ github.workspace }}/pw-browsers - key: ${{ runner.os }}-${{ hashFiles('yarn.lock') }} - - name: Install browser - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - run: jlpm playwright install chromium - working-directory: ${{ matrix.example }}/ui-tests - - name: Install kernel-output dependencies - if: steps.filter.outputs.extension == 'true' && matrix.example == 'kernel-output' - run: pip install numpy pandas - - name: Integration tests - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - working-directory: ${{ matrix.example }}/ui-tests - run: jlpm playwright test - - name: Upload UI Test artifacts - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') && always() - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.example }}-ui-test-output - path: | - ${{ matrix.example }}/ui-tests/test-results - - name: Uninstall extension - if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) - run: | - export NAME=`python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['name'])"` - pip uninstall -y ${NAME} + example: ${{ matrix.example }} + run-ui-tests: 'true' - build_serverextension: + build_extensions_cross_platform: + needs: build_extensions_linux runs-on: ${{ matrix.os }} + strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + example: + - clap-button-message + - cell-toolbar + - codemirror-extension + - command-palette + - commands + - completer + - contentheader + - context-menu + - custom-log-console + - datagrid + - documents + - hello-world + - launcher + - kernel-messaging + - kernel-output + - log-messages + - main-menu + - metadata-form + - mimerenderer + - notifications + - react-widget + - server-extension + - settings + - shout-button-message + - signals + - state + - toolbar-button + - toparea-text-widget + - widgets + os: [macos-latest, windows-latest] + steps: - name: Checkout uses: actions/checkout@v4 - - name: Path filter - id: filter - uses: dorny/paths-filter@v2 - with: - filters: | - extension: - - 'server-extension/**' - - name: Cache lerna - if: steps.filter.outputs.extension == 'true' - uses: actions/cache@v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-lerna-${{ hashFiles('server-extension/package.json') }} - restore-keys: ${{ runner.os }}-lerna- - - name: Install node - if: steps.filter.outputs.extension == 'true' - uses: actions/setup-node@v4 - with: - node-version: '18.x' - - name: Check config files - if: steps.filter.outputs.extension == 'true' - run: | - diff hello-world/setup.py server-extension/setup.py - diff hello-world/tsconfig.json server-extension/tsconfig.json - diff hello-world/.yarnrc.yml server-extension/.yarnrc.yml - diff hello-world/ui-tests/jupyter_server_test_config.py server-extension/ui-tests/jupyter_server_test_config.py - diff hello-world/ui-tests/playwright.config.js server-extension/ui-tests/playwright.config.js - shell: bash - - name: Install Python - if: steps.filter.outputs.extension == 'true' - uses: actions/setup-python@v5 - with: - python-version: '3.11' - architecture: 'x64' - - name: Get pip cache dir - if: steps.filter.outputs.extension == 'true' - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - shell: bash - - name: Cache pip - if: steps.filter.outputs.extension == 'true' - uses: actions/cache@v4 + - name: Build extension + uses: ./.github/actions/build-extension with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('environment.yml') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Install the Python dependencies - if: steps.filter.outputs.extension == 'true' - run: | - python -m pip install --upgrade pip jupyterlab~=4.0.0 build - - name: Install the NPM dependencies - if: steps.filter.outputs.extension == 'true' - run: | - cd server-extension - jlpm - - name: Lint the files - if: steps.filter.outputs.extension == 'true' - run: | - cd server-extension - jlpm run lint:check - - name: Build extension as user - if: steps.filter.outputs.extension == 'true' - # Force the usage of the source distribution (good practice) - run: | - cd server-extension - python -m build --sdist - pip install ./dist/jupyterlab_examples_server* --pre --find-links=dist --no-cache-dir - python -m jupyterlab.browser_check - - name: Check extension as dev - if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) - run: | - jupyter server extension list 2>&1 | tee serverextension.list - cat serverextension.list | grep -ie "jupyterlab_examples_server.*OK" - jupyter labextension list 2>&1 | tee labextension.list - cat labextension.list | grep -ie "@jupyterlab-examples/server-extension.*OK" - - name: Clean extension installation - if: steps.filter.outputs.extension == 'true' - run: | - pip uninstall -y jupyterlab_examples_server - jupyter lab clean - jupyter server extension list - jupyter labextension list - - name: Build extension as dev - if: steps.filter.outputs.extension == 'true' - run: | - cd server-extension - pip install . - - name: Check extension as dev - if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) - run: | - jupyter server extension list 2>&1 | tee serverextension.list - cat serverextension.list | grep -ie "jupyterlab_examples_server.*OK" - jupyter labextension list 2>&1 | tee labextension.list - cat labextension.list | grep -ie "@jupyterlab-examples/server-extension.*OK" - python -m jupyterlab.browser_check + example: ${{ matrix.example }} + run-ui-tests: 'false' - - name: Install galata - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - working-directory: server-extension/ui-tests - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - run: jlpm install - - name: Set up browser cache - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - uses: actions/cache@v4 - with: - path: | - ${{ github.workspace }}/pw-browsers - key: ${{ runner.os }}-${{ hashFiles('yarn.lock') }} - - name: Install browser - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - run: jlpm playwright install chromium - working-directory: server-extension/ui-tests - - name: Integration tests - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') - working-directory: server-extension/ui-tests - run: jlpm playwright test - - name: Upload UI Test artifacts - if: steps.filter.outputs.extension == 'true' && startsWith(runner.os, 'Linux') && always() - uses: actions/upload-artifact@v4 + build_serverextension_linux: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build server extension + uses: ./.github/actions/build-serverextension with: - name: ui-test-output - path: | - server-extension/ui-tests/test-results - - name: Uninstall extension - if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) - run: | - export NAME=`python -c "import tomllib; print(tomllib.load(open('server-extension/pyproject.toml', 'rb'))['project']['name'])"` - pip uninstall -y ${NAME} + run-ui-tests: 'true' - build_all: + build_serverextension_cross_platform: + needs: build_serverextension_linux runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [macos-latest, windows-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build server extension + uses: ./.github/actions/build-serverextension + with: + run-ui-tests: 'false' + + build_all: + runs-on: ubuntu-latest + strategy: + fail-fast: false steps: - name: Checkout uses: actions/checkout@v4