diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index e41f719..ee13f02 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -1,5 +1,7 @@ name: Build and Push Docker +permissions: read-all + on: push: branches: @@ -25,11 +27,26 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract version and increment + id: versioning + run: | + # Get the latest tag from git + VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") + # Strip the 'v' prefix if it exists + VERSION=${VERSION#v} + # Split the version into its components + IFS='.' read -r -a VERSION_PARTS <<< "$VERSION" + # Increment the patch version + NEW_VERSION="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.$((VERSION_PARTS[2] + 1))" + # Output the new version + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT + - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile push: true - tags: ghcr.io/${{ github.repository }}/imageproxy:0.0.1 + tags: ghcr.io/${{ github.repository }}:${{ steps.versioning.outputs.new_version }} platforms: linux/amd64,linux/arm64 diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 0000000..15966d0 --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp diff --git a/.trunk/configs/.hadolint.yaml b/.trunk/configs/.hadolint.yaml new file mode 100644 index 0000000..98bf0cd --- /dev/null +++ b/.trunk/configs/.hadolint.yaml @@ -0,0 +1,4 @@ +# Following source doesn't work in most setups +ignored: + - SC1090 + - SC1091 diff --git a/.trunk/configs/.isort.cfg b/.trunk/configs/.isort.cfg new file mode 100644 index 0000000..5d7bf33 --- /dev/null +++ b/.trunk/configs/.isort.cfg @@ -0,0 +1,2 @@ +[tool.isort] +profile = "black" diff --git a/.trunk/configs/.markdownlint.yaml b/.trunk/configs/.markdownlint.yaml new file mode 100644 index 0000000..b40ee9d --- /dev/null +++ b/.trunk/configs/.markdownlint.yaml @@ -0,0 +1,2 @@ +# Prettier friendly markdownlint config (all formatting rules disabled) +extends: markdownlint/style/prettier diff --git a/.trunk/configs/.yamllint.yaml b/.trunk/configs/.yamllint.yaml new file mode 100644 index 0000000..184e251 --- /dev/null +++ b/.trunk/configs/.yamllint.yaml @@ -0,0 +1,7 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/configs/ruff.toml b/.trunk/configs/ruff.toml new file mode 100644 index 0000000..f5a235c --- /dev/null +++ b/.trunk/configs/ruff.toml @@ -0,0 +1,5 @@ +# Generic, formatter-friendly config. +select = ["B", "D3", "E", "F"] + +# Never enforce `E501` (line length violations). This should be handled by formatters. +ignore = ["E501"] diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 0000000..9c8d4a7 --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,40 @@ +# This file controls the behavior of Trunk: https://docs.trunk.io/cli +# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml +version: 0.1 +cli: + version: 1.22.2 +# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins) +plugins: + sources: + - id: trunk + ref: v1.6.0 + uri: https://github.com/trunk-io/plugins +# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes) +runtimes: + enabled: + - node@18.12.1 + - python@3.10.8 +# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration) +lint: + enabled: + - actionlint@1.7.1 + - bandit@1.7.9 + - black@24.4.2 + - checkov@3.2.164 + - git-diff-check + - hadolint@2.12.0 + - isort@5.13.2 + - markdownlint@0.41.0 + - osv-scanner@1.8.1 + - prettier@3.3.2 + - ruff@0.5.0 + - trivy@0.52.2 + - trufflehog@3.79.0 + - yamllint@1.35.1 +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + - trunk-fmt-pre-commit + enabled: + - trunk-upgrade-available diff --git a/Dockerfile b/Dockerfile index c75a5b1..733d65a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.0b3 as base +FROM python:3.13.0b3 AS base # Prevents Python from writing pyc files. ENV PYTHONDONTWRITEBYTECODE=1 @@ -13,21 +13,20 @@ WORKDIR /app # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ - --disabled-password \ - --gecos "" \ - --home "/nonexistent" \ - --shell "/sbin/nologin" \ - --no-create-home \ - --uid "${UID}" \ - appuser + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. # Leverage a bind mount to requirements.txt to avoid having to copy them into # into this layer. -RUN --mount=type=cache,target=/root/.cache/pip \ - --mount=type=bind,source=requirements.txt,target=requirements.txt \ - python -m pip install -r requirements.txt +RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \ + python -m pip install --no-cache-dir -r requirements.txt # Switch to the non-privileged user to run the application. USER appuser @@ -38,5 +37,7 @@ COPY app.py . # Expose the port that the application listens on. EXPOSE 8000 +HEALTHCHECK --interval=10s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/health | grep OK || exit 1 + # Run the application. -CMD waitress-serve --host=0.0.0.0 --port=8000 app:app +CMD ["waitress-serve", "--host=0.0.0.0", "--port=8000", "app:app"] diff --git a/app.py b/app.py index 8adfaa1..e25c156 100644 --- a/app.py +++ b/app.py @@ -1,8 +1,9 @@ -from flask import Flask, request, render_template_string +# trunk-ignore-all(black) +from flask import Flask, render_template_string, request app = Flask(__name__) -HTML_TEMPLATE = ''' +HTML_TEMPLATE = """ @@ -14,16 +15,22 @@ Proxied Image -''' +""" -@app.route('/image') + +@app.route("/image") def proxy_image(): - image_url = request.args.get('url') - if not image_url: - return "No image URL provided", 400 + image_url = request.args.get("url") + if not image_url: + return "No image URL provided", 400 + + html_content = render_template_string(HTML_TEMPLATE, image_url=image_url) + return html_content - html_content = render_template_string(HTML_TEMPLATE, image_url=image_url) - return html_content +@app.route("/health") +def health(): + return "OK" -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000) \ No newline at end of file +if __name__ == "__main__": + # trunk-ignore(bandit/B104) + app.run(host="0.0.0.0", port=8000) diff --git a/compose.yaml b/compose.yaml index 420ef6a..d66acbf 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,49 +1,5 @@ -# Comments are provided throughout this file to help you get started. -# If you need more help, visit the Docker Compose reference guide at -# https://docs.docker.com/go/compose-spec-reference/ - -# Here the instructions define your application as a service called "server". -# This service is built from the Dockerfile in the current directory. -# You can add other services your application may depend on here, such as a -# database or a cache. For examples, see the Awesome Compose repository: -# https://github.com/docker/awesome-compose services: - server: - build: - context: . + imageproxy: + image: ghcr.io/wuast94/imageproxy ports: - 8000:8000 - -# The commented out section below is an example of how to define a PostgreSQL -# database that your application can use. `depends_on` tells Docker Compose to -# start the database before your application. The `db-data` volume persists the -# database data between container restarts. The `db-password` secret is used -# to set the database password. You must create `db/password.txt` and add -# a password of your choosing to it before running `docker compose up`. -# depends_on: -# db: -# condition: service_healthy -# db: -# image: postgres -# restart: always -# user: postgres -# secrets: -# - db-password -# volumes: -# - db-data:/var/lib/postgresql/data -# environment: -# - POSTGRES_DB=example -# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password -# expose: -# - 5432 -# healthcheck: -# test: [ "CMD", "pg_isready" ] -# interval: 10s -# timeout: 5s -# retries: 5 -# volumes: -# db-data: -# secrets: -# db-password: -# file: db/password.txt -