Compare commits

...

22 Commits

Author SHA1 Message Date
Min RK
95649a3ece Bump to 2.0.0 2021-12-01 14:58:11 +01:00
Erik Sundell
08288f5b0f Merge pull request #3696 from minrk/changelog-2.0
Changelog for 2.0
2021-12-01 14:56:30 +01:00
Min RK
01b1ce3995 Link to upgrading doc from the changelog 2021-12-01 14:36:07 +01:00
Min RK
cbe93810be remove redundant admin/upgrading ref target
confuses myst to have a ref: and doc: target with the same name
2021-12-01 14:36:06 +01:00
Min RK
75309d9dc4 Changelog for 2.0
ready to go!
2021-12-01 14:36:06 +01:00
Erik Sundell
249b4af59f Merge pull request #3695 from minrk/service-auth-doc
Service auth doc
2021-11-30 16:09:12 +01:00
Min RK
db3b2d8961 refine service auth docs
favor HubOAuth, as that should really be the default for most services

- Remove some outdated 'new in' text
- Remove docs for some deprecated features (hub_users, hub_groups)
- more detail on what's required
2021-11-30 10:48:53 +01:00
Min RK
7d44a0ffc8 add tornado to intersphinx 2021-11-30 10:45:38 +01:00
Min RK
202b2590e9 doc: remove redundant TOC from services doc
The same TOC is automatically generated on the sidebar, no need for a manual copy
2021-11-30 09:18:57 +01:00
Erik Sundell
c98ef547a8 Merge pull request #3693 from jupyterhub/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-11-29 22:16:35 +01:00
pre-commit-ci[bot]
8a866a9102 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-prettier: v2.4.1 → v2.5.0](https://github.com/pre-commit/mirrors-prettier/compare/v2.4.1...v2.5.0)
2021-11-29 20:06:06 +00:00
Min RK
b186bdbce3 Bump to 2.0.0rc5 2021-11-26 09:07:15 +01:00
Min RK
36fe6c6f66 Merge pull request #3692 from minrk/clrc5
changelog for 2.0.0rc5
2021-11-26 09:06:21 +01:00
Min RK
8bf559db52 changelog for 2.0.0rc5 2021-11-26 09:05:21 +01:00
Simon Li
750085f627 Merge pull request #3690 from minrk/gha-singleuser
build jupyterhub/singleuser along with other images
2021-11-25 20:17:12 +00:00
Min RK
2dc2c99b4a Merge pull request #3640 from minrk/doc-api-only
add api-only doc
2021-11-25 20:26:25 +01:00
pre-commit-ci[bot]
e703555888 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-11-25 19:16:41 +00:00
Min RK
7e102f0511 Apply suggestions from code review
Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
2021-11-25 20:16:10 +01:00
Min RK
facde96425 build jupyterhub/singleuser along with other images
got lost in the migration to GHA docker builds
2021-11-24 21:15:59 +01:00
Erik Sundell
608c746a59 Merge pull request #3689 from jupyterhub/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-11-22 22:26:25 +01:00
pre-commit-ci[bot]
a8c834410f [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.29.0 → v2.29.1](https://github.com/asottile/pyupgrade/compare/v2.29.0...v2.29.1)
- [github.com/psf/black: 21.10b0 → 21.11b1](https://github.com/psf/black/compare/21.10b0...21.11b1)
2021-11-22 20:51:45 +00:00
Min RK
88f31c29bb add api-only doc
Describe how to use JupyterHub as a "behind the scenes" implementation detail,
rather than a user-facing application.
2021-11-04 17:16:59 +01:00
21 changed files with 273 additions and 104 deletions

View File

@@ -190,3 +190,23 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ join(fromJson(steps.demotags.outputs.tags)) }} tags: ${{ join(fromJson(steps.demotags.outputs.tags)) }}
# jupyterhub/singleuser
- name: Get list of jupyterhub/singleuser tags
id: singleusertags
uses: jupyterhub/action-major-minor-tag-calculator@v2
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
prefix: "${{ env.REGISTRY }}jupyterhub/singleuser:"
defaultTag: "${{ env.REGISTRY }}jupyterhub/singleuser:noref"
branchRegex: ^\w[\w-.]*$
- name: Build and push jupyterhub/singleuser
uses: docker/build-push-action@e1b7f96249f2e4c8e4ac1519b9608c0d48944a1f # associated tag: v2.4.0
with:
build-args: |
JUPYTERHUB_VERSION=${{ github.ref_type == 'tag' && github.ref_name || format('git:{0}', github.sha) }}
context: singleuser
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ join(fromJson(steps.singleusertags.outputs.tags)) }}

