├── .bumpversion.cfg ├── .envrc ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── compose ├── README.md ├── config-mysql.ini ├── config-pg.ini ├── config-redis.ini ├── mysql.yml ├── pg.yml ├── redis.yml └── wait-for ├── cp-static.sh ├── py3-alpine ├── Dockerfile ├── config.ini └── make-config.sh ├── py3-baseimage ├── Dockerfile ├── config.ini ├── make-config.sh └── pypicloud-uwsgi.sh ├── static ├── baseimage │ └── pypicloud-uwsgi.sh ├── config.ini └── make-config.sh └── test.sh /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.3.12 3 | tag_name = {new_version} 4 | files = py3-alpine/Dockerfile py3-baseimage/Dockerfile 5 | commit = True 6 | tag = True 7 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | layout python 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | release: 4 | types: [released, prereleased] 5 | workflow_dispatch: # allow manually running from the Actions tab 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | include: 13 | - image: baseimage 14 | platform: linux/amd64,linux/arm64 15 | - image: alpine 16 | platform: linux/amd64 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: docker/setup-qemu-action@v1 20 | - uses: docker/setup-buildx-action@v1 21 | - uses: docker/login-action@v1 22 | with: 23 | username: ${{ secrets.DOCKERHUB_USERNAME }} 24 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 25 | - run: bash build.sh --publish -i ${{matrix.image}} -p ${{matrix.platform}} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steven Arcangeli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pypicloud Docker 2 | ================ 3 | 4 | This is a docker container for running pypicloud. To test pypicloud and see it 5 | working, run this command: 6 | 7 | ``` 8 | docker run -it --rm -p 8080:8080 stevearc/pypicloud 9 | ``` 10 | 11 | This will start pypicloud with default settings inside the docker container and 12 | serve it on port 8080. The default settings are sufficient to play with, but 13 | they should not be used in production. 14 | 15 | You can access the `pypicloud-make-config` command from the docker image, which 16 | will give you a decent starting config that you can customize for your use. 17 | 18 | ``` 19 | docker run -it --rm stevearc/pypicloud make-config 20 | ``` 21 | 22 | This prints the config to stdout. If you want it to output a file, you can mount 23 | the working directory and provide an output file. 24 | 25 | ``` 26 | docker run -it --rm -v $(pwd):/out stevearc/pypicloud make-config -r /out/config.ini 27 | ``` 28 | 29 | The config file in the container is located at `/etc/pypicloud/config.ini`. To 30 | use your own config file, mount it as a volume. 31 | 32 | ``` 33 | docker run -p 8080:8080 \ 34 | -v /path/to/my/config.ini:/etc/pypicloud/config.ini:ro \ 35 | stevearc/pypicloud 36 | ``` 37 | 38 | See the [list of configuration 39 | options](http://pypicloud.readthedocs.org/en/latest/topics/configuration.html) 40 | for details about the config file. 41 | 42 | ## Compose 43 | 44 | If you want to use pypicloud with Docker Compose, you can find some starter 45 | examples at https://github.com/stevearc/pypicloud-docker/tree/master/compose 46 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | 4 | usage="$0 -i [options] 5 | -i \"baseimage\" or \"alpine\" 6 | -p Build images for this platform(s) 7 | --test Run tests after build 8 | --publish Run tests and push images after build 9 | " 10 | while getopts "h-:i:p:" opt; do 11 | case $opt in 12 | -) 13 | case $OPTARG in 14 | test) 15 | TEST=1 16 | ;; 17 | publish) 18 | TEST=1 19 | PUBLISH=1 20 | ;; 21 | *) 22 | echo "$usage" 23 | exit 1 24 | ;; 25 | esac 26 | ;; 27 | i) 28 | IMAGE="$OPTARG" 29 | ;; 30 | p) 31 | PLATFORM="$OPTARG" 32 | ;; 33 | h) 34 | echo "$usage" 35 | exit 0 36 | ;; 37 | \?) 38 | echo "$usage" 39 | exit 1 40 | ;; 41 | esac 42 | done 43 | if [ -z "$IMAGE" ]; then 44 | echo "$usage" 45 | exit 1 46 | fi 47 | shift $((OPTIND - 1)) 48 | 49 | tag=$(git describe --tags || echo "latest") 50 | platforms="${PLATFORM:-linux/amd64}" 51 | echo "Building ${tag} for platforms ${platforms}" 52 | set -x 53 | ./cp-static.sh 54 | 55 | case $IMAGE in 56 | baseimage) 57 | suffix="" 58 | ;; 59 | alpine) 60 | suffix="-$IMAGE" 61 | ;; 62 | *) 63 | echo "Unrecognized image $IMAGE" 64 | exit 1 65 | ;; 66 | esac 67 | docker buildx build "--platform=${platforms}" --pull "py3-$IMAGE" -t "stevearc/pypicloud:latest${suffix}" 68 | 69 | if [ $TEST ]; then 70 | echo "Build and load single-platform image for test" 71 | if [[ $platforms =~ , ]]; then 72 | docker buildx build "--platform=linux/amd64" "py3-$IMAGE" --load -t "stevearc/pypicloud:latest${suffix}" 73 | else 74 | docker buildx build "--platform=${platforms}" "py3-$IMAGE" --load -t "stevearc/pypicloud:latest${suffix}" 75 | fi 76 | echo "Running tests" 77 | ./test.sh "latest$suffix" 78 | echo "Tests passed" 79 | fi 80 | 81 | if [ $PUBLISH ]; then 82 | if [ -n "$(git status --porcelain)" ]; then 83 | git status 84 | echo "" 85 | echo "Repo is not clean. Refusing to publish." 86 | exit 1 87 | fi 88 | docker buildx build "--platform=${platforms}" --push "py3-$IMAGE" -t "stevearc/pypicloud:latest$suffix" 89 | if git describe --tags --exact-match; then 90 | docker buildx build "--platform=${platforms}" --push "py3-$IMAGE" -t "stevearc/pypicloud:${tag}${suffix}" 91 | else 92 | echo "Not pushing $tag because it is not an exact version" 93 | fi 94 | fi 95 | -------------------------------------------------------------------------------- /compose/README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose files 2 | These are docker-compose files that I use for testing. You may also find them 3 | useful. 4 | 5 | You can bring them up with `docker-compose -f up` 6 | -------------------------------------------------------------------------------- /compose/config-mysql.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | 13 | db.url = mysql://root@mysql:3306/pypi?charset=utf8mb4 14 | 15 | pypi.auth = sql 16 | auth.db.url = mysql://root@mysql:3306/pypi?charset=utf8mb4 17 | 18 | # For beaker 19 | session.encrypt_key = replaceme 20 | session.validate_key = replaceme 21 | session.secure = false 22 | 23 | ### 24 | # wsgi server configuration 25 | ### 26 | 27 | [uwsgi] 28 | paste = config:%p 29 | paste-logger = %p 30 | master = true 31 | uid = pypicloud 32 | gid = pypicloud 33 | processes = 20 34 | reload-mercy = 15 35 | worker-reload-mercy = 15 36 | max-requests = 1000 37 | enable-threads = true 38 | http = 0.0.0.0:8080 39 | 40 | ### 41 | # logging configuration 42 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 43 | ### 44 | 45 | [loggers] 46 | keys = root, boto 47 | 48 | [handlers] 49 | keys = console 50 | 51 | [formatters] 52 | keys = generic 53 | 54 | [logger_root] 55 | level = DEBUG 56 | handlers = console 57 | 58 | [logger_boto] 59 | level = WARN 60 | qualname = boto 61 | handlers = 62 | 63 | [handler_console] 64 | class = StreamHandler 65 | args = (sys.stderr,) 66 | level = NOTSET 67 | formatter = generic 68 | 69 | [formatter_generic] 70 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 71 | -------------------------------------------------------------------------------- /compose/config-pg.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | 13 | db.url = postgresql://postgres@postgres:5432/postgres 14 | 15 | pypi.auth = sql 16 | auth.db.url = postgresql://postgres@postgres:5432/postgres 17 | 18 | # For beaker 19 | session.encrypt_key = replaceme 20 | session.validate_key = replaceme 21 | session.secure = false 22 | 23 | ### 24 | # wsgi server configuration 25 | ### 26 | 27 | [uwsgi] 28 | paste = config:%p 29 | paste-logger = %p 30 | master = true 31 | uid = pypicloud 32 | gid = pypicloud 33 | processes = 20 34 | reload-mercy = 15 35 | worker-reload-mercy = 15 36 | max-requests = 1000 37 | enable-threads = true 38 | http = 0.0.0.0:8080 39 | 40 | ### 41 | # logging configuration 42 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 43 | ### 44 | 45 | [loggers] 46 | keys = root, boto 47 | 48 | [handlers] 49 | keys = console 50 | 51 | [formatters] 52 | keys = generic 53 | 54 | [logger_root] 55 | level = DEBUG 56 | handlers = console 57 | 58 | [logger_boto] 59 | level = WARN 60 | qualname = boto 61 | handlers = 62 | 63 | [handler_console] 64 | class = StreamHandler 65 | args = (sys.stderr,) 66 | level = NOTSET 67 | formatter = generic 68 | 69 | [formatter_generic] 70 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 71 | -------------------------------------------------------------------------------- /compose/config-redis.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | 13 | pypi.auth = sql 14 | auth.db.url = sqlite:////var/lib/pypicloud/db.sqlite 15 | 16 | pypi.db = redis 17 | db.url = redis://redis:6379/0 18 | 19 | # For beaker 20 | session.encrypt_key = replaceme 21 | session.validate_key = replaceme 22 | session.secure = false 23 | 24 | ### 25 | # wsgi server configuration 26 | ### 27 | 28 | [uwsgi] 29 | paste = config:%p 30 | paste-logger = %p 31 | master = true 32 | uid = pypicloud 33 | gid = pypicloud 34 | processes = 20 35 | reload-mercy = 15 36 | worker-reload-mercy = 15 37 | max-requests = 1000 38 | enable-threads = true 39 | http = 0.0.0.0:8080 40 | 41 | ### 42 | # logging configuration 43 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 44 | ### 45 | 46 | [loggers] 47 | keys = root, boto 48 | 49 | [handlers] 50 | keys = console 51 | 52 | [formatters] 53 | keys = generic 54 | 55 | [logger_root] 56 | level = DEBUG 57 | handlers = console 58 | 59 | [logger_boto] 60 | level = WARN 61 | qualname = boto 62 | handlers = 63 | 64 | [handler_console] 65 | class = StreamHandler 66 | args = (sys.stderr,) 67 | level = NOTSET 68 | formatter = generic 69 | 70 | [formatter_generic] 71 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 72 | -------------------------------------------------------------------------------- /compose/mysql.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | pypicloud: 4 | command: sh -c '/wait-for -t 30 mysql:3306 -- uwsgi --die-on-term /etc/pypicloud/config.ini' 5 | depends_on: 6 | - mysql 7 | image: stevearc/pypicloud 8 | ports: 9 | - "8080:8080" 10 | volumes: 11 | - ./config-mysql.ini:/etc/pypicloud/config.ini:ro 12 | - ./wait-for:/wait-for:ro 13 | mysql: 14 | image: mysql 15 | environment: 16 | MYSQL_DATABASE: pypi 17 | MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' 18 | -------------------------------------------------------------------------------- /compose/pg.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | pypicloud: 4 | command: sh -c '/wait-for postgres:5432 -- uwsgi --die-on-term /etc/pypicloud/config.ini' 5 | depends_on: 6 | - postgres 7 | image: stevearc/pypicloud 8 | ports: 9 | - "8080:8080" 10 | volumes: 11 | - ./config-pg.ini:/etc/pypicloud/config.ini:ro 12 | - ./wait-for:/wait-for:ro 13 | postgres: 14 | image: postgres 15 | environment: 16 | POSTGRES_PASSWORD: 17 | POSTGRES_DB: postgres 18 | POSTGRES_HOST_AUTH_METHOD: trust 19 | -------------------------------------------------------------------------------- /compose/redis.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | pypicloud: 4 | command: sh -c '/wait-for redis:6379 -- uwsgi --die-on-term /etc/pypicloud/config.ini' 5 | depends_on: 6 | - redis 7 | image: stevearc/pypicloud 8 | ports: 9 | - "8080:8080" 10 | volumes: 11 | - ./config-redis.ini:/etc/pypicloud/config.ini:ro 12 | - ./wait-for:/wait-for:ro 13 | redis: 14 | image: redis 15 | -------------------------------------------------------------------------------- /compose/wait-for: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This was pulled from https://github.com/mrako/wait-for 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (c) 2017 Eficode Oy 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | TIMEOUT=15 26 | QUIET=0 27 | 28 | echoerr() { 29 | if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi 30 | } 31 | 32 | usage() { 33 | exitcode="$1" 34 | cat << USAGE >&2 35 | Usage: 36 | $cmdname host:port [-t timeout] [-- command args] 37 | -q | --quiet Do not output any status messages 38 | -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout 39 | -- COMMAND ARGS Execute command with args after the test finishes 40 | USAGE 41 | exit "$exitcode" 42 | } 43 | 44 | wait_for() { 45 | for i in `seq $TIMEOUT` ; do 46 | nc -z "$HOST" "$PORT" > /dev/null 2>&1 47 | 48 | result=$? 49 | if [ $result -eq 0 ] ; then 50 | if [ $# -gt 0 ] ; then 51 | exec "$@" 52 | fi 53 | exit 0 54 | fi 55 | sleep 1 56 | done 57 | echo "Operation timed out" >&2 58 | exit 1 59 | } 60 | 61 | while [ $# -gt 0 ] 62 | do 63 | case "$1" in 64 | *:* ) 65 | HOST=$(printf "%s\n" "$1"| cut -d : -f 1) 66 | PORT=$(printf "%s\n" "$1"| cut -d : -f 2) 67 | shift 1 68 | ;; 69 | -q | --quiet) 70 | QUIET=1 71 | shift 1 72 | ;; 73 | -t) 74 | TIMEOUT="$2" 75 | if [ "$TIMEOUT" = "" ]; then break; fi 76 | shift 2 77 | ;; 78 | --timeout=*) 79 | TIMEOUT="${1#*=}" 80 | shift 1 81 | ;; 82 | --) 83 | shift 84 | break 85 | ;; 86 | --help) 87 | usage 0 88 | ;; 89 | *) 90 | echoerr "Unknown argument: $1" 91 | usage 1 92 | ;; 93 | esac 94 | done 95 | 96 | if [ "$HOST" = "" -o "$PORT" = "" ]; then 97 | echoerr "Error: you need to provide a host and port to test." 98 | usage 2 99 | fi 100 | 101 | # Try to install nc if it is not present 102 | if ! command -v nc > /dev/null; then 103 | if command -v apt-get > /dev/null; then 104 | apt-get install -qy netcat 105 | fi 106 | fi 107 | 108 | wait_for "$@" 109 | -------------------------------------------------------------------------------- /cp-static.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | for base in alpine baseimage; do 5 | dir="py3-${base}" 6 | for file in static/*; do 7 | if [ -f "$file" ]; then 8 | cp "$file" "$dir" 9 | elif [ -d "$file" ] && [ "$(basename "$file")" = "$base" ]; then 10 | cp "$file/"* "$dir" 11 | fi 12 | done 13 | done 14 | -------------------------------------------------------------------------------- /py3-alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine3.12 2 | MAINTAINER Steven Arcangeli 3 | 4 | EXPOSE 8080 5 | WORKDIR /app/ 6 | 7 | # Install packages required 8 | ENV PYPICLOUD_VERSION 1.3.12 9 | ENV CARGO_NET_GIT_FETCH_WITH_CLI true 10 | RUN --mount=type=tmpfs,target=/root/.cargo \ 11 | apk add --no-cache --virtual build-deps python3-dev mariadb-dev postgresql-dev build-base gcc \ 12 | linux-headers openldap-dev autoconf automake make libffi-dev libressl-dev musl-dev git curl \ 13 | && curl –proto '=https' –tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ 14 | && source ~/.cargo/env \ 15 | && apk add --no-cache libldap libpq mariadb-connector-c-dev mariadb-connector-c util-linux-dev g++ \ 16 | && python -m pip install --no-cache-dir --upgrade pip setuptools \ 17 | && python -m pip install --no-cache-dir pypicloud[all_plugins]==$PYPICLOUD_VERSION \ 18 | requests uwsgi pastescript mysqlclient psycopg2-binary bcrypt cryptography \ 19 | --no-binary cryptography,uwsgi \ 20 | && adduser -D -s /bin/sh -h /var/lib/pypicloud/ pypicloud \ 21 | && apk del --no-cache build-deps \ 22 | && mkdir -p /etc/pypicloud 23 | 24 | # Add the command for easily creating config files 25 | ADD config.ini /etc/pypicloud/config.ini 26 | ADD make-config.sh /usr/local/bin/make-config 27 | 28 | # Create a working directory for pypicloud 29 | VOLUME /var/lib/pypicloud 30 | 31 | # Run as pypicloud user 32 | USER pypicloud 33 | 34 | CMD ["uwsgi", "--die-on-term", "/etc/pypicloud/config.ini"] 35 | -------------------------------------------------------------------------------- /py3-alpine/config.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | pypi.auth = sql 13 | 14 | db.url = sqlite:////var/lib/pypicloud/db.sqlite 15 | auth.db.url = sqlite:////var/lib/pypicloud/db.sqlite 16 | 17 | # For beaker 18 | session.encrypt_key = replaceme 19 | session.validate_key = replaceme 20 | session.secure = false 21 | 22 | ### 23 | # wsgi server configuration 24 | ### 25 | 26 | [uwsgi] 27 | paste = config:%p 28 | paste-logger = %p 29 | master = true 30 | uid = pypicloud 31 | gid = pypicloud 32 | processes = 20 33 | reload-mercy = 15 34 | worker-reload-mercy = 15 35 | max-requests = 1000 36 | enable-threads = true 37 | http = 0.0.0.0:8080 38 | 39 | ### 40 | # logging configuration 41 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 42 | ### 43 | 44 | [loggers] 45 | keys = root, boto 46 | 47 | [handlers] 48 | keys = console 49 | 50 | [formatters] 51 | keys = generic 52 | 53 | [logger_root] 54 | level = INFO 55 | handlers = console 56 | 57 | [logger_boto] 58 | level = WARN 59 | qualname = boto 60 | handlers = 61 | 62 | [handler_console] 63 | class = StreamHandler 64 | args = (sys.stderr,) 65 | level = NOTSET 66 | formatter = generic 67 | 68 | [formatter_generic] 69 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 70 | -------------------------------------------------------------------------------- /py3-alpine/make-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$1" ]; then 3 | ppc-make-config -r 4 | else 5 | ppc-make-config "$@" 6 | fi 7 | -------------------------------------------------------------------------------- /py3-baseimage/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:focal-1.1.0 2 | MAINTAINER Steven Arcangeli 3 | 4 | ENV PYPICLOUD_VERSION 1.3.12 5 | 6 | EXPOSE 8080 7 | 8 | # Install packages required 9 | RUN --mount=type=tmpfs,target=/root/.cargo \ 10 | apt-get update -qq \ 11 | && DEBIAN_FRONTEND=noninteractive apt-get install -qy python3-pip \ 12 | python3-dev libldap2-dev libsasl2-dev libmysqlclient-dev libffi-dev libssl-dev apache2-utils \ 13 | cargo libpq-dev \ 14 | && python3 -m pip install --no-cache-dir --upgrade pip \ 15 | && python3 -m pip install --no-cache-dir --upgrade setuptools \ 16 | && python3 -m pip install --no-cache-dir pypicloud[all_plugins]==$PYPICLOUD_VERSION \ 17 | requests uwsgi pastescript mysqlclient psycopg2-binary bcrypt \ 18 | # Create the pypicloud user 19 | && groupadd -r pypicloud \ 20 | && useradd -r -g pypicloud -d /var/lib/pypicloud -m pypicloud \ 21 | # Make sure this directory exists for the baseimage init 22 | && mkdir -p /etc/my_init.d 23 | 24 | # Add the startup service 25 | ADD pypicloud-uwsgi.sh /etc/my_init.d/pypicloud-uwsgi.sh 26 | 27 | # Add the pypicloud config file 28 | RUN mkdir -p /etc/pypicloud 29 | ADD config.ini /etc/pypicloud/config.ini 30 | 31 | # Create a working directory for pypicloud 32 | VOLUME /var/lib/pypicloud 33 | 34 | # Add the command for easily creating config files 35 | ADD make-config.sh /usr/local/bin/make-config 36 | 37 | # Add an environment variable that pypicloud-uwsgi.sh uses to determine which 38 | # user to run as 39 | ENV UWSGI_USER pypicloud 40 | 41 | # Use baseimage-docker's init system. 42 | CMD ["/sbin/my_init"] 43 | -------------------------------------------------------------------------------- /py3-baseimage/config.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | pypi.auth = sql 13 | 14 | db.url = sqlite:////var/lib/pypicloud/db.sqlite 15 | auth.db.url = sqlite:////var/lib/pypicloud/db.sqlite 16 | 17 | # For beaker 18 | session.encrypt_key = replaceme 19 | session.validate_key = replaceme 20 | session.secure = false 21 | 22 | ### 23 | # wsgi server configuration 24 | ### 25 | 26 | [uwsgi] 27 | paste = config:%p 28 | paste-logger = %p 29 | master = true 30 | uid = pypicloud 31 | gid = pypicloud 32 | processes = 20 33 | reload-mercy = 15 34 | worker-reload-mercy = 15 35 | max-requests = 1000 36 | enable-threads = true 37 | http = 0.0.0.0:8080 38 | 39 | ### 40 | # logging configuration 41 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 42 | ### 43 | 44 | [loggers] 45 | keys = root, boto 46 | 47 | [handlers] 48 | keys = console 49 | 50 | [formatters] 51 | keys = generic 52 | 53 | [logger_root] 54 | level = INFO 55 | handlers = console 56 | 57 | [logger_boto] 58 | level = WARN 59 | qualname = boto 60 | handlers = 61 | 62 | [handler_console] 63 | class = StreamHandler 64 | args = (sys.stderr,) 65 | level = NOTSET 66 | formatter = generic 67 | 68 | [formatter_generic] 69 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 70 | -------------------------------------------------------------------------------- /py3-baseimage/make-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$1" ]; then 3 | ppc-make-config -r 4 | else 5 | ppc-make-config "$@" 6 | fi 7 | -------------------------------------------------------------------------------- /py3-baseimage/pypicloud-uwsgi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$UWSGI_USER" ] || [ "$UWSGI_USER" = "root" ]; then 3 | uwsgi --die-on-term /etc/pypicloud/config.ini 4 | else 5 | /sbin/setuser "$UWSGI_USER" uwsgi --die-on-term /etc/pypicloud/config.ini 6 | fi 7 | -------------------------------------------------------------------------------- /static/baseimage/pypicloud-uwsgi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$UWSGI_USER" ] || [ "$UWSGI_USER" = "root" ]; then 3 | uwsgi --die-on-term /etc/pypicloud/config.ini 4 | else 5 | /sbin/setuser "$UWSGI_USER" uwsgi --die-on-term /etc/pypicloud/config.ini 6 | fi 7 | -------------------------------------------------------------------------------- /static/config.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use = egg:pypicloud 3 | 4 | pyramid.reload_templates = false 5 | pyramid.debug_authorization = false 6 | pyramid.debug_notfound = false 7 | pyramid.debug_routematch = false 8 | pyramid.default_locale_name = en 9 | 10 | pypi.storage = file 11 | storage.dir = /var/lib/pypicloud/packages 12 | pypi.auth = sql 13 | 14 | db.url = sqlite:////var/lib/pypicloud/db.sqlite 15 | auth.db.url = sqlite:////var/lib/pypicloud/db.sqlite 16 | 17 | # For beaker 18 | session.encrypt_key = replaceme 19 | session.validate_key = replaceme 20 | session.secure = false 21 | 22 | ### 23 | # wsgi server configuration 24 | ### 25 | 26 | [uwsgi] 27 | paste = config:%p 28 | paste-logger = %p 29 | master = true 30 | uid = pypicloud 31 | gid = pypicloud 32 | processes = 20 33 | reload-mercy = 15 34 | worker-reload-mercy = 15 35 | max-requests = 1000 36 | enable-threads = true 37 | http = 0.0.0.0:8080 38 | 39 | ### 40 | # logging configuration 41 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html 42 | ### 43 | 44 | [loggers] 45 | keys = root, boto 46 | 47 | [handlers] 48 | keys = console 49 | 50 | [formatters] 51 | keys = generic 52 | 53 | [logger_root] 54 | level = INFO 55 | handlers = console 56 | 57 | [logger_boto] 58 | level = WARN 59 | qualname = boto 60 | handlers = 61 | 62 | [handler_console] 63 | class = StreamHandler 64 | args = (sys.stderr,) 65 | level = NOTSET 66 | formatter = generic 67 | 68 | [formatter_generic] 69 | format = %(levelname)s %(asctime)s [%(name)s] %(message)s 70 | -------------------------------------------------------------------------------- /static/make-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$1" ]; then 3 | ppc-make-config -r 4 | else 5 | ppc-make-config "$@" 6 | fi 7 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | CONTAINER=pypi-test 4 | tag=${1-latest} 5 | 6 | cleanup() { 7 | docker rm -f "$CONTAINER" >/dev/null 2>&1 || true 8 | } 9 | trap cleanup SIGINT SIGTERM EXIT 10 | 11 | docker rm -f "$CONTAINER" >/dev/null 2>&1 || true 12 | docker run --pull never -d --name "$CONTAINER" -P "stevearc/pypicloud:$tag" 13 | sleep 5 14 | 15 | port=$(docker inspect "$CONTAINER" | jq -r '.[0].NetworkSettings.Ports["8080/tcp"][0].HostPort') 16 | if curl --fail "http://localhost:$port/health"; then 17 | echo -e "\nSuccess" 18 | else 19 | echo -e "\nFailed" 20 | exit 1 21 | fi 22 | --------------------------------------------------------------------------------