mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-08 02:24:08 +00:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
95649a3ece | ||
![]() |
08288f5b0f | ||
![]() |
01b1ce3995 | ||
![]() |
cbe93810be | ||
![]() |
75309d9dc4 | ||
![]() |
249b4af59f | ||
![]() |
db3b2d8961 | ||
![]() |
7d44a0ffc8 | ||
![]() |
202b2590e9 | ||
![]() |
c98ef547a8 | ||
![]() |
8a866a9102 | ||
![]() |
b186bdbce3 | ||
![]() |
36fe6c6f66 | ||
![]() |
8bf559db52 | ||
![]() |
750085f627 | ||
![]() |
2dc2c99b4a | ||
![]() |
e703555888 | ||
![]() |
7e102f0511 | ||
![]() |
facde96425 | ||
![]() |
608c746a59 | ||
![]() |
a8c834410f | ||
![]() |
88f31c29bb |
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
@@ -190,3 +190,23 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
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)) }}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.29.0
|
||||
rev: v2.29.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
@@ -10,11 +10,11 @@ repos:
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.10b0
|
||||
rev: 21.11b1
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.4.1
|
||||
rev: v2.5.0
|
||||
hooks:
|
||||
- id: prettier
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
|
@@ -6,7 +6,7 @@ info:
|
||||
description: The REST API for JupyterHub
|
||||
license:
|
||||
name: BSD-3-Clause
|
||||
version: 2.0.0rc4
|
||||
version: 2.0.0
|
||||
servers:
|
||||
- url: /hub/api
|
||||
security:
|
||||
|
@@ -1,5 +1,3 @@
|
||||
.. _admin/upgrading:
|
||||
|
||||
====================
|
||||
Upgrading JupyterHub
|
||||
====================
|
||||
|
File diff suppressed because one or more lines are too long
@@ -205,7 +205,10 @@ epub_exclude_files = ['search.html']
|
||||
|
||||
# -- 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 --------------------------------------------------------
|
||||
|
||||
|
BIN
docs/source/images/binder-404.png
Normal file
BIN
docs/source/images/binder-404.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
BIN
docs/source/images/binderhub-form.png
Normal file
BIN
docs/source/images/binderhub-form.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 138 KiB |
BIN
docs/source/images/chp-404.png
Normal file
BIN
docs/source/images/chp-404.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
docs/source/images/server-not-running.png
Normal file
BIN
docs/source/images/server-not-running.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
128
docs/source/reference/api-only.md
Normal file
128
docs/source/reference/api-only.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
:::
|
@@ -21,6 +21,7 @@ what happens under-the-hood when you deploy and configure your JupyterHub.
|
||||
monitoring
|
||||
database
|
||||
templates
|
||||
api-only
|
||||
../events/index
|
||||
config-user-env
|
||||
config-examples
|
||||
|
@@ -1,3 +1,5 @@
|
||||
(rest-api)=
|
||||
|
||||
# Using JupyterHub's REST API
|
||||
|
||||
This section will give you information on:
|
||||
|
@@ -1,17 +1,5 @@
|
||||
# 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
|
||||
|
||||
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:
|
||||
|
||||
(service-env)=
|
||||
|
||||
```bash
|
||||
JUPYTERHUB_SERVICE_NAME: The name of 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
|
||||
|
||||
JupyterHub 0.7 introduces some utilities for using the Hub's authentication
|
||||
mechanism to govern access to your service. When a user logs into JupyterHub,
|
||||
the Hub sets a **cookie (`jupyterhub-services`)**. The service can use this
|
||||
cookie to authenticate requests.
|
||||
JupyterHub provides some utilities for using the Hub's authentication
|
||||
mechanism to govern access to your service.
|
||||
|
||||
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
|
||||
create custom hub-authenticating clients and services. We describe the process
|
||||
below.
|
||||
|
||||
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,
|
||||
or via the `JUPYTERHUB_API_TOKEN` environment variable.
|
||||
@@ -250,18 +260,17 @@ for more details.
|
||||
### Authenticating tornado services with JupyterHub
|
||||
|
||||
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.
|
||||
|
||||
Tornado's `@web.authenticated` method calls a Handler's `.get_current_user`
|
||||
method to identify the user. Mixing in `HubAuthenticated` defines
|
||||
`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,
|
||||
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 {class}`.HubAuthenticated` defines
|
||||
{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 {py:meth}`~.tornado.web.RequestHandler.initialize` method,
|
||||
such as:
|
||||
|
||||
```python
|
||||
class MyHandler(HubAuthenticated, web.RequestHandler):
|
||||
hub_users = {'inara', 'mal'}
|
||||
class MyHandler(HubOAuthenticated, web.RequestHandler):
|
||||
|
||||
def initialize(self, 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
|
||||
environment variables.
|
||||
The HubAuth class will automatically load the desired configuration from the Service
|
||||
[environment variables](service-env).
|
||||
|
||||
If you want to limit user access, you can specify allowed users through either the
|
||||
`.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
|
||||
list nor the group list, they will not be allowed access. If both are left
|
||||
undefined, then any user will be allowed.
|
||||
:::{versionchanged} 2.0
|
||||
|
||||
Access scopes are used to govern access to services.
|
||||
Prior to 2.0,
|
||||
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
|
||||
|
||||
@@ -354,9 +370,11 @@ section on securing the notebook viewer.
|
||||
|
||||
[requests]: http://docs.python-requests.org/en/master/
|
||||
[services_auth]: ../api/services.auth.html
|
||||
[hubauth]: ../api/services.auth.html#jupyterhub.services.auth.HubAuth
|
||||
[huboauth]: ../api/services.auth.html#jupyterhub.services.auth.HubOAuth
|
||||
[hubauth.user_for_token]: ../api/services.auth.html#jupyterhub.services.auth.HubAuth.user_for_token
|
||||
[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
|
||||
[fastapi example]: https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-fastapi
|
||||
[fastapi]: https://fastapi.tiangolo.com
|
||||
|
@@ -2,7 +2,7 @@
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
# 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
|
||||
# 0.1.0rc1
|
||||
|
@@ -3,10 +3,24 @@
|
||||
Tokens are sent to the Hub for verification.
|
||||
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
|
||||
authenticate with the Hub.
|
||||
- :class:`HubOAuth` - Use OAuth 2 to authenticate browsers 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
|
||||
@@ -212,6 +226,7 @@ class HubAuth(SingletonConfigurable):
|
||||
help="""The base API URL of the Hub.
|
||||
|
||||
Typically `http://hub-ip:hub-port/hub/api`
|
||||
Default: $JUPYTERHUB_API_URL
|
||||
""",
|
||||
).tag(config=True)
|
||||
|
||||
@@ -227,7 +242,10 @@ class HubAuth(SingletonConfigurable):
|
||||
os.getenv('JUPYTERHUB_API_TOKEN', ''),
|
||||
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)
|
||||
|
||||
@@ -236,6 +254,7 @@ class HubAuth(SingletonConfigurable):
|
||||
help="""The URL prefix for the Hub itself.
|
||||
|
||||
Typically /hub/
|
||||
Default: $JUPYTERHUB_BASE_URL
|
||||
""",
|
||||
).tag(config=True)
|
||||
|
||||
@@ -854,8 +873,6 @@ class HubAuthenticated:
|
||||
Examples::
|
||||
|
||||
class MyHandler(HubAuthenticated, web.RequestHandler):
|
||||
hub_users = {'inara', 'mal'}
|
||||
|
||||
def initialize(self, 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_users = None # set of allowed users
|
||||
hub_groups = None # set of allowed groups
|
||||
@@ -960,6 +978,10 @@ class HubAuthenticated:
|
||||
raise UserNotAllowed(model)
|
||||
|
||||
# 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):
|
||||
app_log.debug("Allowing Hub admin %s", name)
|
||||
|
@@ -11,7 +11,7 @@ target_version = [
|
||||
github_url = "https://github.com/jupyterhub/jupyterhub"
|
||||
|
||||
[tool.tbump.version]
|
||||
current = "2.0.0rc4"
|
||||
current = "2.0.0"
|
||||
|
||||
# Example of a semver regexp.
|
||||
# Make sure this matches current_version before
|
||||
|
@@ -6,7 +6,6 @@ FROM $BASE_IMAGE
|
||||
MAINTAINER Project Jupyter <jupyter@googlegroups.com>
|
||||
|
||||
ADD install_jupyterhub /tmp/install_jupyterhub
|
||||
ARG JUPYTERHUB_VERSION=main
|
||||
# install pinned jupyterhub and ensure jupyterlab is installed
|
||||
RUN python3 /tmp/install_jupyterhub && \
|
||||
python3 -m pip install jupyterlab
|
||||
ARG JUPYTERHUB_VERSION=git:HEAD
|
||||
# install pinned jupyterhub
|
||||
RUN python3 /tmp/install_jupyterhub
|
||||
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
docker build --build-arg JUPYTERHUB_VERSION=$DOCKER_TAG -t $DOCKER_REPO:$DOCKER_TAG .
|
@@ -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
|
@@ -3,19 +3,22 @@ import os
|
||||
from subprocess import check_call
|
||||
import sys
|
||||
|
||||
V = os.environ['JUPYTERHUB_VERSION']
|
||||
version = os.environ['JUPYTERHUB_VERSION']
|
||||
|
||||
pip_install = [
|
||||
sys.executable, '-m', 'pip', 'install', '--no-cache', '--upgrade',
|
||||
'--upgrade-strategy', 'only-if-needed',
|
||||
sys.executable,
|
||||
'-m',
|
||||
'pip',
|
||||
'install',
|
||||
'--no-cache',
|
||||
'--upgrade',
|
||||
'--upgrade-strategy',
|
||||
'only-if-needed',
|
||||
]
|
||||
if V in {'main', 'HEAD'}:
|
||||
req = 'https://github.com/jupyterhub/jupyterhub/archive/HEAD.tar.gz'
|
||||
if version.startswith("git:"):
|
||||
ref = version.partition(":")[-1]
|
||||
req = f"https://github.com/jupyterhub/jupyterhub/archive/{ref}.tar.gz"
|
||||
else:
|
||||
version_info = [ int(part) for part in V.split('.') ]
|
||||
version_info[-1] += 1
|
||||
upper_bound = '.'.join(map(str, version_info))
|
||||
vs = '>=%s,<%s' % (V, upper_bound)
|
||||
req = 'jupyterhub%s' % vs
|
||||
req = f"jupyterhub=={version}"
|
||||
|
||||
check_call(pip_install + [req])
|
||||
|
Reference in New Issue
Block a user