Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2340926
core library version updates, remove nested-dict, pin numpy and matpl…
Zeitsperre Mar 17, 2026
565bc1a
use micromamba image, move VCS tools to conda env
Zeitsperre Mar 17, 2026
4a1bb81
remove jupyter lab build command, update comments, CMD now uses micro…
Zeitsperre Mar 17, 2026
5be5022
do not install nodejs, DEBIAN_FRONTEND=noninteractive, pin intake to 2.0
Zeitsperre Mar 17, 2026
20b4ba4
use micromamba
Zeitsperre Mar 17, 2026
eb52112
release: update to use image pavics/workflow-tests:py311-260317
Zeitsperre Mar 17, 2026
9ea491e
remove plotly-dash, add exactextract and mamba, raise birdy pin
Zeitsperre Mar 18, 2026
7377969
typo fix, add requests
Zeitsperre Mar 18, 2026
3fbc508
you can do this
Zeitsperre Mar 18, 2026
985b001
release: update to use image pavics/workflow-tests:py311-260318
Zeitsperre Mar 18, 2026
2d8033a
use modern $ replacement for better shell support
Zeitsperre Mar 19, 2026
29196bc
update jupyterlab version, use python3.12, use conda-provided browse…
Zeitsperre Mar 19, 2026
b16946c
let ravenpy manage raven-hydro
Zeitsperre Mar 19, 2026
ece26fd
set workdir to /
Zeitsperre Mar 19, 2026
669fd9b
release: update to use image pavics/workflow-tests:py311-260319
Zeitsperre Mar 19, 2026
3d7cc98
rewrite Dockerfile to use mambaorg recommendations
Zeitsperre Mar 20, 2026
2d80777
release: update to use image pavics/workflow-tests:py312-260320
Zeitsperre Mar 20, 2026
d14e1a0
TEST try forcing env name
Zeitsperre Mar 20, 2026
9b2708a
TEST use args
Zeitsperre Mar 20, 2026
f63d414
split jupyter dependencies into base, no longer rely on birdy activat…
Zeitsperre Mar 20, 2026
18b4ebe
WIP - use mamba env vars, update docker-stacks scripts, add healthche…
Zeitsperre Mar 23, 2026
3a49639
drop workdir, copy env files to root dir
Zeitsperre Mar 23, 2026
0eb3c9d
recombine environment files, add openssh and jupyterlab-unfold
Zeitsperre Mar 24, 2026
acb1bb9
save build artifacts in build-src, clean tmp, set conda-forge as defa…
Zeitsperre Mar 24, 2026
5b9ec72
add hyperlinks to projects
Zeitsperre Mar 24, 2026
7068909
release: update to use image pavics/workflow-tests:py312-260324
Zeitsperre Mar 24, 2026
db20d18
re-add shell scripts
Zeitsperre Mar 24, 2026
0bd1dba
re-add PATH overrides
Zeitsperre Mar 24, 2026
84c3d03
release: update to use image pavics/workflow-tests:py312-260324-1
Zeitsperre Mar 24, 2026
4047e0f
undo PATH modification in order to try a different approach to enviro…
Zeitsperre Mar 25, 2026
4d62b0c
do not install conda in birdy environment as it takes priority over t…
Zeitsperre Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docker-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ jobs:
set -e
echo "Running tests in Docker container"
# Run the test script inside the Docker container
conda run -n birdy mamba list
micromamba run -n birdy micromamba list
3 changes: 2 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ pipeline {
// https://jenkins.io/doc/book/pipeline/syntax/
agent {
docker {
image "pavics/workflow-tests:py311-250423-update250730"
image "pavics/workflow-tests:py312-260320"
label 'linux && docker'
args "-e ENV_NAME='birdy'"
}
}

Expand Down
2 changes: 1 addition & 1 deletion binder/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM pavics/workflow-tests:py311-250423-update250730
FROM pavics/workflow-tests:py312-260320

USER root

Expand Down
195 changes: 96 additions & 99 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,89 +1,67 @@
FROM continuumio/miniconda3

# Use mamba for much improved performance over conda.
# The 'channel_priority strict' did help conda but it was not enough.
RUN conda config --prepend channels nodefaults || true && \
conda config --remove channels defaults || true && \
Comment thread
Zeitsperre marked this conversation as resolved.
conda update conda -n base && \
conda install mamba conda-pack -n base -c conda-forge && \
conda clean --all --yes && \
conda config --set channel_priority strict && \
wget -qO- https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -C /usr/local -xvj bin/micromamba

# Default for micromamba is $HOME/micromamba which is wrong.
ENV MAMBA_ROOT_PREFIX="/opt/conda"
Comment thread
Zeitsperre marked this conversation as resolved.

# to checkout other notebooks and to run pip install
# Use micromamba for improved speed and reduced image size
FROM mambaorg/micromamba
# No interactive apt calls
ENV DEBIAN_FRONTEND=noninteractive

# Install all jupyter and docker build dependencies in base
COPY environment-base.yml /tmp/environment-base.yml
RUN micromamba install --yes --name base --file /tmp/environment-base.yml && \
micromamba clean --all --yes

# Rename mambauser to jenkins for our Jenkins e2e notebooks test suite.
ARG NEW_MAMBA_USER=jenkins
ARG NEW_MAMBA_USER_ID=1000
ARG NEW_MAMBA_USER_GID=1000

USER root
# Rename mambauser to jenkins; copied from mambaorg/micromamba documentation
RUN if grep -q '^ID=alpine$' /etc/os-release; then \
# alpine does not have usermod/groupmod
apk add --no-cache --virtual temp-packages shadow; \
fi && \
usermod "--login=${NEW_MAMBA_USER}" "--home=/home/${NEW_MAMBA_USER}" \
--move-home "-u ${NEW_MAMBA_USER_ID}" "${MAMBA_USER}" && \
groupmod "--new-name=${NEW_MAMBA_USER}" \
"-g ${NEW_MAMBA_USER_GID}" "${MAMBA_USER}" && \
if grep -q '^ID=alpine$' /etc/os-release; then \
# remove the packages that were only needed for usermod/groupmod
apk del temp-packages; \
fi && \
# Update the expected value of MAMBA_USER for the
# _entrypoint.sh consistency check.
echo "${NEW_MAMBA_USER}" > "/etc/arg_mamba_user" && \
:
ENV MAMBA_USER=$NEW_MAMBA_USER

# Alias UID and GID for use with docker-stacks
ENV NB_USER=${MAMBA_USER}
ARG NB_GID="${NEW_MAMBA_USER_GID}"

# Copy environment file
COPY --chown=$MAMBA_USER:$MAMBA_USER environment.yml /tmp/environment.yml

# Add additional fonts and x11-utils for matplotlib and firefox support
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
git mercurial gcc unzip patch fonts-humor-sans firefox-esr x11-utils && \
Comment thread
Zeitsperre marked this conversation as resolved.
apt-get install --yes fonts-humor-sans x11-utils && \
apt-get clean

# Create user jenkins for our Jenkins e2e notebooks test suite.
# Change /opt/conda folder permissions for jupyter-conda extension.
RUN groupadd --gid 1000 jenkins && \
useradd --uid 1000 --gid jenkins --shell /bin/bash --create-home jenkins && \
chmod -R a+rwX /opt/conda
Comment thread
Zeitsperre marked this conversation as resolved.

COPY environment.yml /environment.yml
Comment thread
Zeitsperre marked this conversation as resolved.

# create env "birdy"
# Create "birdy" environment
# use umask 0000 so that the files for the new environment are usable by user 'jenkins' for the jupyter-conda-extension
#
# Perform 2 stages install because one single 'conda env create -f
# /environment.yml' was taking forever to complete, same with mamba.
# Had to do this 2 stages install. 2 stages install was also taking forever
# with conda so had to switch to mamba.
#
# One single 'conda env create -f /environment.yml' takes forever because we
# removed all direct dependencies of xclim and ravenpy in /environment.yml for
# dependencies pinning by xclim and ravenpy to take effect. This results in
# conda having a lot more packages to "solve" and it seems the solver
# performance dropped exponentially with the number of packages to solve.
#
# Conda was stuck at this step:
# DEBUG conda.common._logic:_run_sat(607): Invoking SAT with clause count: 2500273
#
RUN umask 0000 && \
mamba create --name birdy --channel conda-forge python=3.11 clisops figanos ravenpy xclim xscen xsdba --yes && \
(conda env export --name birdy 2>&1 | tee /conda-env-export-phase1.yml) && \
(du -sh /opt/conda 2>&1 | tee /conda-envs-size-phase1.txt) && \
mamba env update --name birdy --file /environment.yml && \
mamba clean --all --yes

# alternate way to 'source activate birdy'
ENV PATH="/opt/conda/envs/birdy/bin:$PATH"

# our notebooks are hardcoded to lookup for kernel named 'birdy'
# this python is from the birdy env above
RUN python -m ipykernel install --name birdy && \
(conda env export --name birdy 2>&1 | tee /conda-env-export-final.yml) && \
(diff --unified /conda-env-export-phase1.yml /conda-env-export-final.yml 2>&1 | tee /conda-env-export.diff) && \
(du -sh /opt/conda 2>&1 | tee /conda-envs-size-final.txt) && \
diff /conda-envs-size-phase1.txt /conda-envs-size-final.txt || true

# install using same channel preferences as birdy original env to not downgrade anything accidentally
# this is for debug only, all dependencies should be specified in environment.yml above
# RUN mamba install -c conda-forge -c cdat -c bokeh -c fortiers -n birdy nbdime

# build jupyterlab extensions installed by conda, see `jupyter labextension list`
# Supposedly not needed with jupyterlab v3 anymore but see
# https://github.com/jupyterlab/jupyterlab/issues/11726#issuecomment-998901247
# TODO: remove 'jupyter lab build' step once all extensions move to prebuilt extensions,
# see comment https://github.com/jupyterlab/jupyterlab/issues/11726#issuecomment-998917305
# Currently jupyter-dash is holding back this step, see
# https://github.com/plotly/jupyter-dash/issues/49
Comment thread
Zeitsperre marked this conversation as resolved.
RUN jupyter lab build

RUN jupyter serverextension enable voila --sys-prefix && \
jupyter serverextension enable panel.io.jupyter_server_extension --sys-prefix && \
Comment thread
Zeitsperre marked this conversation as resolved.
jupyter serverextension list
# jupyter labextension install jupyterlab-clipboard

# This should be "master" but commit
# https://github.com/jupyter/docker-stacks/commit/c772e98ac794173d6ed83a08ec249038b27ca3be
# is breaking with us since we do not have user jovyan.
ENV DOCKER_STACKS_COMMIT=709206ac8788475728cc9c992c25fb5f1501bc29
micromamba create --name birdy --file /tmp/environment.yml "python>=3.12,<3.13" --yes && \
micromamba clean --all --yes

# Notebooks are hardcoded to lookup for kernel named 'birdy' created in the previous code block
RUN umask 0000 && \
micromamba run --name birdy python -m ipykernel install --name birdy && \
micromamba env export --name birdy | tee /conda-env-export-final.yml && \
du -sh "${MAMBA_ROOT_PREFIX}" | tee /conda-envs-size-final.txt
Comment thread
Zeitsperre marked this conversation as resolved.
Outdated

# Docker-stacks commit from last time base-notebook was modified (2025/05/24)
ENV DOCKER_STACKS_COMMIT=e73d308ba06fd5bcc75a4ee8417711743860c964

# Activate base environment
ARG MAMBA_DOCKERFILE_ACTIVATE=1

# /notebook_dir for Pavics-landing notebooks to re-create Jupyter env layout:
# /notebook_dir/writable-workspace, /notebook_dir/pavics-homepage.
Expand All @@ -92,48 +70,67 @@ ENV DOCKER_STACKS_COMMIT=709206ac8788475728cc9c992c25fb5f1501bc29
# hardcoded so users can copy the nb to writable-workspace/ dir and still be able
# to run them seemlessly from the Jupyter env (without having to also copy
# those *.geojson files with the notebooks).
RUN wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/base-notebook/start.sh --output-document /usr/local/bin/start.sh && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/base-notebook/start-singleuser.sh --output-document /usr/local/bin/start-singleuser.sh && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/base-notebook/start-notebook.sh --output-document /usr/local/bin/start-notebook.sh && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/base-notebook/fix-permissions --output-document /usr/local/bin/fix-permissions && \
RUN wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/images/base-notebook/start-singleuser.py --output-document /usr/local/bin/start-singleuser.py && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/images/base-notebook/start-notebook.py --output-document /usr/local/bin/start-notebook.py && \
mkdir /etc/jupyter && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/base-notebook/jupyter_notebook_config.py --output-document /etc/jupyter/jupyter_notebook_config.py && \
chmod a+rx /usr/local/bin/start.sh /usr/local/bin/start-singleuser.sh /usr/local/bin/start-notebook.sh /usr/local/bin/fix-permissions && \
chmod a+r /etc/jupyter/jupyter_notebook_config.py && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/images/base-notebook/jupyter_server_config.py --output-document /etc/jupyter/jupyter_server_config.py && \
wget https://raw.githubusercontent.com/jupyter/docker-stacks/$DOCKER_STACKS_COMMIT/images/base-notebook/docker_healthcheck.py --output-document /etc/jupyter/docker_healthcheck.py && \
chmod a+rx /usr/local/bin/start-singleuser.py /usr/local/bin/start-notebook.py /etc/jupyter/docker_healthcheck.py && \
chmod a+r /etc/jupyter/jupyter_server_config.py && \
mkdir -p /notebook_dir/writable-workspace && \
chown jenkins /notebook_dir/writable-workspace && \
chown "${MAMBA_USER}" /notebook_dir/writable-workspace && \
mkdir -p /notebook_dir/pavics-homepage && \
chown jenkins /notebook_dir/pavics-homepage && \
chown "${MAMBA_USER}" /notebook_dir/pavics-homepage && \
chown root:root /notebook_dir && \
chmod a-w /notebook_dir && \
chmod a+rwX -R /opt/conda/envs/birdy/fonts && \
mkdir /opt/conda/pkgs/cache && \
chown jenkins:jenkins -R /opt/conda/pkgs/cache && \
mkdir -p "${MAMBA_ROOT_PREFIX}/envs/birdy/fonts" && \
chmod a+rwX -R "${MAMBA_ROOT_PREFIX}/envs/birdy/fonts" && \
mkdir -p "${MAMBA_ROOT_PREFIX}/pkgs/cache" && \
chown "${MAMBA_USER}":"${MAMBA_USER}" -R "${MAMBA_ROOT_PREFIX}/pkgs/cache" && \
touch "${MAMBA_ROOT_PREFIX}/pkgs/pkgs.lock" && \
chown "${MAMBA_USER}":"${MAMBA_USER}" "${MAMBA_ROOT_PREFIX}/pkgs/pkgs.lock" && \
mkdir -p /usr/local/bin && \
wget https://downloads.globus.org/globus-connect-personal/linux/stable/globusconnectpersonal-latest.tgz -O /usr/local/bin/globusconnectpersonal-latest.tgz && \
tar xzf /usr/local/bin/globusconnectpersonal-latest.tgz -C /usr/local/bin/ && \
ln -vs /usr/local/bin/globusconnectpersonal*/globusconnectpersonal /usr/local/bin/globusconnectpersonal && \
rm -v /usr/local/bin/globusconnectpersonal-latest.tgz

# Set birdy as the default kernel
RUN echo "c.MultiKernelManager.default_kernel_name = 'birdy'" >> /etc/jupyter/jupyter_server_config.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This do not work, remove or comment out to not give false idea.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? I am seeing a difference when it comes to the kernel order.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this line:
image
Default kernel when opening a notebook is ipykernel.

Adding it back in:
image
Default kernel is now birdy.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I though, via our discussions, that this kernel setting is to activate the kernel by default. So this is only to change the display ordering of the kernel?

Can you update the comment to clarify it is for default kernel in the selection list display ordering, not for automated activation?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets the default ipython kernel in Jupyterlab environment, not the same as activating the birdy environment; These are two different things! I think the comment is clear enough.


# For jupyter-panel-proxy launcher.
ENV BOKEH_ALLOW_WS_ORIGIN="*"

# To supply the location of the RavenHydroFramework binary
# FIXME: This location should be provided via the raven-hydro package which would help with the discovery logic in RavenPy
ENV RAVENPY_RAVEN_BINARY_PATH="${MAMBA_ROOT_PREFIX}/envs/birdy/bin/raven"

# For import xesmf since esmf-8.4.0, see: https://github.com/conda-forge/esmf-feedstock/issues/91
ENV ESMFMKFILE="/opt/conda/envs/birdy/lib/esmf.mk"
ENV ESMFMKFILE="${MAMBA_ROOT_PREFIX}/envs/birdy/lib/esmf.mk"

# To avoid error "PROJ: proj_create_from_database: Open of /opt/conda/envs/birdy/share/proj failed"
# To avoid error "PROJ: proj_create_from_database: Open of ${MAMBA_ROOT_PREFIX}/envs/birdy/share/proj failed"
# This simulates a real `conda activate birdy`.
ENV PROJ_DATA="/opt/conda/envs/birdy/share/proj"
ENV PROJ_DATA="${MAMBA_ROOT_PREFIX}/envs/birdy/share/proj"

# To expose conda-installed compilers so that users can make use of them
ENV gcc=x86_64-conda-linux-gnu-gcc
ENV g++=x86_64-conda-linux-gnu-g++
ENV gfortran=x86_64-conda-linux-gnu-gfortran

# problem running start-notebook.sh when being root
# JupyterLab cannot write to hidden folders by design
ENV XDG_CACHE_HOME="/home/${MAMBA_USER}/cache"
Comment thread
Zeitsperre marked this conversation as resolved.

# Healthcheck - Requires NB_USER variable (copied from Docker-stacks)
HEALTHCHECK --interval=3s --timeout=1s --start-period=3s --retries=3 CMD /etc/jupyter/docker_healthcheck.py || exit 1

# Problem running start-notebook.sh when being root
# the jupyter/base-notebook image also do not default to root user so we do the same here
USER jenkins
USER $MAMBA_USER
# Activate birdy environment
ENV ENV_NAME=birdy
# Set entrypoint for jenkins to root directory
WORKDIR /
Comment thread
Zeitsperre marked this conversation as resolved.
Outdated

# follow jupyter/base-notebook image so config in jupyterhub is simpler
# start notebook in conda environment to have working jupyter extensions
CMD ["conda", "run", "-n", "birdy", "/usr/local/bin/start-notebook.sh"]
CMD ["/usr/local/bin/_entrypoint.sh", "run", "-n", "base", "/usr/local/bin/start-notebook.py"]
94 changes: 94 additions & 0 deletions docker/environment-base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# conda env create -f environment-base.yml
name: base
channels:
- conda-forge
dependencies:
# UTILITIES

# conda: a cross-platform, language-agnostic binary package manager
# NOTE: Included as a requirement for gator
# https://github.com/conda/conda
- conda >= 4.7.12
# git: fast, scalable, distributed revision control system
# NOTES: Included as a requirement for jupyterlab-git
- git
# mamba: a reimplementation of the conda package manager in C++
# NOTE: Included as a requirement for gator
# https://github.com/mamba-org/mamba
- mamba >= 2.0
# wget: command-line utility for downloading files from the web
- wget

# JUPYTER LIBRARIES

# Jupyter conda metapackage: Install all the Jupyter components in one go.
# https://anaconda.org/conda-forge/jupyter
- jupyter
# notebook: A web-based notebook environment for interactive computing.
# to be launched by image jupyterhub/jupyterhub
# https://github.com/jupyter/notebook
- notebook
# jupyterlab: An extensible environment for interactive and reproducible computing
# https://github.com/jupyterlab/jupyterlab
- jupyterlab >= 4.0
# JupyterHub: A multi-user server for Jupyter notebooks
# https://github.com/jupyterhub/jupyterhub
- jupyterhub
# ipywidgets: Interactive HTML Widgets
# https://github.com/jupyter-widgets/ipywidgets
# https://ipywidgets.readthedocs.io/en/latest/user_install.html
- ipywidgets
# ipyleaflet: A Jupyter / Leaflet bridge enabling interactive maps in the Jupyter notebook.
# https://github.com/jupyter-widgets/ipyleaflet
- ipyleaflet
# gator: The Mamba Navigator, a Web UI for managing conda environments
# https://github.com/mamba-org/gator
- mamba_gator
# nbdime: Jupyter Notebook Diff and Merge tools
# https://github.com/jupyter/nbdime
- nbdime
# jupyter_bokeh: An extension for rendering Bokeh content in JupyterLab notebooks
# https://github.com/bokeh/jupyter_bokeh
- jupyter_bokeh
# jupytext: extension to produce .py files from notebook .ipynb files
# https://github.com/mwouts/jupytext
- jupytext
# jupyterlab-git: A JupyterLab extension for version control using Git
# https://github.com/jupyterlab/jupyterlab-git
- jupyterlab-git >= 0.44.0
# voila: Voilà turns Jupyter notebooks into standalone web applications
# https://github.com/voila-dashboards/voila
- voila
# jupyterlab-archive: A Jupyter extension to make, download and extract archive files.
# https://github.com/jupyterlab-contrib/jupyter-archive
- jupyter-archive
# jupyter-resource-usage: Jupyter Extension to show resource usage
# https://github.com/jupyter-server/jupyter-resource-usage
# migrated from https://github.com/jtpio/jupyterlab-system-monitor and originally from jupyterlab-topbar
- jupyter-resource-usage
# xeus-python: back-end kernel implementing the Jupyter Debug Protocol
# https://github.com/jupyter-xeus/xeus-python
- xeus-python
# jupyter-server-proxy: run arbitrary external processes alongside your notebook server
# https://github.com/jupyterhub/jupyter-server-proxy
- jupyter-server-proxy
# dask-labextension: A JupyterLab extension for Dask.
# https://github.com/dask/dask-labextension
- dask-labextension
# jupyterlab-geojson: JupyterLab extension for rendering GeoJSON
# https://github.com/jupyterlab/jupyter-renderers/tree/main/packages/geojson-extension
- jupyterlab-geojson
# jupyterlab-topbar: JupyterLab Top Bar Extensions
# https://github.com/jtpio/jupyterlab-topbar
- pip:
- jupyterlab-logout >= 1.1.0
- jupyterlab-theme-toggler >= 1.1.0

# BROWSER SUPPORT

# firefox: The one and only
# https://github.com/mozilla-firefox/firefox
- firefox
# selenium: Python library for automating web browser interaction
# https://github.com/SeleniumHQ/Selenium
- selenium
Loading
Loading