View File

@@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.29.0 rev: v2.29.1
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: args:
@@ -10,11 +10,11 @@ repos:
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 21.10b0 rev: 21.11b1
hooks: hooks:
- id: black - id: black
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.4.1 rev: v2.5.0
hooks: hooks:
- id: prettier - id: prettier
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8

View File

@@ -6,7 +6,7 @@ info:
description: The REST API for JupyterHub description: The REST API for JupyterHub
license: license:
name: BSD-3-Clause name: BSD-3-Clause
version: 2.0.0rc4 version: 2.0.0
servers: servers:
- url: /hub/api - url: /hub/api
security: security:

View File

@@ -1,5 +1,3 @@
.. _admin/upgrading:
==================== ====================
Upgrading JupyterHub Upgrading JupyterHub
==================== ====================

File diff suppressed because one or more lines are too long

View File

@@ -205,7 +205,10 @@ epub_exclude_files = ['search.html']
# -- Intersphinx ---------------------------------------------------------- # -- Intersphinx ----------------------------------------------------------
intersphinx_mapping = {'https://docs.python.org/3/': None} intersphinx_mapping = {
'python': ('https://docs.python.org/3/', None),
'tornado': ('https://www.tornadoweb.org/en/stable/', None),
}
# -- Read The Docs -------------------------------------------------------- # -- Read The Docs --------------------------------------------------------

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,128 @@
(api-only)=
# Deploying JupyterHub in "API only mode"
As a service for deploying and managing Jupyter servers for users, JupyterHub
exposes this functionality _primarily_ via a [REST API](rest).
For convenience, JupyterHub also ships with a _basic_ web UI built using that REST API.
The basic web UI enables users to click a button to quickly start and stop their servers,
and it lets admins perform some basic user and server management tasks.
The REST API has always provided additional functionality beyond what is available in the basic web UI.
Similarly, we avoid implementing UI functionality that is also not available via the API.
With JupyterHub 2.0, the basic web UI will **always** be composed using the REST API.
In other words, no UI pages should rely on information not available via the REST API.
Previously, some admin UI functionality could only be achieved via admin pages,
such as paginated requests.
## Limited UI customization via templates
The JupyterHub UI is customizable via extensible HTML [templates](templates),
but this has some limited scope to what can be customized.
Adding some content and messages to existing pages is well supported,
but changing the page flow and what pages are available are beyond the scope of what is customizable.
## Rich UI customization with REST API based apps
Increasingly, JupyterHub is used purely as an API for managing Jupyter servers
for other Jupyter-based applications that might want to present a different user experience.
If you want a fully customized user experience,
you can now disable the Hub UI and use your own pages together with the JupyterHub REST API
to build your own web application to serve your users,
relying on the Hub only as an API for managing users and servers.
One example of such an application is [BinderHub][], which powers https://mybinder.org,
and motivates many of these changes.
BinderHub is distinct from a traditional JupyterHub deployment
because it uses temporary users created for each launch.
Instead of presenting a login page,
users are presented with a form to specify what environment they would like to launch:
![Binder launch form](../images/binderhub-form.png)
When a launch is requested:
1. an image is built, if necessary
2. a temporary user is created,
3. a server is launched for that user, and
4. when running, users are redirected to an already running server with an auth token in the URL
5. after the session is over, the user is deleted
This means that a lot of JupyterHub's UI flow doesn't make sense:
- there is no way for users to login
- the human user doesn't map onto a JupyterHub `User` in a meaningful way
- when a server isn't running, there isn't a 'restart your server' action available because the user has been deleted
- users do not have any access to any Hub functionality, so presenting pages for those features would be confusing
BinderHub is one of the motivating use cases for JupyterHub supporting being used _only_ via its API.
We'll use BinderHub here as an example of various configuration options.
[binderhub]: https://binderhub.readthedocs.io
## Disabling Hub UI
`c.JupyterHub.hub_routespec` is a configuration option to specify which URL prefix should be routed to the Hub.
The default is `/` which means that the Hub will receive all requests not already specified to be routed somewhere else.
There are three values that are most logical for `hub_routespec`:
- `/` - this is the default, and used in most deployments.
It is also the only option prior to JupyterHub 1.4.
- `/hub/` - this serves only Hub pages, both UI and API
- `/hub/api` - this serves _only the Hub API_, so all Hub UI is disabled,
aside from the OAuth confirmation page, if used.
If you choose a hub routespec other than `/`,
the main JupyterHub feature you will lose is the automatic handling of requests for `/user/:username`
when the requested server is not running.
JupyterHub's handling of this request shows this page,
telling you that the server is not running,
with a button to launch it again:
![screenshot of hub page for server not running](../images/server-not-running.png)
If you set `hub_routespec` to something other than `/`,
it is likely that you also want to register another destination for `/` to handle requests to not-running servers.
If you don't, you will see a default 404 page from the proxy:
![screenshot of CHP default 404](../images/chp-404.png)
For mybinder.org, the default "start my server" page doesn't make sense,
because when a server is gone, there is no restart action.
Instead, we provide hints about how to get back to a link to start a _new_ server:
![screenshot of mybinder.org 404](../images/binder-404.png)
To achieve this, mybinder.org registers a route for `/` that goes to a custom endpoint
that runs nginx and only serves this static HTML error page.
This is set with
```python
c.Proxy.extra_routes = {
"/": "http://custom-404-entpoint/",
}
```
You may want to use an alternate behavior, such as redirecting to a landing page,
or taking some other action based on the requested page.
If you use `c.JupyterHub.hub_routespec = "/hub/"`,
then all the Hub pages will be available,
and only this default-page-404 issue will come up.
If you use `c.JupyterHub.hub_routespec = "/hub/api/"`,
then only the Hub _API_ will be available,
and all UI will be up to you.
mybinder.org takes this last option,
because none of the Hub UI pages really make sense.
Binder users don't have any reason to know or care that JupyterHub happens
to be an implementation detail of how their environment is managed.
Seeing Hub error pages and messages in that situation is more likely to be confusing than helpful.
:::{versionadded} 1.4
`c.JupyterHub.hub_routespec` and `c.Proxy.extra_routes` are new in JupyterHub 1.4.
:::

