From e2cb258ef390f1fc215921f2e43f40d76023ebef Mon Sep 17 00:00:00 2001 From: ElevenNotes Date: Thu, 6 Feb 2025 11:27:51 +0100 Subject: [PATCH] switch to new github workflow and build process --- .github/workflows/docker.yml | 158 ++++++++++++++++++++++++++++ .github/workflows/release.yml | 26 ----- .json | 10 ++ LICENSE | 21 ---- README.md | 55 ++++------ RELEASE.md | 2 + amd64.dockerfile | 95 ----------------- arch.dockerfile | 91 ++++++++++++++++ arm64v8.dockerfile | 100 ------------------ compose.yaml | 12 +-- hooks/post_push | 4 - hooks/pre_build | 2 - multi-arch-manifest-latest.yaml | 11 -- rootfs/usr/local/bin/entrypoint.sh | 5 +- rootfs/usr/local/bin/healthcheck.sh | 2 + whodb.png | Bin 33224 -> 0 bytes 16 files changed, 288 insertions(+), 306 deletions(-) create mode 100644 .github/workflows/docker.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .json delete mode 100644 LICENSE create mode 100644 RELEASE.md delete mode 100644 amd64.dockerfile create mode 100644 arch.dockerfile delete mode 100644 arm64v8.dockerfile delete mode 100644 hooks/post_push delete mode 100644 hooks/pre_build delete mode 100644 multi-arch-manifest-latest.yaml create mode 100644 rootfs/usr/local/bin/healthcheck.sh delete mode 100644 whodb.png diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..fc99076 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,158 @@ +name: create and publish docker image + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +env: + DOCKER_USERNAME: 11notes + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + security-events: write + + steps: + - name: init / checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - name: init / .json to env + uses: rgarcia-phi/json-to-variables@9835d537368468c4e4de5254dc3efeadda183793 + with: + filename: '.json' + + - name: init / setup environment + run: | + : # set default arch if not set + echo "IMAGE_ARCH=${json_arch:-linux/amd64,linux/arm64}" >> $GITHUB_ENV + + : # create tags for semver, stable and other shenanigans + export LOCAL_SHA=$(git rev-parse --short HEAD) + export LOCAL_SEMVER_MAJOR=$(awk -F. '{ print $1 }' <<< ${json_version}) + export LOCAL_SEMVER_MINOR=$(awk -F. '{ print $2 }' <<< ${json_version}) + export LOCAL_SEMVER_PATCH=$(awk -F. '{ print $3 }' <<< ${json_version}) + export LOCAL_TAGS="${json_image}:latest" + if [ ! -z ${LOCAL_SEMVER_MAJOR} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}"; fi + if [ ! -z ${LOCAL_SEMVER_MINOR} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}.${LOCAL_SEMVER_MINOR}"; fi + if [ ! -z ${LOCAL_SEMVER_PATCH} ]; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SEMVER_MAJOR}.${LOCAL_SEMVER_MINOR}.${LOCAL_SEMVER_PATCH}"; fi + if echo "${LOCAL_TAGS}" | grep -q "${json_stable}" ; then LOCAL_TAGS="${LOCAL_TAGS},${json_image}:stable"; fi + if [ ! -z ${json_tags} ]; then SPECIAL_LOCAL_TAGS=$(echo ${json_tags} | sed 's/,/ /g'); for LOCAL_TAG in ${json_tags}; do LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_TAG}"; done; fi + LOCAL_TAGS="${LOCAL_TAGS},${json_image}:${LOCAL_SHA}" + echo "IMAGE_TAGS=${LOCAL_TAGS}" >> $GITHUB_ENV + + : # if for whatever reason UID/GID must be changed at build time + echo "IMAGE_UID=${json_uid:-1000}" >> $GITHUB_ENV + echo "IMAGE_GID=${json_gid:-1000}" >> $GITHUB_ENV + + - name: docker / login to hub + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + with: + username: ${{ env.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: docker / setup qemu + uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a + + - name: docker / setup buildx + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 + + - name: grype / build & push + uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d + with: + context: . + file: arch.dockerfile + push: true + platforms: ${{ env.IMAGE_ARCH }} + cache-from: type=registry,ref=${{ env.json_image }}:buildcache + cache-to: type=registry,ref=${{ env.json_image }}:buildcache,mode=max,compression=zstd,force-compression=true + build-args: | + APP_IMAGE=${{ env.json_image }} + APP_NAME=${{ env.json_name }} + APP_VERSION=${{ env.json_version }} + APP_ROOT=${{ env.json_root }} + APP_UID=${{ env.IMAGE_UID }} + APP_GID=${{ env.IMAGE_GID }} + tags: | + ${{ env.json_image }}:grype + + - name: grype / scan + id: scan + uses: anchore/scan-action@abae793926ec39a78ab18002bc7fc45bbbd94342 + with: + image: ${{ env.json_image }}:grype + severity-cutoff: high + + - name: grype / delete tag + if: success() || failure() + run: | + curl --request DELETE \ + --url https://hub.docker.com/v2/repositories/${{ env.json_image }}/tags/grype/ \ + --header 'authorization: jwt ${{ secrets.DOCKER_TOKEN }}' \ + --header 'content-type: application/json' \ + --fail + + - name: grype / report / upload + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} + + - name: grype / report / print + run: cat ${{ steps.scan.outputs.sarif }} + + - name: docker / build & push + uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d + with: + context: . + file: arch.dockerfile + push: true + sbom: true + provenance: mode=max + platforms: ${{ env.IMAGE_ARCH }} + cache-from: type=registry,ref=${{ env.json_image }}:buildcache + cache-to: type=registry,ref=${{ env.json_image }}:buildcache,mode=max,compression=zstd,force-compression=true + build-args: | + APP_IMAGE=${{ env.json_image }} + APP_NAME=${{ env.json_name }} + APP_VERSION=${{ env.json_version }} + APP_ROOT=${{ env.json_root }} + APP_UID=${{ env.IMAGE_UID }} + APP_GID=${{ env.IMAGE_GID }} + tags: | + ${{ env.IMAGE_TAGS }} + + - name: github / create release notes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release create ${{ github.ref_name }} -F RELEASE.md + + - name: github / update description and set repo defaults + run: | + curl --request PATCH \ + --url https://api.github.com/repos/${{ github.repository }} \ + --header 'authorization: Bearer ${{ secrets.REPOSITORY_TOKEN }}' \ + --header 'content-type: application/json' \ + --data '{ + "description":"${{ env.json_description }}", + "homepage":"", + "has_issues":true, + "has_discussions":true, + "has_projects":false, + "has_wiki":false + }' \ + --fail + + - name: docker / push README.md to docker hub + uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 + env: + DOCKER_USER: ${{ env.DOCKER_USERNAME }} + DOCKER_PASS: ${{ secrets.DOCKER_TOKEN }} + with: + destination_container_repo: ${{ env.json_image }} + provider: dockerhub + short_description: ${{ env.json_description }} + readme_file: 'README.md' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 05d468b..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: create release notes - -on: - push: - tags: - - "*" - - "!amd64*" - - "!arm64*" - -permissions: - contents: write - -jobs: - release: - name: release - runs-on: ubuntu-latest - steps: - - name: create release notes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ github.ref_name }} - run: | - gh release create "$tag" \ - --repo="$GITHUB_REPOSITORY" \ - --title="${tag#v}" \ - --generate-notes \ No newline at end of file diff --git a/.json b/.json new file mode 100644 index 0000000..5631289 --- /dev/null +++ b/.json @@ -0,0 +1,10 @@ +{ + "image":"11notes/kms", + "description":"Activate any version of Windows and Office, forever", + "name":"kms", + "version":"646f476", + "root":"/kms", + + "stable":"646f476", + "parent":"11notes/alpine:stable" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 16df8f8..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 11notes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 3283db7..3c75140 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ ![Banner](https://github.com/11notes/defaults/blob/main/static/img/banner.png?raw=true) -# ๐Ÿ”๏ธ Alpine - kms -[](https://github.com/11notes/docker-kms/tree/latest) ![size](https://img.shields.io/docker/image-size/11notes/kms/latest?color=0eb305) ![version](https://img.shields.io/docker/v/11notes/kms/latest?color=eb7a09) ![pulls](https://img.shields.io/docker/pulls/11notes/kms?color=2b75d6) +# ๐Ÿ”๏ธ kms on Alpine +[](https://github.com/11notes/docker-kms)![size](https://img.shields.io/docker/image-size/11notes/kms/646f476?color=0eb305)![version](https://img.shields.io/docker/v/11notes/kms/646f476?color=eb7a09)![pulls](https://img.shields.io/docker/pulls/11notes/kms?color=2b75d6)[](https://github.com/11notes/docker-kms/issues) **Activate any version of Windows and Office, forever** ![slmgr](https://github.com/11notes/docker-kms/blob/main/slmgr.png?raw=true) -![whodb](https://github.com/11notes/docker-kms/blob/main/whodb.png?raw=true) -# SYNOPSIS +# SYNOPSIS ๐Ÿ“– **What can I do with this?** This image will run a KMS server you can use to activate any version of Windows and Office, forever. Works with: @@ -33,15 +32,15 @@ Works with: - Microsoft Office 2021 ( Volume License ) - Microsoft Office 2024 ( Volume License ) -# VOLUMES +# VOLUMES ๐Ÿ“ * **/kms/var** - Directory of the activation database -# COMPOSE +# COMPOSE โœ‚๏ธ ```yaml name: "kms" services: kms: - image: "11notes/kms:latest" + image: "11notes/kms:646f476" container_name: "kms" environment: TZ: Europe/Zurich @@ -50,21 +49,11 @@ services: ports: - "1688:1688/tcp" restart: always - whodb: - image: "11notes/whodb:latest" - container_name: "whodb" - environment: - TZ: Europe/Zurich - volumes: - - "var:/whodb/var" - ports: - - "8080:8080/tcp" - restart: always volumes: var: ``` -# EXAMPLES + ## Windows Server 2025 Datacenter. List of [GVLK](https://learn.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys) ```cmd slmgr /ipk D764K-2NDRG-47T6Q-P8T8W-YP6DF @@ -81,20 +70,20 @@ Activate server slmgr /ato ``` -# DEFAULT SETTINGS +# DEFAULT SETTINGS ๐Ÿ—ƒ๏ธ | Parameter | Value | Description | | --- | --- | --- | -| `user` | docker | user docker | -| `uid` | 1000 | user id 1000 | -| `gid` | 1000 | group id 1000 | +| `user` | docker | user name | +| `uid` | 1000 | [user identifier](https://en.wikipedia.org/wiki/User_identifier) | +| `gid` | 1000 | [group identifier](https://en.wikipedia.org/wiki/Group_identifier) | | `home` | /kms | home directory of user docker | | `database` | /kms/var/kms.db | SQlite database holding all client data | -# ENVIRONMENT +# ENVIRONMENT ๐Ÿ“ | Parameter | Value | Default | | --- | --- | --- | | `TZ` | [Time Zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | | -| `DEBUG` | Show debug information | | +| `DEBUG` | Show debug messages from image **not** app | | | `KMS_IP` | localhost or 127.0.0.1 or a dedicated IP | 0.0.0.0 | | `KMS_PORT` | any port > 1024 | 1688 | | `KMS_LOCALE` | see Microsoft LICD specification | 1033 (en-US) | @@ -103,20 +92,20 @@ slmgr /ato | `KMS_RENEWALINTERVAL` | re-activation after N minutes | 259200 (180 days) | | `KMS_LOGLEVEL` | CRITICAL, ERROR, WARNING, INFO, DEBUG, MININFO | INFO | -# SOURCE -* [11notes/kms:latest](https://github.com/11notes/docker-kms/tree/latest) +# SOURCE ๐Ÿ’พ +* [11notes/kms](https://github.com/11notes/docker-kms) -# PARENT IMAGE +# PARENT IMAGE ๐Ÿ›๏ธ * [11notes/alpine:stable](https://hub.docker.com/r/11notes/alpine) -# BUILT WITH +# BUILT WITH ๐Ÿงฐ * [py-kms](https://github.com/Py-KMS-Organization/py-kms) * [alpine](https://alpinelinux.org) -# TIPS -* Use a reverse proxy like Traefik, Nginx to terminate TLS with a valid certificate +# TIPS ๐Ÿ“Œ +* Use a reverse proxy like Traefik, Nginx, HAproxy to terminate TLS with a valid certificate * Use Letโ€™s Encrypt certificates to protect your SSL endpoints * [Microsoft LICD](https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a) - -# ElevenNotesโ„ข๏ธ -This image is provided to you at your own risk. Always make backups before updating an image to a different version. Check the [RELEASE.md](https://github.com/11notes/docker-kms/blob/latest/RELEASE.md) for breaking changes. You can find all my repositories on [github](https://github.com/11notes). \ No newline at end of file + +# ElevenNotesโ„ข๏ธ +This image is provided to you at your own risk. Always make backups before updating an image to a different version. Check the [releases](https://github.com/11notes/docker-kms/releases) for breaking changes. If you have any problems with using this image simply raise an [issue](https://github.com/11notes/docker-kms/issues), thanks . You can find all my repositories on [github](https://github.com/11notes?tab=repositories). \ No newline at end of file diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..811e5fb --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,2 @@ +### ๐Ÿช„ Features +* switch to new github workflow and build process \ No newline at end of file diff --git a/amd64.dockerfile b/amd64.dockerfile deleted file mode 100644 index 9a7e64d..0000000 --- a/amd64.dockerfile +++ /dev/null @@ -1,95 +0,0 @@ -# :: Util - FROM alpine as util - - RUN set -ex; \ - apk add --no-cache \ - git; \ - git clone https://github.com/11notes/util.git; - -# :: Build - FROM alpine as build - - RUN set -ex; \ - apk add --update --no-cache \ - curl \ - wget \ - unzip \ - build-base \ - linux-headers \ - make \ - cmake \ - g++ \ - git; \ - git clone https://github.com/Py-KMS-Organization/py-kms.git /tmp/py-kms; \ - mv /tmp/py-kms/py-kms /usr/local/bin; - -# :: Header - FROM python:3.11-alpine - COPY --from=build /usr/local/bin/ /usr/local/bin - COPY --from=util /util/docker /usr/local/bin - ENV APP_ROOT=/kms - ENV APP_NAME="kms" - ENV APP_VERSION="latest" - ENV KMS_IP=0.0.0.0 - ENV KMS_PORT=1688 - ENV KMS_LOCALE=1033 - ENV KMS_CLIENTCOUNT=25 - ENV KMS_ACTIVATIONINTERVAL=120 - ENV KMS_RENEWALINTERVAL=259200 - ENV KMS_LOGLEVEL="INFO" - -# :: Run - USER root - - # :: update image - RUN set -ex; \ - apk --no-cache --update add \ - curl \ - tzdata \ - shadow; \ - apk --no-cache upgrade; - - # :: prepare image - RUN set -ex; \ - mkdir -p ${APP_ROOT}; \ - mkdir -p ${APP_ROOT}/var; \ - touch /var/log/kms.log; \ - ln -sf /dev/stdout /var/log/kms.log; - - RUN set -ex; \ - apk add --no-cache \ - py3-configargparse \ - py3-pygments \ - python3-tkinter \ - sqlite-libs \ - python3-dev \ - sqlite-dev \ - gcc \ - musl-dev \ - py3-pip; \ - pip3 install --no-cache-dir \ - peewee \ - tzlocal \ - pytz \ - pysqlite3; - - # :: create user - RUN set -ex; \ - addgroup --gid 1000 -S docker; \ - adduser --uid 1000 -D -S -h / -s /sbin/nologin -G docker docker; - - # :: copy root filesystem changes and set correct permissions - COPY ./rootfs / - RUN set -ex; \ - chmod +x -R /usr/local/bin; \ - usermod -d ${APP_ROOT} docker; \ - chown -R 1000:1000 \ - ${APP_ROOT} \ - /var/log/kms.log; - -# :: Volumes - VOLUME ["${APP_ROOT}/var"] - -# :: Start - USER docker - ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] \ No newline at end of file diff --git a/arch.dockerfile b/arch.dockerfile new file mode 100644 index 0000000..476e6f9 --- /dev/null +++ b/arch.dockerfile @@ -0,0 +1,91 @@ +# :: Util + FROM alpine AS util + + RUN set -ex; \ + apk --no-cache --update add \ + git; \ + git clone https://github.com/11notes/docker-util.git; + +# :: Build / redis + FROM python:3.12-alpine AS build + + ARG TARGETARCH + ARG APP_VERSION + + USER root + + RUN set -ex; \ + apk --update --no-cache add \ + curl \ + wget \ + unzip \ + build-base \ + linux-headers \ + make \ + cmake \ + g++ \ + git; \ + pip3 install --upgrade pip; \ + pip3 install pyinstaller; \ + git clone https://github.com/Py-KMS-Organization/py-kms.git; \ + cd /py-kms/py-kms; \ + git checkout ${APP_VERSION}; \ + pyinstaller --onefile pykms_Server.py; \ + cp /py-kms/py-kms/dist/pykms_Server /usr/local/bin; + +# :: Header + FROM 11notes/alpine:stable + + # :: arguments + ARG TARGETARCH + ARG APP_IMAGE + ARG APP_NAME + ARG APP_VERSION + ARG APP_ROOT + + # :: environment + ENV APP_IMAGE=${APP_IMAGE} + ENV APP_NAME=${APP_NAME} + ENV APP_VERSION=${APP_VERSION} + ENV APP_ROOT=${APP_ROOT} + + ENV KMS_IP=0.0.0.0 + ENV KMS_PORT=1688 + ENV KMS_LOCALE=1033 + ENV KMS_CLIENTCOUNT=25 + ENV KMS_ACTIVATIONINTERVAL=120 + ENV KMS_RENEWALINTERVAL=259200 + ENV KMS_LOGLEVEL="INFO" + + # :: multi-stage + COPY --from=util /docker-util/src/ /usr/local/bin + COPY --from=build /usr/local/bin/ /usr/local/bin + + # :: Run + USER root + + # :: install application + RUN set -ex; \ + apk --no-cache --update add \ + python3; + + RUN set -ex; \ + mkdir -p ${APP_ROOT}/var; \ + touch /var/log/kms.log; \ + ln -sf /dev/stdout /var/log/kms.log; + + # :: copy filesystem changes and set correct permissions + COPY ./rootfs / + RUN set -ex; \ + chmod +x -R /usr/local/bin; \ + chown -R 1000:1000 \ + ${APP_ROOT}; + +# :: Volumes + VOLUME ["${APP_ROOT}/var"] + +# :: Monitor + HEALTHCHECK --interval=5s --timeout=2s CMD /usr/local/bin/healthcheck.sh || exit 1 + +# :: Start + USER docker \ No newline at end of file diff --git a/arm64v8.dockerfile b/arm64v8.dockerfile deleted file mode 100644 index 9c3d12d..0000000 --- a/arm64v8.dockerfile +++ /dev/null @@ -1,100 +0,0 @@ -# :: QEMU - FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu - -# :: Util - FROM alpine as util - - RUN set -ex; \ - apk add --no-cache \ - git; \ - git clone https://github.com/11notes/util.git; - -# :: Build - FROM --platform=linux/arm64 alpine as build - COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin - - RUN set -ex; \ - apk add --update --no-cache \ - curl \ - wget \ - unzip \ - build-base \ - linux-headers \ - make \ - cmake \ - g++ \ - git; \ - git clone https://github.com/Py-KMS-Organization/py-kms.git /tmp/py-kms; \ - mv /tmp/py-kms/py-kms /usr/local/bin; - -# :: Header - FROM --platform=linux/arm64 arm64v8/python:3.11-alpine - COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin - COPY --from=build /usr/local/bin/ /usr/local/bin - COPY --from=util /util/docker /usr/local/bin - ENV APP_ROOT=/kms - ENV APP_NAME="kms" - ENV APP_VERSION="latest" - ENV KMS_IP=0.0.0.0 - ENV KMS_PORT=1688 - ENV KMS_LOCALE=1033 - ENV KMS_CLIENTCOUNT=25 - ENV KMS_ACTIVATIONINTERVAL=120 - ENV KMS_RENEWALINTERVAL=259200 - ENV KMS_LOGLEVEL="INFO" - -# :: Run - USER root - - # :: update image - RUN set -ex; \ - apk --no-cache --update add \ - curl \ - tzdata \ - shadow; \ - apk --no-cache upgrade; - - # :: prepare image - RUN set -ex; \ - mkdir -p ${APP_ROOT}; \ - mkdir -p ${APP_ROOT}/var; \ - touch /var/log/kms.log; \ - ln -sf /dev/stdout /var/log/kms.log; - - RUN set -ex; \ - apk add --no-cache \ - py3-configargparse \ - py3-pygments \ - python3-tkinter \ - sqlite-libs \ - python3-dev \ - sqlite-dev \ - gcc \ - musl-dev \ - py3-pip; \ - pip3 install --no-cache-dir \ - peewee \ - tzlocal \ - pytz \ - pysqlite3; - - # :: create user - RUN set -ex; \ - addgroup --gid 1000 -S docker; \ - adduser --uid 1000 -D -S -h / -s /sbin/nologin -G docker docker; - - # :: copy root filesystem changes and set correct permissions - COPY ./rootfs / - RUN set -ex; \ - chmod +x -R /usr/local/bin; \ - usermod -d ${APP_ROOT} docker; \ - chown -R 1000:1000 \ - ${APP_ROOT} \ - /var/log/kms.log; - -# :: Volumes - VOLUME ["${APP_ROOT}/var"] - -# :: Start - USER docker - ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index 73146e7..20e820f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,7 +1,7 @@ name: "kms" services: kms: - image: "11notes/kms:latest" + image: "11notes/kms:646f476" container_name: "kms" environment: TZ: Europe/Zurich @@ -10,15 +10,5 @@ services: ports: - "1688:1688/tcp" restart: always - whodb: - image: "11notes/whodb:latest" - container_name: "whodb" - environment: - TZ: Europe/Zurich - volumes: - - "var:/whodb/var" - ports: - - "8080:8080/tcp" - restart: always volumes: var: \ No newline at end of file diff --git a/hooks/post_push b/hooks/post_push deleted file mode 100644 index 509b94d..0000000 --- a/hooks/post_push +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - curl -Lo manifest-tool https://github.com/estesp/manifest-tool/releases/download/v0.9.0/manifest-tool-linux-amd64 - chmod +x manifest-tool - ./manifest-tool push from-spec multi-arch-manifest-latest.yaml \ No newline at end of file diff --git a/hooks/pre_build b/hooks/pre_build deleted file mode 100644 index 13e5e55..0000000 --- a/hooks/pre_build +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash - docker run --rm --privileged multiarch/qemu-user-static:register --reset \ No newline at end of file diff --git a/multi-arch-manifest-latest.yaml b/multi-arch-manifest-latest.yaml deleted file mode 100644 index fac6a38..0000000 --- a/multi-arch-manifest-latest.yaml +++ /dev/null @@ -1,11 +0,0 @@ -image: 11notes/kms:latest -manifests: - - image: 11notes/kms:amd64-latest - platform: - architecture: amd64 - os: linux - - image: 11notes/kms:arm64v8-latest - platform: - architecture: arm64 - os: linux - variant: v8 \ No newline at end of file diff --git a/rootfs/usr/local/bin/entrypoint.sh b/rootfs/usr/local/bin/entrypoint.sh index 2d606b6..6c9123b 100644 --- a/rootfs/usr/local/bin/entrypoint.sh +++ b/rootfs/usr/local/bin/entrypoint.sh @@ -1,8 +1,7 @@ #!/bin/ash if [ -z "${1}" ]; then - elevenLogJSON info "starting ${APP_NAME}" - set -- "python3" \ - /usr/local/bin/py-kms/pykms_Server.py \ + eleven log start + set -- "pykms_Server" \ ${KMS_IP} \ ${KMS_PORT} \ -l ${KMS_LOCALE} \ diff --git a/rootfs/usr/local/bin/healthcheck.sh b/rootfs/usr/local/bin/healthcheck.sh new file mode 100644 index 0000000..f991b6c --- /dev/null +++ b/rootfs/usr/local/bin/healthcheck.sh @@ -0,0 +1,2 @@ +#!/bin/ash + netstat -an | grep -q ${KMS_PORT} \ No newline at end of file diff --git a/whodb.png b/whodb.png deleted file mode 100644 index 62b1054f8146ea0add554635301ea0966cb81ebc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33224 zcmeFZ2UL^W);0>Fpn!m~5tX_(3JM4!NI;bO#xnH^G#la!a%Kqi-gyh?Ea6GlTdhz^Se`_p5uuRb`Sc)n?UvSsO?6?S` zzI0{Ckl*`M*uG?{d-cV6-DypCnn)Cc;IR`do{D~$0? z4n$^<(>>h1iU1#DRzR*pb5B}um+;3MeHl7|Ogg1g3Pu-0lRIE6JWFSrE$Q5iki8WL zq}eaKW%Qqo?G@tW;0T#NAl>4!I}&*F9A4jtx@&=eVx{BMm5@uHz}sSRE5~I>JAn z{cFL878qHOWwROFYJ*h!`ScT(K>>&VUX1p#jkNx5V2+mtA)jx7%>Ln?=eyollH5OM z61UpSjd=QVX$Ifq^eA1{rW$KwFaLh?u81fz6t*rHz-c>Paq8!!@g*xf-Hx`vXQTiA z?0DmmtiP|OV*lZ)=jE@wf9dOYo0t5zeySlW1}U`XFa2ej91ovC5Vp;cyMIo#RJ)v; z8h{3gZx?xfk5K8Am>M2A;1IzXY_+83TXf3J8;Qy1bGCBRJzdU zKJ&XJ+g`pI90wkGJ>axWl61@X)tVOV8{~qlTZ#8~n5|FIBN9hC=D!s%b?Ql|Ko&)h zwMgjb&}9xwF;eyVs~4vZ(?DLWn^26QR6;j!HIbsU|!XGrp;=fP^hcw#hrWNynr`6LU6T3ZIwB( zwx6(m6|g!PwW~BZxCyg~l7RJCPKhzK#?gvqBwfb5&xi#6iM7sPM7f(6{q8WC-#4A{^E|H9wWkK^V2Lwq*TMZ{4&;r_@Z6Go=)% zg^9h#CYY8-0Z9&B0z7&Q&q46h^Y2YytuBt-vOsP0cPpGPBFSBxU@0a^#CCqdxtmf-$%U(C&n`wE_l{`$GPd$s<_6FAl zheX^IBU38&)`Mi~(7l;B$hCowPMJ3ZWAe-Yy!husn4Gc8#+NcT;fsKYJ|?oal=w&E zSFD;vMufH7Xr;TExLVGBAz?#@^nI5vS(-z8%Ot;Ejtg@J-wKbq6!NO1>b+fOJYy+= z`M9zCDFJ>urGLf(^#(e4dOs~%yo}C#AR!%2Fl;nGf$%qvgSir=!WdQvvHRwub|L1wVTR1n-I3}3Dwm%*hK!cnt|Ys*1r#h8^g2su z`L1EL&_biuUP zAm6F+x_oGn1@ZG)1aWyt^G}?(5VTg_38iL(zycRgVuL}eY3nL zA?@pV8L*N@J=@=UcD0@3Rb=hHCM>PX7%;Om^G#u35pkme`KgXGarpx@1-BTRpX`M9!Y~gn{jI=(T=*Msa_V;8A_#W|D zSb^N63Gs$g*2mx03QdC+0!yxKd>R2yFpcU~if9w_wYK7aGJmNTkRr8qYQZk%Tp>kg zoZ~G@>3&hOpChkE9mcGjd)aG z0@@F~d?yb`skEi+1nXM&netA_26rZ1CPCYCq33myptcY$m`>=Nef;@H9?9U^FN93j z3B-^I5t2dc{RUDDd?<{t4h+=t!%extnA9hm2pcEDp zM$NshL@7iQO7%QHtO1_RF*^K|JCX#~@SuKsZy@=^-lTeCG>p%oFYO7Sr&-s}UrP2w zx*AMzv0bhhnA&*K4}C#U$9twF%s+6N4FKxA7A0$0P=u|lLBk@nUhXe^BB@nn) z#1@_jM-yFH>+SFaE4Fjrmaq)*2gV9_Cct0w3jr8{P~m_?`JsaaGjt^m*(%%HF79e$ zc+NlcxL^#;+)0)%anrlnfC1>>wb5YG6lwjnYWCQ*CGFsqZ;Z}_h(tMD1E5M=-R3G- zkMfa=fD83pSz%GA@FxQ)dcu&Fg&zoEEilA?du^-%U%%QGOww^@G32EDrRg=FdGJl7 zL<7kvnQMS;b%4jdR&|hsl4lHBSm|1R3z-o{>Do`wO(aRc4KyAPM4>4etFVQuAm)M` z1{=@UiK0r(ppan3{Agl5%MeU|Eoa4|!+eV9V_UkrBf-tsd1GEL-*F^#WYOAptQSus z3d>2xXP*l%4C;(StaE{xOL9nxD@j1>EeC;~KGKW>L-92%BI16SKYHsFdfQ5m)|m-A zEFI4syvSU`_KvsLBmJiwa4~*4cN#bQNUR1tLkcmU+Oh!(?q-o0%5>z=+ecsSRyA$< zLD%M4&JAD~u@pulHCK)*17?tRrin?Tyhu}W@ltw#ykmg8PXKwPPNr!EZhsog!|Qoq zo&t+Jl@54v&jrDBJ(zBn)gJaJ!X}Ikc-yh6ArMFy=vXt>7b@rhTyi{r%&>F ztn?u=vUwYuhdJ!qcYb%|BE+A7j@f6Gbz@z9pAf!b!?7I^J-P`}YWNV%GjWpHfhLQ2 zoTTQ$Hij|iTb4(pQGW9eo6i0bU;xM$5`?u2@yD0j0gg@h3b-qEDVx2eZ6TC9^P7uE zGngv{@EA=f;C4FX=>phcmDr&5t>x2eYgE|5@@xB8ixbU~kecR=EM$8n?g&^ZE{^{2ARzA9Yh||@@-XsQtag}h~JwpZ-6qcEAytK3G>Jzy1_9` zOPZ8)Mi6??_8Q3$w(1CBO}^ip(ezyLg22X&5z3Qwy87CbVc6}Q2=ta;1`FK!WwDvM z{ff!}LpO}0IkCT_XjASi8-#mwu9GH91ZN;^z$n~}|86t-8aO`5Tm ze30a^!h%3H>JGx1g^))no202ge5jzVKLFTAhkSsIrwZ&{Of$_$cl~4b6kL_0On3?yJDFw?Jdpa#0;55U5$=^?O^?I#qnu+vPF#-pcpxJzJ+ z#6g%VWz4=BU@%fx57(TF_Y2|4^Z55}Ou-b`hokFl!cvrSc_}(Vmlf__n2zB=-_!F# zlPbx{?dqq*_X#E8m7r)oy)Ds8mHPmrDYiiOM3N?hNn9i}YrW1(Smg^41veI@0+vOg zvz>hz7c4xc7o@wyL5Px* zL@-jKgY#JjItIw{LtJ!v1nW1{qfQ_WXgr@kKFl52yJGjnC;ZN_;3Q6rFdRleUv*&R zLMRh@TN`A&ERcRVG$%?q2qg`;b3Pk$*$KYcjU_y8j9EQK4qgDefTpIDJNMX$Dy8WL z2PAQnlNN7g6>LVM11j(PQq;SY%E&0EH$m7JJRcr&9{&)tqFJPW5aD_iMo^W5lz z&EpDT7(T?hpW9wl(N<=jaw8=L-&{!YyEVK!(pcMrx@z`}6YX)GAX4^aNuC5_MB`tj zMO>)Hb7IWhNA8PBtAjc%T|&&Y#dOz`&|t<`@;KUmoVEhjVaMy&=Xy8@!-e3xdm=M+ zn4d$-{if-ZO`C$aU2SsBzBC26Hv>tZDh>J-B~SptCL^3nZr4ds#~FkY*}Chn5pk}z z1W|-y^NgOQ?P^y=tUG)00gnKrw^gMV z*Tf|V*YaaU3HwQ{vbw{w3&_fpbWft7Ovf=?8$QUmmUNvp@BPHgz4sInb{nr;cvp3D zosOr|0b$J70x{)?psNIhS+$@bx-j_UKr5bEkx<>*vd}lReg_n$31?jZYe_Z$Q3y`% zLGX$hHt)SxJm9KKhOycgo8@&TAJSL`cbOY0h&-bOX5ah}Ld_qr>(+JF78tT^aQDrD z-f?3Y5jLUw{~0FcxHjFZYdV^>HG?Jo6qO+WAwCX+*v6F>m6F?*8S{8xum!V9?( zRKsN{|B_v8t6)lagsnjiwZxVNk?BC|B4!06qO2((i;S&0($rxHL|odMHC#B`Tl>Xt zlFWzRtP@Q+#k@w<(xp*B+SAB8YXuBJA-F%LSz#7!h&wUcOawL;5YBBD!SY2zB*b#UUre`3?*PaySQCk&VBZ0^pL{DH{dPUC_RM%^gw3w%xeF3ZJEr%;W zomXPg#WO>0;|X~%FqrjaG#KBnh!S%1}lS##us6;2Kc(!VIbv; znrixvSY^6WyXV_x;S@7a8$=?$EojI%Ax3%9H&G3E=p0iENBx%COrKzF z)SKzdBbv5(bOYO2o)ZyJLS`vrnTBvOUkHRT2;Gh8_l4S-X;PA`Ldd+b~{UA(c z0`ozwCe`3>K_ZyEG=jl)aS&#Z`&mw2WwyzkdYhu{ej=gAXbq+DI=_t*P(&VQoOLAVhhMOm1YIZuMi08L(p(uTok= zCjV~m76j+!AZf^@oQ5D7*zan*25r<{e*Ht=(9A2nnRDsE3wi#kj9Ict0 z+YO<_Nl{vmFb5SZk~vt4%7B)aU*kf|c7Vwl$R>?yv2KwV9~hw%U+eseN5G6y;sl3~ z6ZCwLp&mjFKZrf#3{ND49tB~IOEqs~tU_r7gGc-wU(hF6-=mQXY~dZ%;Gh9?%5qd& zpI=h%2}1%AJZ5O(WEwNztq#VW6Mw4i&`M?6F?1Puca6WC@M4JnMsqpBn7{4^b?Nn)6C(zt z!kd@ijADPTGjjSnWZ~l1U2S$X!OL-ao1w~72iPhWu^v0R=e01qOy}rkSC?^ce^v~0 z3{AzFX-|M}+Nk`PLwv*dmCe;37ymO3^#4HB&Bk#h&KWcrWL=S?a_vH&f2Vi}%tC83 zp%JUdHb~>8U~~h^d@~MrT172xY$Ax7Jq(z76*b)Eg|g z8a-w0({U~d0HI3pY;q4^{*uK^pP((vjX=k!Jz{$z4SpACYCkwa@NOKt3q$W}LpGKR zgPUQc4&(kI=1QGi>0N+AMGXMzP4g><$FO;x@G%eX7hUka&syq-b(W9!k8kJvjQ z8t6)FiERDyG4{vVOr0hfB3%s@D!k9gUXZ`-ct7@5$K}f=e}3y0S~c8yw0i87qafuZ ztlIKr>6J-q_g-4eWHFiPc;sU;_+p&H)OFjs)cbyge{gmeiie4F3|kp-MhuNdk3v_ z;_L<<99I%8fXU#G;}M0{);eOZ^O;B5YJzi>9zQwy>|hGRQPEsU?mc>RpAZtc!oM%q z==nyE%jMw$#ZvKMHwEsXDTynWtK}%<*YGWft{x!gK7bQl&__Id75&MuKf?mXDBwL$ zJ$ZHfxmw<>xP&gFgPpsFDhUgZD#~eJq7ie95z)Mx9vLqd2fRv~7pe@42iHn9k6$(p zK~XfHJonz!d_S*Z%fpQxp0$WuPaxsU5?Vbp9M_G2oElu7)N-9f!qt*fj1w>y%U0LqB~ z-uQS)7$81<*X4SE3l33$cKWxcCLa&@&9nejY|#3sj@+TamezfO^19jpui7 zk$Z(&=Vp@VU3Uc2NscdvT9ywOJ$Em7CM@e)JRNH*ES7N#nAF{7+2*)*{`yzUeqhSy zKLl0v(ONW8OgB5Ai=+^|P{)-r@W96b)vzXHMvvxSB7M9vJ-fQPq>l|6NDxiJ^o zccS`i@a28L0v^0)KZhY8lmOS5_sexTo_1Ooe8lT=dGWI9HMS0~9|!zy%`aq0STXRl z$GArJXlVA6%TGRVaJaYa+c`k;{n{TJII3r`8M|T6$uS+tR_E@>wYS+vNM*42g(LG_ zyEr}`klQ(e8ar>GinC#f_o{wL+QShdW3;2~525joCR>?ZjM(}aFEG8ynR&VriP~}~ zcjn4a1UOj_K#DkyM2Q!HldUCPq*!VxjGm_)Fy;&;x*`~TelY1uP~$wPwHIm0s`Dou zGd{$@5wEdxdi7c03kYWedKhpyn6RJ+>(+uU0(QV)2eKEDh-ECknOI%gK2i9$JP`mD zL&#XPqJGOW!@kDXDQtVrVKs4e*gFVTyu!qG)Px=DsTN~Q5Js<%n~k&b+gK>n4e~0l zr~#AW6*_$bO<=m)L6`UVSu>t@L+3Qhn+P4F4J_s=oW5*^AO`nC(iAX5(I-!Ht zSqeDVP)ub<=2}in6&^U`sr4UefNyT4fW;v0Z9is)4H@Rm##Ol?a1!u(RVAb%l0&5` zfoD-@jS?$A*jf`TiweM~LbPl738L29Z%}@McStBdR*W@u#osP$If9#mBZh5&wLarl zcz@(((7wbk8MBg{80)=4W_7D?7G;-hmPcve_2(o!i*IN@oQ5fF#@5WnwHYv{6PVMp ztg8D^rg9?&31K)@X*+GEfPwSDW9-f402C?K%x@#KUB4ykbm+_i`DK2J5=CxUSankf zG)8R^J}IZCsu&vA;jkcZa*_E}(A^tDyWLg%#L$l&lZyE}x1O0`LIRTC(&fP$jPGGi&Y~jc7L4Ssuj*A3D;wm4aRmuW3|39j;iI7X=PX*_&r92O>=c2}NxmBJHjzrgPgi z_EYNI-Xw<2tG75Wob)UgL30+VR-K;7!|~5Hpubs^_ZwE9(Um>Gt)>D#-h*CttZudi z1Oyl}d5Lo0gHN^@Z(;Fg#=WEDMKb zbsAd*S~Xe)pkkOL1i70~QSpJ$4G@}&Nq|3BXtB64!XwShfkidgoiG{K1P|mT?ntbe zXpE^rU{}4wOv!IVttW$>F^M%`GkQ&WP&-?FhwM<^plZ5{L7#C?W5VOe2*2@Bt+Z`aH0f*UIkAWG3 z(E3F_BiMqh)(Kq`ffIR6#k|6NsmJ|p`K^;*wYlprjjhByZ6aWwiS3TGaDD8+-cQUm zPnzo|J3Q?CrrE2Qd9v!b6xujTBHxEQs5h zLamwHYFF+QQ*a@${ptFSKRsgmlQM+0=1CBK1Pgl&?7EC5p2$m=tKFtDYzq=bDnw*j zdjr-B;@mzY_p7|`3n9R`RFohe#OCW=S@t2d#BksC3}pUWa*EJ|l&stsI8n@0&jf0b zdN3DZi(@^jyLs$+DEw0CMves&(~Iz!2S~c3kd(fRz_}6B_5_$QJ~E3P8rD?<_NzJ@ zt=vRBHGdX9559Tw4U8kP16a?5?pZnkf3ezR?y-$I125Q-`p|jfgWCA&$$`papRY3y zZE}mHPkwQ~VSMIoFEc>r)5_G|ZjT@e&)bQsZ+hGJHL$Afsv3L4eOrNrHop>d+Y)KH z<;)7YhLnOWbbx~>rHwPE@YFao6#Tf9Rpr-6PJzxFL+C+`fdjsRKrTDP2A5-*@Y5Gu zt800#HF#bY(X+AMT)cBRczY8}WhBrSzv`00Qtd{#weJC`kDW7j3~Yvxd}+(mm%o8F zZj3Bd!abD(6mO6Qc%O{EJr7oYvNh{pXBy2n4ln-cvl6|v5ezRi>DmhuLCB;C7KtJ3Uhw1>y$Mu`@OHAW)Eeyv@#(GzsYp zP1^d;Jp61>7IoPDzh>IQ;(8uVm_e;~E$`0|9+pR|M)wx$WaIQG(_>zI+UYH!yn?4t z0EUxcLlw;s4wV~{x*y@%#6Q6qXMRnSoH@2H@0x~Jco9wEAhiSsr9eMkpBZk(f?i>OYc*FsO!@WqPM-bBc**+a|K)YXNBci1YrQM zC~orOpx0(&Rt`8(B7UXtCA2F&KtGea?_VD#XXU@rt^{Z~d#CaV;piu2PEVVlDRCeG zCC0N`W7y3Qst!`RPgXMhxis`=-pe)c0Jju>+yb6TnLi^Qz5Z?U_(kA+{WDwR+gkeC zzR0NB-QpU?`hUH-JF@ISMhJjJ6WLe`Q1mBlPENqS*V*=M;~)&=>FOwP(tzp)SoXag zEOg)y(B1R*)ra+=2|RrZJ}GqHJkm;R=d9WQU>E??JdCf3%V=}c0wr5g}I|3IxsTER8vO`U(w_u@VEEj6h#f^5?;zOzo)f2x8L^f;fXZ?7ymo zqNRYFWKibkf9k7uhC9;C>dBNVVHheTjY~=9v?WAVcaf{TzwK%>W_9hCqBHb>kltc7 z!tB})fV97b_FWWEmhR*6hC71MItB%Mn(`aM#7a;bQR z5792S-=u!xnpes16~oa$)^AqDj_XW%4%&PKpiF16hWS3fz!psQwRW-31BA6>12kJC2uO8l1- zi`xr%U&`A*=8NC-Gd*bn(mWakH8r^YUhUESx-VPRZvxGBM>Y`(b0yHN`#}PN<;$&JM{M+Tm#Dd1@NU@)i=|>EE@~=G_t_=S% z)PGyPs5XK_Wk2skAc#+BA~khwCOkF1f5sIq4|#cK$}}uW!bZ^Hvk>W^bi5?}ajn@b z?a3`p%($Ic(HOGT1K8^*YCNf#n2v8+xd3B((h}Q3HLL}EI1^4MDHn3z_&a!beFoZQ z_JtauELpx)SK_I^-a5GI%}Q=kV=Nb$v&145vU2VoRV}v6sVR1^pa~x6n9o9>BD`0D;>rzqPW`nC6@5^bziRR z^KAh`iSf>?i8y#2Gk0qu()oMm*$@8~mzLE#X*iJe+vEw zNXs2%ZO&N$T7}2}JTJRvPDKNXdGQOEfVuwwg$kr|_ZRG_!6PIc-UU?tKEOC=H$RWG zc7My@DeYxoye~UP>io!&$t$#*0EWnP9B)=fQ-B`Xy*um!AF!!`9@*HPM6*N7vXkZi zUb6m|J*WQ?S~Q+mRMz%g*?LWjhXcuUANJ;b4RYr0Ori2)5j&IxyIs`-U$C^^GZrBg zNGFW8Kjq`md+|24^2w;7;QXv9(t>-&~-R{%!@WVXp{)1M#3yl}xn=^6`HdHRVTt zhj{a$;&(>@0xfgbMWqfb(8L5#9rg1_2%hoY!*EdT$4_cgSOR?J!ejD@qk1ol4obV7 z635iG8J%m}wNGfQV!ePdUm8UGLbBQ%07GYunY0uMH$LMm>x7qPcRo#~@-Cq6o+jn~ zQ3n#~X?p(vXShezX=c`eEOb;iI$ZD_A$!>g5F$+Of~BpTgzZy+rRg;U*Uq!Z#;hvR zZD?fgalL;U?FEs475M$Zqb)oG>*Z=teXH{!wN>l(k=NJWT~Pd!BT=Uo5M=!Q)@ijA z`oCHM`x%aWQrGhk5hwH~@B5!=JMKI1>!B+lpR3&vv!f}9jUf~tDHHVXM??6ejO3Vr z88E{wNxv$UCkd46@48s+)4JH~Jx?oHZgP?e@ajT-7x*8R{9PB6>h&3K%=Jj%ga^71 zhXh!HH`~uiXb~F7ca5QZVmKIAYJIB|I_nIZbA`XhrC9(g%sJrKMAJkDk|YQn&9$=y z4!Vm4o_r%s^!KHR>NT0O_uev&>)Tt@&?(r4b+bDG|Bc=zS|SG551fUXON=IS5bDfE zz|Vn-f8tT(4nc(Sw`)AGPbi_JYZq3T^S6od-k8GcP{3~gMGUq{>%%tM1q-}8ABh!}URcp%^yjhGh2ilaZh)HR{jo~m!x zY886wBBZVv2pd!gOIy>Z*WuJHhh{mK+_lDSKv%@&%tdy~=Ofs>=W`A=o0hDT-gjS> zxvUH`R@QgHjW14ba4`CKbmte>o4!)Eu1|a` z1v+iNmVFj3B>1@bV|{=QQ}ZVN-}pwFvp^;l!>gUoJ1zICwke=VG&aRS=y>-(v5FQM zXfA*V92x`D`ySkU2ko^wUkeHkxxP?QzxF87#-?@a^ee@$%!AS&il-WD>*p5#SL6L~ zlOl@~V0ziPvQRx6x5@)HnPPj~4oFw`+xKtl=_^8SPF{W%wtrWfQyn$YQ+apeHj!v1 za6O%)d^5S#8QeeNwq3xlQduU{pnm#?V>dkU@cFkJ>R!#wX0nZsbRh>J3+UO6iORm` z5Z0a$lcWf#;82kt#@a_Vp=E2HU|ImGfwJDI%gjuG0$syBKTKPUsAaDBfMC>gJqljW zbMutcGP(-l+I^jA;0DD!HD)@M7j_ngF+&XRznY7Z<4z#>z@gJorxuDx$FP$Dr7g#5 zn*H8+AqBBQ<1a-fFQl&C1X_E?JWz;gh)7YwdIr2W@Sci`8g21gwQDM`oBxS;jE#1i z`n^xoGVnJO$n5r0i*6F%r}f=mO{?SNNAYkso8sYKsimm~1h%QB4XYj>0gB-9;+DKU z5y6}5THzEgH4}rTCO)QZ8!h}cp`|x=agSU{pcTvdRQ1hf{}h?4 zn)WM1izVY&-Cm;lUZKKICeEy)Ykmb;$*hmIV$#jb@ePPhimN8<{QPf*haSU#tT_Cw zy~;mu+~+3bWOd`5S;|N2McGqU<>t+lmpsv|OHD^^HdfX9q;`=SWB*It(aMS%T$aG>uhm_=0)h+8RqhgNW;OwUQH2g7k9c zp`FwOwW$DZPh_D?tV*7*n$q-MRGIY}L(g5#dU)+{fKxk5+^krPIiWqUqGPJ%K%=E^ z!W(MN#(dtMzeGpzpbG-C0A^NMkgPH&**wrYt&DKQc|l2+mE^qB0nvM4r;y{JR2=o zU^a=O+t{B%6`8pR`dg}?CX|DhdYGD3uBk=SZP%BkrTuU^o)HA0394DY`HKa2BHUoM z)SuzdDeHVTbhbA?hzg;)xTf$}xID5ReqC{IPlR&bGGSkT@8wBusI;l2sR z6_a2;+tpAXEl_uuj`eSTm9BL08wIL9y8|4csuu>&seW@l-g^1VxSDdKjH~M&Nb~V; z`PyQF3s>*PT6%2MB;Cz3c$}T8AeyV&cXzfsu{_>JzpCGJ%KeWjhMizug=N)xWPpx| zSo~V=9f+T?!`&0$AWJR)R2hmJ`hNNjYh;y6GEG=pHn9hINI0|9$wEv^TI2G*;yTOy zqS2(QSYlQEhj2fm<($f$T-n9@GH_HfhkCfW^O(dv>>N{K%a@U_F zA}_no#KDSO?+?C4X;n|6Z3kcaF1VO}I*+_~DSH0Cmh6!~e+gLU$M&i&6%D74Qy&|j zGqFmMwft)cnpC zPfeOH56;`#r@^+mhN^SpXHb1*)Fh*NQ%K#d5{&kPm<-JsrzKOGv8S!@&*;|&6C$5iwCRbK8--x#a zcl$ld37Gq$b2TM`uWN%`BA`6FTn{wzHnk>ZN^GUJzvnHPoUo@%3;5^&t(dFZ7Yi3H zRbgk}idVD(4xc2FrmK;SH;hOpwp{T5X_2QyuI)#BYl@4fxs_A9HePmC!)}QwI}bLl$5$G{ zL$i&aG5imT`1yeI3x}~_67*4hg`=TFYtztN7a3F1|30Am;q~~-NAkFd+D(ap08cR6g^j(`RL^B(ePX z!j9${bl*LJr7bnr00ldMsJp%WtYKWit=?@)6^eJaFM5!_@WrqGyVq_y4WWj+c>?lN zZQ5ze8=bOEpNP7#eXo@gdeD_SL3d44Bfz~trmnie`mkipenF^Td#HerU-k`QS8A0` z<%*qt@Yf6t=x>x1Vy6_;Tw}*UAC+biTfS}PJ1TVdXYk87R}~*NO*__Ak0r-}yGu!9r*IpdYd>q*8r`j5+Y@Vv-L5dinRpmpLv1}%Z+spWW@0xm-`|HRHqkgk zT=9V|6_vo+A2uJ=ESFo}aL*jkOnX;1&{+tfTz+@2#t8_=H{@JUZ@taN26KY53{BmC zYI11dj}H#^QkNHRq(p3jT=Qbij_1m2K;2h5t-Y(|*3BI%u2r2rfy?DVywYn{i|av) zjp1(RBHz1CY&i!GeMu0S`P``^D0P43_hS&o1x?KCH7VjP&F^07dgwCvScHy3cOuZ1 zu{i5PEPEu}RqvcpoGo|Tg?K1618`L_6Zbiej^V%n-{BR-h_J)~!HmqpjBiaoc489& zLjgLyy_GO;O~qJK?a{u0{CUXSrt&p2?wOzu+6h_ z?26w%R6_WgFz+uAai9x;?YNTnEo#hWsJ|qe4VNq2^O!0iHmf8mM)*PPxLVEzl5-1c z{}eTv4Decv7V{w;vyxQuPBHMULIqHCWkxrodfdWDN?i>To-Jg=OFy&Mv|fxlWIVi^ z=Lz7ymsXn$6)S0Fwtep@g`D%RBml?&HD%O#8kg)^FfT$p$*FspX?(!*vorx_mi8*@> zo-l|UE*EyJZ6Ey}VLx$-i^J#qa}!i3SM(aUz9o5QWVYVtA4wk%sG922JqBv%gn2)y zg*Qx9=Rdd=>0|H~=5hg6sGSp&Sg8C);mgrfQB-2}y*=H&e;Q|b2YKaB^x2nQRv%Gt zvy{{}?hc%tvoZJjLX?$yU0~^U*5NuuK(jzALPje!ADsOikG$sr4o$Uus6DSM1Uq*a zk(+P*XFswb*<3sE>$n?kD8A|ldZw54=+>Tw9e=DYdO)&TzPqSYL;s@IFM!vq&2ROO z#4a^OD`9)a;&>mC&NW`ZepK*RY2Rqk8hBLjAlEo+>iw)F>DUWppoSI#Y)lX96d-81 z-QdgFw5PsTI_AAS%Gb)(G0_ReJ6}Wohx1Med@0n8EabX zE4?9%UC#)OIS06PJgRUB2nSR74}42owY~{7{vmotkqrxSc^^@ay;*)y``PyZJHr$9 zcI*`*VZJS^=1FN+me!SDJMraLDC4vcyDpab;I#)erX=psgLK8G*m{dhPHq zHBcUtZfbusicYVWCpjgm^t_(Krmz$R2!^ zi!(^MM2Ye@LG{n8Ts`zfRIf~3$3)199qWwoM zIDN>;JR9w`$y4x2nbrkv!uhZ9J>Ta%|M|Re)o|3RwJ4~-=glIU5@Qn{Loq{tem^F* z_*b>=g#8{pPp??4F3@EqD6U8NC9bWcdvk0yq{!rj* zqn4Hir;7xLeD z?9YJlgPr;B4E~c{{14pi=idJhl>L)|{ttHX4mtfFZk(T#_7DE^zcbNaeDx2CkaMR~ z;eV#ynW1|>>G(n4cWlP>$Bc%J=cKZxCKc54{-^_l1!TB;+E4zKmPk)J?<3c&pnqD} zUpE$v_IUmE`C-{FKl&hlgv`Hg{QnR7e^p1+nAJ`t#N6|4y+aW3KhFl5tPLDb(VmaC zNS&ves*Nt<@-0gJulZC+eDn1&Q>1F$F>48uX)2_NO>s6c)^PLk467BxgDxP~+6I`h zr<(k{%aHcasP~gtkZWCP{(}=x($=-U(3NHp<5ZQYq1VV^8;tVB-* z%IGj{zL?TN6)ucVORz}G%vK+rx$5j&yH(Dv;J<1fOzpXMO)7|}-%_k2l^CP*;{8mh z{4W-x-oj#1&$!B&n&#RVa!7Z?Yk6AXuABte*QBCR$p&UVakFx;C}aNfy*r-_<7bv` zDb|NaJRy2W8mTNDd2L@i><@NM@^bXP(fL5E)#S45k*SKTiiTtx;{v^|;v^CAVdv3S zn>Q!mKO)Pja!Cpp6~!meHfMbtA{8xBbm)QuG2F%;o=S6)qM5^pwmqYvis)ExGo}5x zv8F`O8_rgSi?2<94{{|KUaf5d;avviak{Xzjk)*|-_fPFrx)kVVr75B>UtNiK(@G#(OxV1N?q?9 zRXXl`*}_ol@{KjG3g|hPfMnYXVrgb;mG60F9d2v?I;&ZY`6czkf~768I!M45KE3LE z#-w&jJ3qxN!T55-`KFN%9do+HW3N%p3uO)pb_s44ceE@mq@=A0<=&fY9W`{h#g1=A z4@NsGBN_v*TmT<9;!;}|JYKFdJ`H$M+V3ZyV-JsMUngW+=dBb~X19zF zMcQ9k;v;QWALz6!$K1L0s+@jPG~)Q3_6$^Xy3S+#6<>s1?e(ykrQ+QEO?EWPDEvmt ztEH1>jWVasi@%ij^Q-{5u9bB0s^^(heN8i8{{8G*%e@Z4_-Ff8+cV~l-16gYT7JH7 zcHhhRlFqN?3!0;v(3$ni?YETTY3pZItunQmVGxh(d!?2c(Ei8aa(nPbuTbK+{uR+V z&mQ-PdL3-2qdMwF`u0#jl1X>#)yuiX4ptSd#8+Ls;ma{cFs$nVyjYx>7=)D5Xcgz8 z)jjllD2Z4Y42kF%a>lwe5i!vO6jMPT#=L*lgbW#!xNNE;ocdVHtTDqII8rM0XvINfk@YGsX9RdnIF+^+&qhfP$Tu*SkZ>DBk4$ zzQp~8uJz42XL8B-Xi)1d^Y`_ERLcUl1Z1LHjkRRS0RHO8if&e4+(4P!(RBOJ#c5Nq z8GXYS#y=3CPV=WJhonG}?L26b(4pC85s1Nfjli<5s2xV9_YI-9`*Z8Hh!=cVi|4qG zf#RxYl`i#yoG6i2D-XGG>~2@T0?C0RNBIpPXI+;ha9+o)wD`rWa8ZRWTe)!(>QDy*1>lhH!~>F!vHwaAC^dms+jPd-A#< zF!j0Gb3$ui%#m^46u+EVp6FX)_3v7H&%Sw5KXF(}5_gie%F-JieEdjhVHN%Q=o9a_ znbt|C2rWDH!s(^ogO5!}xZn&^47B7VnOo5|Hr@?B5HFZ{)8gsMYQUJfVoxqYRaNghLjb%%xV?;9|gj&NJM&9+~AaW`C7^IzpZ z&^Uor+1FX1eb=tj{btPXO}}Spy*)embtzY&_n3@b3#`-cQGPSbDOtqpm%_VRcg16U z3fnS<67s#W)z+de%e{sQ9BOzq>|7LaLG70A4=}R51$?s>pnyi<+lIe4j*?)c7i(Vi zw5EuIZUQI{lZqP1#Wy+C3i+3-QcKZp-xxWyRsAB_8poCrc&DbiCOFeG&ud+EjdAQo zG5jHV#`g|Ve(z~FpQ9A1AEN`BEg1CHpsOde_Gdwn3y{p#9!E>cOYBllFE+JU) zJwK7ziT6j|6g+scb-%sbNM0lX`eCxz=;RSSPe?`BLUJ9b`F4qj+jh0Kx{Zv{i=rVT zRC=AnZRP!eyf)bU*Y6sLS$PL(tMPO7gQ@nTNp2qP0;CR?015t>_VML3EVEN)OQZ}I~!Z>t}5Ig4F(3-ErvKO=O08U@`S zsKNht5Lbq{lhAY4VT>bnC4Qtcb;&3d{}Z9{1#ZC0Ee);71 zgYt^Yzu<5yJ(}Cn+%a~8+%^{nt?*vKGOs}rFO~9shgJ5cs{4+<;e9DL*!f2Forc)} z%%nu~0+3PqqF#B+=6Nigg514O2oVyQSb}=Wo1Lq4e=uTbQWjWw{c6riHd@IzL8`9C z#Q0?_e&vPt{K9>Y7N%2c*9C`>J4&oYkN1a6ooY;w8g;}BUk6j8=UbIDjrvxGiWr!u zFm6Z(o6F}Rs5P59U*3mX39HTV=o*Zn=kg+ZJqwD;=d+A#reFSU+>}q{S z+%L3d%p6N{_IcUaMw2tU2kQJBlC0AuF=g0J%2{`muMA2e1LP3LU_XpV=eao zYVW(Fno8Hbb(jM-Kt&X#si>$Ri1e=Dh#*A;5_%H}J=9PFf+CsBqAwvzKv~WzHiq0?po*EyUtqotd&1^-o5v;pZ9s6`umAyHVn{i zpH4ePbS3s`j?BzmgMtwVWWKA%a(-N9Rq2D)+Qf|at>({O?d&>wDVTUD0+}-{xzWu~ zlk9#o)!qp>Gh6ZwGA~HlcgaX+%hZ17ymY-ody(zD^pdLF{zYq?I&)%fHwpe`XxtpF zCD2#j1Pnxsc8^Gv^cAaSI64vyU^TK%vGaDylT*g$gvTTkU>LYi+(@M>hPzrY@>V8^ z67EpJxK?Gxi1#=t#UG06piM>F0eU^uvmCBR*1mL4;6_BekJr)=GBS9>(JKu5k@a@% z<(2k68PjHa|7-E~mA&y&k(;Za3MJ2)oTmo7XO70Z90^vHy*bk4nGAcsr>iJ#{JepY zYACeItFpSFC5z~lm|(EkbFn4ZKyB}zRe4RZhRfau)juC>R;u6Fa(2}8RBk$g>e{6i z5q*r47XysN|0d0%e(qGYlqj|;ug_+dZE7%H)tk&3-X3x#da;Kz*aa7&PIEah#;d;? z6A3f5xWvjAN}a@|JY{WAhC=J?n;6aDU*97<&sdj2%+~yrD+Wx;)QN->`Vz?_q(T=H zmVbM~2x8*P*XM%!i-0haF|=U`USHl-l@3R!MkhTnsK@b$-%)7f zIsA@FP-nK0vYaLyQV{-l-Qdn!E4y?=I)~hINy4P7&{dF%(tE;)D)uq#4L?def-3Zk zyEMXn5T7|ub{DlETaI}~%d6wkyY8Usi4h0joJ*&ok+fr;^V^=W4It<7>lgIwyTGc$zWAl^c@ zajYfh4=-7;p;`Us`dYC2pCY>-0UYt_%3l*Ce}srXBEjN6j^nRw9sg?=+Jov&&v*Iu z!-!q@lH&c6&XH(P$p)q+N~?f)+hgfR8saalQU64c|94rx|Kw1%)1!YRWd9At%V=e? zvqE*J*K9T**m_o#hYxP+4N@9A)jt@LV=)`ZqH}Y$!~6r)y0D_b1E*6O=VAf08;Q0< zJQF7ZSmL3_k)m3ke}iv$kUWX}?I+MQH8FK$LNXA>S3lkpl@bZDCXXy+X^uY&WJPy} zEXt2n&%K(fCKP1Dye~}d8IYF5qaGd^yC&t8i*os8<_roxeiJRVxu*h$bY9%Ah2`9s z^oXtSB&fxl7KTG<6mgGgKU6#{QMx(}e+toDGp7nhmUEegG!}6# zu(d^KMvEdywuVb_oaU!Gsy(q!MGpgmyk|mm{6~xZ z@M{$^_`}nbXa01wj=~4}@GQZ-Ms@?!Tr1a)_duxq)_nh%^00!Y3qm2JLzMVgd8l+q zNRGPyYY*J_-1%z4*Y) zY&sjq^SNu4lH|(O_oX%R zd)`x_rY1^Z6=Ww$O2_UQT>d0=0w^WJn3bez;nwJCy}B6+$L>yGm&_>@TX?S z_qe%mEG)X5a-On@<{;|yxwUXLZrYb-!lsl?1?1sj!EFhqWCc|enhf+ixNC5l?Cmmt zDrLg)E)vXpkIy8#dW=0ci0;#dUuvJZ{+XvSt?b_6Cz?5E%w5aoixq6GXWb*T(aJ30 zfv~npDFYC%=yBv=Tk<3lfjvySu&Ro>gM;)rT{g=KAxmHmir(pq;TjT(ew`}251EXZ z9VG$iU#4g&hrqJT8}`9=a8ePgj9mVbz~vDv&J~TOQZhI!`hfs1YSK!yXW|(grnYeR zmwZDEYu&xmnpc3eZ}5EQk>~iN0+MV{k!Z5Qt>$iE1lQ`6BA0wg`ze20ymCcOp}}C% zlPBi2vIe9nD~nK=KhcS|Y;?sBstV6~a*p`vl>u?qewYR%QtIaN+#3!PcYf`N{GM~? zmMU$XiNPuESpuN8iljenu=jUGYNEZUs1&?O`BK(#$pTHnx%OzbkoV=qGlf>(N~XSG z`JOV5M!h;s7s%4(eua?VTewZds?!x1yz$y_Ff{8GWXepwMIi{EPxV8T+6nJ#h047} zokBwyh@=v=`l4>u!*eRg^^*;gyyCX9m!ghQmdb6|Yf^F9+!N6cVV1PvHoudp{n|qIg<^%P0VE@+3+W6$ zv1<}BoX;+@5m9zF5G3Oqm6cx+VsOeY{E6FIEr2(c5NQiEjL&qR?kmks%zQq89FsLe zYCxhOxA3z5Uj^Nl61(XwGJu{lnzyN@g=4rCV(&`oYec#Q+v7&jOu)<`y4Ieo3Sa)vxOIK(tl#Ly z!vF;wB) z%-vxwaF3#0RLOuxyK?zNLR2|YGc{hFt=T!?ev(IGro@iGu(>eCM z#r+lmFFagdJ+$TEi|5y8V}|lvq4WO8&5Asd>vM$8v-Kix2l41=ImW)8`+>%>9zZx@ z{D^iAER9kdj&Wcn1U0-hVtjC`5;wALw>L7gsbz}c&zv6;DevtknkpjaUTtK`=#pax zLJPGg$oPULXQE9&dE^4Tn+pDxz0DqqCglRj2Q1Fa49HnsC3>Hv~iSvDejJ*@1Ic5zYACIUrlld-0wG!h#U9l9Qt%_y+u%NsAw;Sp>A-=X_<0Xjit2WRa92eheKdi z02BP7WGD?6kVNGW^?dDWX@Jo0*W_slYIE{TFeNDzy}-11SpOr|SfwEn)$@8nQm-+5 zzb7a_elNCrdH0D1Ta>tqDt@UfP&<2_!8GqN3@ftb6$sknJPi6YH~0bJW}^_BR_~Sv zH^rXPSTj$_SJhN*bZyydRZqe$jyuT)k+0dX`>SF?AL-;CHGe-QgbbeQz%JfL)v5$w zY>IDtmMS}US!1z9De!Ik+ZipJ3Jc_M&crnPUleL%G?eh%G4869C)BQLM>PAp?Nyvj zvqAcvjAJs4Fk&C_&zlL2(mGo^ylqVJy}$r-{+J6dFkeV9u;~I(-@o9W2Z>cvC}h|+#iw)0$^wKIidY}WpoL_Rl1 zRC`V>5^mBCp~ameUgQ4&t0^h5TU`CM;V~~AQxCCY#y6SIN<~*w^7z9Z2on4 z0_q#+-ii{tA-$5}Y}m&lSu(OWrl$qhjS{v+FYdL%f|0hn5oE!vKwK7Y0BRP_s+`-( zK`IfiltabXAY%g)ujO$}pwi+lbp0*$7)Q(TQYSuagaS6HMZ0c7s5fz2uqLOJpQcBQ zNZ(aIQ>Yah!|GJ;m#qMi-&o^fFIPj#n7M<1`C|LX1ZN<+S+$2PEZK3RF1)rWb?S)5 z^fvfNFj|h;Ycm*4#C{x0U2%89V3(!f)i-VjpM6) zlY>zA)nxHv&GNa9m73P0Dy|X2S!k!|8={C}yIQVHv2t8S9-|q|duoKZbf9)fJMEap zYpwD~1I6;iD@yz4vm;u?;(2RAQVT1Z&fi>J=2FMSFv|r?P4(PNc^?bsTGHc`9{?68 zEQ6Grn$;>sI9o?NF`uyH6?3Jh1E{jLtyzf8^m`>!&Yf>fof7Z^A5|Yd-*rw!!-<-| z#^IS+{CgmFRkde4?MG1>FM3M@WUUn&yKvveKpL3;VKxPC^-3*FmyZOu@_Re0jQ=7O zySN*l;1zEF9=t_s|lqKA*Z> zf}U_0HSv!q@@r#PNmq91^G)JqBoqZ{mbJAwexyMFU6kd1XR6P`Es1}S^N~@I-i(8S z`phgg$9Lkj;gI7N`$h0gY+7vQNR7oB`VL&qy!mt9(&tW{p83^CIkgO0ngWa_AMrAI z22x`#+VQmdWJwJZC!_PWZpMzTl+e&4Bve1qXhyb9O3qjVs1n_Y-!T9PZk0_ZCx_(Cwr7Q4E~{Yz?Lh35ri=9Vf=D&R`Z zq=@jaFtR!|0Skvp+Kb74{iG-41(hKLG$fd3^pC{r>_qVINAOhk`3B3gjd}Z@g?1sL zn@h!o?PV;;#z>GGo!-=PF0Oc#gaXOiug1To2=0o|3cKW2lXK5V#&o&Y^cZ>YscsvO zMHt@xTq)XN!1;TvS^O$cYraU)@|9Abj*;MmIE0EMvk7D>j*u;tRwe{uU~=bW{ECBk zVDYv}4Y$0X#ZGF3Ft3AW z1`x#-B8HbPd8p5)3zxR*gD&dZzTdxP+i&a~&_oQZm!77&I+DywpBHBp+fw1Ga-}wI zkol4$;3sUB?atMu!J@)9VF=pAin+9*tEAcq=BxUr9B9CC2ra8pK^KaI5Ww8}@VRN0 zH_xDJO3Upb!(dyvq5DstxwwlbL_OXEi@!_XC9fD8*%#oD8|}BQ1o&iClvP$o>O>PB zy0=A-KxS@;A?5hkiX((k1Wgx$dcbhp%Z`*^73-IAQy`mHiBvTEdMS-Dv?qVp|513R z#Jbgp9H9&Htn1ZH+wcoxtA&^^U?1k%4~|Og8!hvLCz4v4wYyTggDI|E!uBZQX0K`m zocU{a?>jHZT-K+p`TD4cy2Q0LE)S%E4nar|>vAXB0DSRTlbY^?3wgb1^@EFHLfOr1 zqgIEoLN!a`Q*Y|aYE}E8%Nw0-ftM5l%HIIlrEw$=B*=ZT+!M};5;4#{hFc!Tw(j$Y z?5yu!ON zQh5~E`JK6$e7s@7tz{SM$aU4Vnv!q5)xC>Yn-`51%qkxjRnEGpA|oBjZB(2Et++_- z7O@Gnm4{3?>JzHgYUZNVF6Czx%HGMbDUcD6x%6>Y+H1R+>ydX%L_SYE5|?(odtu@= z&Au3Xy;!9i2sk|J`t>heIik=^{hMGb||#P<%Xc?ufmx82ZVFCH>x$R)In}m4N0Er%*3Cg=o{uJd8=1k-!Xxb zpD%R?QI*#==9f&cK`IUC=1ue&J+CL6J3XmG^T713pRD??mZ8}gtZtM#nidaDX;-O7>Wp_gL>=MINoiIWjp%uF@LW^9Zhov ztK0;>&3yFGF1SqqL)3iOREQY2^Y0M9%E|LibQ=uH$w)#<0Yl8gpR` zx=A*_T8pi8q#8KM#-l*)b+WjQR6hj4CqTQCU&<_-!J7s!Ur$V#olxqsYbg3uZ#G$eW3&ryM zl13>B@^orTS9g>3q3(FsTJ!7A*9t7iX&E<}!=~INjaN+EbJuW%R+Qd1_pKF8n`wPg zr<02k?$xWQ-l)eBiveduOYd`c{8nzYS**d^DN8?$sqN~sR1}K(_C4% zA5WW{wVp>xLkBd=6kbo-~fy2ndeN*XZT4}H#E|n%Gl&L{+=Y)O|lJuJq%*@oj_+ty4 z853#Mya}=~Anj%E9P&ZEv5cf`o?a6?679x4C_<$)#X56x?*mKM)#XO>9gaYj5@f+| zI-CYZquLb7u($^P^wd|CX08fkXT=YuqBGoM=<><|(=sKF#7fnSOILhf%0q4jpU=zm zc2>U8RJq`B1TQLWdoO5f@KI~HCvP%t&taW<_{cR0g2_uWGxd9c&=TR~c)A{~@Cevw zXq2ywr{^XAn@6J?21b=k*hiR4oD+Hclo;N#0x&=G=TqO`>{ePa(Q8)(%nwyWM~q#Z z?$JWMW^XbBQ+bgpiM@5SW^?uud3tpBIhi1*@`*?>3c9b z^(qft@)F^y@^SAPY0FRHNtThL%kePVe1#+2(fS((iF!kq>6@5>(ZJOS<+@ssFVuI& zE3w=~ad_PEJ#a|WbnlyH&1D5odKk%imv?pnpvKtg*C7wZ67ITV`l#_9FBL;2goQr3 z(lSS{s*cADCN;x&-T|4=vUy53(-Xs6LMwq*x1?u1mZ|d6f7g;Ef_S0fm|21n@I@CF z+M!f%Cj^z_GhNZq!MT$oq?^4|ARSjTu-q*-$A1RwQZ28cn-?06LsRxp!mp-c)qmcQ znOC%io*xaHpF%pTT01_T{w*K1iY&+iK zvi#^6bl|)Q_6>c$&vrN@fvtMS$>V@h9TSYIA8wIwZWS)TDY(8@0XAN z3S{yH5e&epselw1X4le}aR7G9Xk=62#NWTQ*moEe!!UN)$uDCswE_SR4XE|ecntD; z1v}qmjaW46H8G~H|FsPgfRp(zkt_Z!(9J)|+1O+F^CN(M>JLv#hX4YErJG=Y(cfoCf zWis1PV*t%rG(evN5L+eQkg5gP3LCI*R+eU3$}TF6ik!XObO5QPwe#fCHw^YlzOlB_hJDzEQY1cJb$8iv~?S4TJ+UgwX(W_%;Yn zPiY9p;yEDZTB*LhM{@(Ox#2O!@(5dkZg$P6(}T+A?@HG*`fspV+;1nGN(O~J&#*C1 z?WHg4L(KZH8f8;wzu;Ky=&Y33Iib0#vvqc}>RC!7W1I4?v4jCEhcBPclP0@`2TtEL z_y|68k69wHCC||rrX@3)#`!37IPqxR9Vx!!2ftBOZa*|Ycv1UmjqhVSfZ#>nz&5eT zV;kfI&CS4t^cnH@6m{Lh0##4#4)R2`c3Uy6k&15)oVol376}4wyg#&5GPYV0Mn!E@ z1&SR2r^FkHFFa&?h2`$xEc$6ynra)^cTP$O~E$wro zXG3QK?#)!+o3C!;KXbazPWrSyltr8KL+^B`tLZ;;N9o|V8!lW8U(S;brO>xFnzl9` zi*7B7Zk3C!l?Ub?;vDhmyx<+F>mrxx1pg(BE&d({=ITV;apUjQRWsYdK4Z}HckO@t z$3jXdxyw&>+EH-Q4VP*Qj+rv9QqON(Ez3OlXhSBf_&RUlH>sN2h41?Q#Q~1d|Bh|& zpJl&5TK#`yyKM`$`CJHqwWuKKXs-4Jwj0)ct^oG=!n;6@gFgYlkANwIoxHsM5xV&? z1-o!px$mG0DbN0!qX8qrUvT%##y0)K?<26WH&sgm-*risjJ+=v@}u zrE6bR3Yw-09PYPUB%tmw@mVai>u(Qxnm*y|F@}vt>YTdP0P@9?-}a3-07ZQ4I8Q|D zXx9O*MC$JS=HJZ&VBC!0M7+&X`rsi=6RC~2M(bCZJ;yx(0G2V0jLj;aAWCBl{FzTZnPTxpekTPkf*@R1AKYyixV@840b3MS#RuZCYY2>)vGwhcCF8Geq^ z6en=_-g0zUOGN4xx2+JA^1cKY&&f`nC0$4aobP5M7r6^#t1DyL(|S&bPrt_OFXkl69 zF1*gTO$G1fGPhM_Y;RuOW5^HXvyX|&z(+at?V~*2IkB%Fb>U6^x0lpOd)7yZiHL_9YoUyd|_*&D}+SuACyDg^- z`CahJZkoW8#W8^EEQpQp1 z?@$lB2b(sT(;JapKfQe%Uh-({Sck<=o-aZ)bMAByp357y zG8ye-*an#k9*0E@2K;>24?GsLV(Tw1M%fgmVnz7qt{}=n&j%0*i-du~ zYw+I%JYtjz9zJ_n(&UO|VuPvfh}q}o*Frcbdf44D%tVSC`8QN0XsZ;bJfRYC0E@L_ ze?R$b!p#aD-ljH13g$MG%pQSD1PE^ax%CqR*IA0YmVsp00*WMCK^O-`C6(Rzb~8!& z%ln889spsPSq}C3WSyrUcqCo=vgF}5jD_Ux{jy)sDtA1Gi2w8!@HNI(6}LW)Whnce z85QB9^V?d;p`ml9-WpLz?){3re1$m(<&<1Uyr1DW~$ggHCBagJd+ z5nuHPn5DkI|F{VEw=WOiZfb=G%6+@`z;TgXJ16>n@QwfXV^#i>F8rUi`u_y7=6{~H q^FK!vK!x!?DTIEt#kX6cFU~YykV}3s3^-;xbbm3tTB>FD;Qs&*wzU5M