View File

@@ -21,6 +21,7 @@ what happens under-the-hood when you deploy and configure your JupyterHub.
monitoring monitoring
database database
templates templates
api-only
../events/index ../events/index
config-user-env config-user-env
config-examples config-examples

View File

@@ -1,3 +1,5 @@
(rest-api)=
# Using JupyterHub's REST API # Using JupyterHub's REST API
This section will give you information on: This section will give you information on:

View File

@@ -1,17 +1,5 @@
# Services # Services
With version 0.7, JupyterHub adds support for **Services**.
This section provides the following information about Services:
- [Definition of a Service](#definition-of-a-service)
- [Properties of a Service](#properties-of-a-service)
- [Hub-Managed Services](#hub-managed-services)
- [Launching a Hub-Managed Service](#launching-a-hub-managed-service)
- [Externally-Managed Services](#externally-managed-services)
- [Writing your own Services](#writing-your-own-services)
- [Hub Authentication and Services](#hub-authentication-and-services)
## Definition of a Service ## Definition of a Service
When working with JupyterHub, a **Service** is defined as a process that interacts When working with JupyterHub, a **Service** is defined as a process that interacts
@@ -115,6 +103,8 @@ parameters, which describe the environment needed to start the Service process:
The Hub will pass the following environment variables to launch the Service: The Hub will pass the following environment variables to launch the Service:
(service-env)=
```bash ```bash
JUPYTERHUB_SERVICE_NAME: The name of the service JUPYTERHUB_SERVICE_NAME: The name of the service
JUPYTERHUB_API_TOKEN: API token assigned to the service JUPYTERHUB_API_TOKEN: API token assigned to the service
@@ -196,18 +186,38 @@ extra slash you might get unexpected behavior. For example if your service has a
## Hub Authentication and Services ## Hub Authentication and Services
JupyterHub 0.7 introduces some utilities for using the Hub's authentication JupyterHub provides some utilities for using the Hub's authentication
mechanism to govern access to your service. When a user logs into JupyterHub, mechanism to govern access to your service.
the Hub sets a **cookie (`jupyterhub-services`)**. The service can use this
cookie to authenticate requests.
JupyterHub ships with a reference implementation of Hub authentication that Requests to all JupyterHub services are made with OAuth tokens.
These can either be requests with a token in the `Authorization` header,
or url parameter `?token=...`,
or browser requests which must complete the OAuth authorization code flow,
which results in a token that should be persisted for future requests
(persistence is up to the service,
but an encrypted cookie confined to the service path is appropriate,
and provided by default).
:::{versionchanged} 2.0
The shared `jupyterhub-services` cookie is removed.
OAuth must be used to authenticate browser requests with services.
:::
JupyterHub includes a reference implementation of Hub authentication that
can be used by services. You may go beyond this reference implementation and can be used by services. You may go beyond this reference implementation and
create custom hub-authenticating clients and services. We describe the process create custom hub-authenticating clients and services. We describe the process
below. below.
The reference, or base, implementation is the [`HubAuth`][hubauth] class, The reference, or base, implementation is the [`HubAuth`][hubauth] class,
which implements the requests to the Hub. which implements the API requests to the Hub that resolve a token to a User model.
There are two levels of authentication with the Hub:
- [`HubAuth`][hubauth] - the most basic authentication,
for services that should only accept API requests authorized with a token.
- [`HubOAuth`][huboauth] - For services that should use oauth to authenticate with the Hub.
This should be used for any service that serves pages that should be visited with a browser.
To use HubAuth, you must set the `.api_token`, either programmatically when constructing the class, To use HubAuth, you must set the `.api_token`, either programmatically when constructing the class,
or via the `JUPYTERHUB_API_TOKEN` environment variable. or via the `JUPYTERHUB_API_TOKEN` environment variable.
@@ -250,18 +260,17 @@ for more details.
### Authenticating tornado services with JupyterHub ### Authenticating tornado services with JupyterHub
Since most Jupyter services are written with tornado, Since most Jupyter services are written with tornado,
we include a mixin class, [`HubAuthenticated`][hubauthenticated], we include a mixin class, [`HubOAuthenticated`][huboauthenticated],
for quickly authenticating your own tornado services with JupyterHub. for quickly authenticating your own tornado services with JupyterHub.
Tornado's `@web.authenticated` method calls a Handler's `.get_current_user` Tornado's {py:func}`~.tornado.web.authenticated` decorator calls a Handler's {py:meth}`~.tornado.web.RequestHandler.get_current_user`
method to identify the user. Mixing in `HubAuthenticated` defines method to identify the user. Mixing in {class}`.HubAuthenticated` defines
`get_current_user` to use HubAuth. If you want to configure the HubAuth {meth}`~.HubAuthenticated.get_current_user` to use HubAuth. If you want to configure the HubAuth
instance beyond the default, you'll want to define an `initialize` method, instance beyond the default, you'll want to define an {py:meth}`~.tornado.web.RequestHandler.initialize` method,
such as: such as:
```python ```python
class MyHandler(HubAuthenticated, web.RequestHandler): class MyHandler(HubOAuthenticated, web.RequestHandler):
hub_users = {'inara', 'mal'}
def initialize(self, hub_auth): def initialize(self, hub_auth):
self.hub_auth = hub_auth self.hub_auth = hub_auth
@@ -271,14 +280,21 @@ class MyHandler(HubAuthenticated, web.RequestHandler):
... ...
``` ```
The HubAuth will automatically load the desired configuration from the Service The HubAuth class will automatically load the desired configuration from the Service
environment variables. [environment variables](service-env).
If you want to limit user access, you can specify allowed users through either the :::{versionchanged} 2.0
`.hub_users` attribute or `.hub_groups`. These are sets that check against the
username and user group list, respectively. If a user matches neither the user Access scopes are used to govern access to services.
list nor the group list, they will not be allowed access. If both are left Prior to 2.0,
undefined, then any user will be allowed. sets of users and groups could be used to grant access
by defining `.hub_groups` or `.hub_users` on the authenticated handler.
These are ignored if the 2.0 `.hub_scopes` is defined.
:::
:::{seealso}
{meth}`.HubAuth.check_scopes`
:::
### Implementing your own Authentication with JupyterHub ### Implementing your own Authentication with JupyterHub
@@ -354,9 +370,11 @@ section on securing the notebook viewer.
[requests]: http://docs.python-requests.org/en/master/ [requests]: http://docs.python-requests.org/en/master/
[services_auth]: ../api/services.auth.html [services_auth]: ../api/services.auth.html
[hubauth]: ../api/services.auth.html#jupyterhub.services.auth.HubAuth
[huboauth]: ../api/services.auth.html#jupyterhub.services.auth.HubOAuth [huboauth]: ../api/services.auth.html#jupyterhub.services.auth.HubOAuth
[hubauth.user_for_token]: ../api/services.auth.html#jupyterhub.services.auth.HubAuth.user_for_token [hubauth.user_for_token]: ../api/services.auth.html#jupyterhub.services.auth.HubAuth.user_for_token
[hubauthenticated]: ../api/services.auth.html#jupyterhub.services.auth.HubAuthenticated [hubauthenticated]: ../api/services.auth.html#jupyterhub.services.auth.HubAuthenticated
[huboauthenticated]: ../api/services.auth.html#jupyterhub.services.auth.HubOAuthenticated
[nbviewer example]: https://github.com/jupyter/nbviewer#securing-the-notebook-viewer [nbviewer example]: https://github.com/jupyter/nbviewer#securing-the-notebook-viewer
[fastapi example]: https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-fastapi [fastapi example]: https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-fastapi
[fastapi]: https://fastapi.tiangolo.com [fastapi]: https://fastapi.tiangolo.com

View File

@@ -2,7 +2,7 @@
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
# version_info updated by running `tbump` # version_info updated by running `tbump`
version_info = (2, 0, 0, "rc4", "") version_info = (2, 0, 0, "", "")
# pep 440 version: no dot before beta/rc, but before .dev # pep 440 version: no dot before beta/rc, but before .dev
# 0.1.0rc1 # 0.1.0rc1

View File

@@ -3,10 +3,24 @@
Tokens are sent to the Hub for verification. Tokens are sent to the Hub for verification.
The Hub replies with a JSON model describing the authenticated user. The Hub replies with a JSON model describing the authenticated user.
``HubAuth`` can be used in any application, even outside tornado. This contains two levels of authentication:
``HubAuthenticated`` is a mixin class for tornado handlers that should - :class:`HubOAuth` - Use OAuth 2 to authenticate browsers with the Hub.
authenticate with the Hub. This should be used for any service that should respond to browser requests
(i.e. most services).
- :class:`HubAuth` - token-only authentication, for a service that only need to handle token-authenticated API requests
The ``Auth`` classes (:class:`HubAuth`, :class:`HubOAuth`)
can be used in any application, even outside tornado.
They contain reference implementations of talking to the Hub API
to resolve a token to a user.
The ``Authenticated`` classes (:class:`HubAuthenticated`, :class:`HubOAuthenticated`)
are mixins for tornado handlers that should authenticate with the Hub.
If you are using OAuth, you will also need to register an oauth callback handler to complete the oauth process.
A tornado implementation is provided in :class:`HubOAuthCallbackHandler`.
""" """
import base64 import base64
@@ -212,6 +226,7 @@ class HubAuth(SingletonConfigurable):
help="""The base API URL of the Hub. help="""The base API URL of the Hub.
Typically `http://hub-ip:hub-port/hub/api` Typically `http://hub-ip:hub-port/hub/api`
Default: $JUPYTERHUB_API_URL
""", """,
).tag(config=True) ).tag(config=True)
@@ -227,7 +242,10 @@ class HubAuth(SingletonConfigurable):
os.getenv('JUPYTERHUB_API_TOKEN', ''), os.getenv('JUPYTERHUB_API_TOKEN', ''),
help="""API key for accessing Hub API. help="""API key for accessing Hub API.
Generate with `jupyterhub token [username]` or add to JupyterHub.services config. Default: $JUPYTERHUB_API_TOKEN
Loaded from services configuration in jupyterhub_config.
Will be auto-generated for hub-managed services.
""", """,
).tag(config=True) ).tag(config=True)
@@ -236,6 +254,7 @@ class HubAuth(SingletonConfigurable):
help="""The URL prefix for the Hub itself. help="""The URL prefix for the Hub itself.
Typically /hub/ Typically /hub/
Default: $JUPYTERHUB_BASE_URL
""", """,
).tag(config=True) ).tag(config=True)
@@ -854,8 +873,6 @@ class HubAuthenticated:
Examples:: Examples::
class MyHandler(HubAuthenticated, web.RequestHandler): class MyHandler(HubAuthenticated, web.RequestHandler):
hub_users = {'inara', 'mal'}
def initialize(self, hub_auth): def initialize(self, hub_auth):
self.hub_auth = hub_auth self.hub_auth = hub_auth
@@ -865,6 +882,7 @@ class HubAuthenticated:
""" """
# deprecated, pre-2.0 allow sets
hub_services = None # set of allowed services hub_services = None # set of allowed services
hub_users = None # set of allowed users hub_users = None # set of allowed users
hub_groups = None # set of allowed groups hub_groups = None # set of allowed groups
@@ -960,6 +978,10 @@ class HubAuthenticated:
raise UserNotAllowed(model) raise UserNotAllowed(model)
# proceed with the pre-2.0 way if hub_scopes is not set # proceed with the pre-2.0 way if hub_scopes is not set
warnings.warn(
"hub_scopes ($JUPYTERHUB not set, proceeding with pre-2.0 authentication",
DeprecationWarning,
)
if self.allow_admin and model.get('admin', False): if self.allow_admin and model.get('admin', False):
app_log.debug("Allowing Hub admin %s", name) app_log.debug("Allowing Hub admin %s", name)

View File

@@ -11,7 +11,7 @@ target_version = [
github_url = "https://github.com/jupyterhub/jupyterhub" github_url = "https://github.com/jupyterhub/jupyterhub"
[tool.tbump.version] [tool.tbump.version]
current = "2.0.0rc4" current = "2.0.0"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before

View File

@@ -6,7 +6,6 @@ FROM $BASE_IMAGE
MAINTAINER Project Jupyter <jupyter@googlegroups.com> MAINTAINER Project Jupyter <jupyter@googlegroups.com>
ADD install_jupyterhub /tmp/install_jupyterhub ADD install_jupyterhub /tmp/install_jupyterhub
ARG JUPYTERHUB_VERSION=main ARG JUPYTERHUB_VERSION=git:HEAD
# install pinned jupyterhub and ensure jupyterlab is installed # install pinned jupyterhub
RUN python3 /tmp/install_jupyterhub && \ RUN python3 /tmp/install_jupyterhub
python3 -m pip install jupyterlab

View File

@@ -1,4 +0,0 @@
#!/bin/bash
set -ex
docker build --build-arg JUPYTERHUB_VERSION=$DOCKER_TAG -t $DOCKER_REPO:$DOCKER_TAG .

View File

@@ -1,21 +0,0 @@
#!/bin/bash
set -ex
function get_hub_version() {
rm -f hub_version
V=$1
docker run --rm -v $PWD:/version -u $(id -u) -i $DOCKER_REPO:$DOCKER_TAG sh -c 'jupyterhub --version > /version/hub_version'
hub_xyz=$(cat hub_version)
split=( ${hub_xyz//./ } )
hub_xy="${split[0]}.${split[1]}"
# add .dev on hub_xy so it's 1.0.dev
if [[ ! -z "${split[3]:-}" ]]; then
hub_xy="${hub_xy}.${split[3]}"
fi
}
# tag e.g. 0.9 with main
get_hub_version
docker tag $DOCKER_REPO:$DOCKER_TAG $DOCKER_REPO:$hub_xy
docker push $DOCKER_REPO:$hub_xy
docker tag $DOCKER_REPO:$DOCKER_TAG $DOCKER_REPO:$hub_xyz
docker push $DOCKER_REPO:$hub_xyz

View File

@@ -3,19 +3,22 @@ import os
from subprocess import check_call from subprocess import check_call
import sys import sys
V = os.environ['JUPYTERHUB_VERSION'] version = os.environ['JUPYTERHUB_VERSION']
pip_install = [ pip_install = [
sys.executable, '-m', 'pip', 'install', '--no-cache', '--upgrade', sys.executable,
'--upgrade-strategy', 'only-if-needed', '-m',
'pip',
'install',
'--no-cache',
'--upgrade',
'--upgrade-strategy',
'only-if-needed',
] ]
if V in {'main', 'HEAD'}: if version.startswith("git:"):
req = 'https://github.com/jupyterhub/jupyterhub/archive/HEAD.tar.gz' ref = version.partition(":")[-1]
req = f"https://github.com/jupyterhub/jupyterhub/archive/{ref}.tar.gz"
else: else:
version_info = [ int(part) for part in V.split('.') ] req = f"jupyterhub=={version}"
version_info[-1] += 1
upper_bound = '.'.join(map(str, version_info))
vs = '>=%s,<%s' % (V, upper_bound)
req = 'jupyterhub%s' % vs
check_call(pip_install + [req]) check_call(pip_install + [req])