diff --git a/.bumpversion.toml b/.bumpversion.toml new file mode 100644 index 0000000000000000000000000000000000000000..fa69c63cdb5c8dbe369ff78750b946f6c7402621 --- /dev/null +++ b/.bumpversion.toml @@ -0,0 +1,19 @@ +[tool.bumpversion] +commit = true +current_version = "1.0.0" +parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)" +replace = "{new_version}" +search = "{current_version}" +serialize = [ + "{major}.{minor}.{patch}", +] +tag = false + +[[tool.bumpversion.files]] +filename = "CHANGELOG.md" +replace = """## [Unreleased] + +## [{new_version}] - {now:%Y-%m-%d} +""" +search = """## [Unreleased] +""" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..6d2a3d146fb70cac84c0b0d738b54ea8421d6fa8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,176 @@ +include: + - remote: 'https://gitlab.com/swepy/cicd-templates/docker-build/-/raw/0.2.0/templates/docker-build.yml' + - remote: 'https://gitlab.com/swepy/cicd-templates/release-by-changelog/-/raw/0.4.2/templates/release-by-changelog.yml' + +release_by_changelog: + rules: + - if: '$CI_PIPELINE_SOURCE != "pipeline" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + +next_version: + stage: build + image: alpine:latest + variables: + PACKAGE_NAME: ruff + IMAGE_NAME: swepy/ruff + script: + #!/bin/bash + - apk update + - apk add --no-cache curl jq + + # get docker tags + - echo "Image name=$IMAGE_NAME" + - docker_url="https://registry.hub.docker.com/v2/repositories/$IMAGE_NAME/tags" + - tags="" + + # get all tags + - while [ -n "$docker_url" ]; do + - docker_response=$(curl -s "$docker_url") + - if [ "$(echo "$docker_response" | jq -r '.results | length')" -eq 0 ]; then + - break + - fi + - tags="$tags $(echo "$docker_response" | jq -r '.results[].name')" + - docker_url=$(echo "$docker_response" | jq -r '.next') + - if [ "$docker_url" = "null" ]; then + - docker_url="" + - fi + - done + - echo "tags on docker=$tags" + + # pypi + - pypi_url="https://pypi.org/pypi/$PACKAGE_NAME/json" + - pypi_response=$(curl -s "$pypi_url") + - releases=$(echo "$pypi_response" | jq -r '.releases | keys[]') + + - echo "releases on pypi:" + - printf "%s " $releases + + # find missing releases + - missing_releases="" + - eval 'for release in $releases; do found=0; for tag in $tags; do if [ "$release" = "$tag" ]; then found=1; break; fi; done; if [ $found -eq 0 ]; then missing_releases="$missing_releases $release"; fi; done' + - missing_releases=$(echo $missing_releases) + - echo "missing tags=$missing_releases" + + # regex pattern for versioning (adjust if needed) + #regex='^[0-9]+(\.[0-9]+)*$' + - regex='^((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*))(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$' + - sorted_releases=$(printf "%s\n" $missing_releases | grep -E "$regex" | sort -t. -k1,1n -k2,2n -k3,3n) + + # print the sorted versions + - echo "Sorted releases:" + - printf "%s " $sorted_releases + + - version_to_publish=$(echo $sorted_releases | awk '{print $1}') + - 'echo "Version to publish: $version_to_publish"' + + - echo "$version_to_publish" > version.txt + - cat version.txt + artifacts: + name: "version" + paths: + - version.txt + expire_in: 1 day + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + +docker_build: + variables: + TAG: $CI_COMMIT_SHA + REPOSITORY: ruff + PACKAGE_NAME: ruff + IMAGE_NAME: $DOCKER_USER/$REPOSITORY + TARGET_IMAGE: $CI_REGISTRY_IMAGE:$TAG + PROJECT_PATH: . + before_script: + - cat version.txt + - KANIKO_OPTIONS="--build-arg VERSION=$(cat version.txt)" + - echo "Kaniko options=$KANIKO_OPTIONS" + needs: + - job: next_version + artifacts: true + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + +test_ruff_image: + image: $SRC_IMAGE + stage: test + variables: + SRC_IMAGE: $CI_REGISTRY_IMAGE:$TAG + TAG: $CI_COMMIT_SHA + tags: + - docker + script: + - ruff --help + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + +trivy: + stage: test + image: docker:stable + variables: + SRC_IMAGE: $CI_REGISTRY_IMAGE:$TAG + TAG: $CI_COMMIT_SHA + TRIVY_VERSION: "latest" + services: + - name: docker:dind + entrypoint: ["env", "-u", "DOCKER_HOST"] + command: ["dockerd-entrypoint.sh"] + tags: + - docker + before_script: + - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin lab.frogg.it:5050 + - docker info + - docker pull aquasec/trivy:$TRIVY_VERSION + - wget --no-verbose https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/gitlab.tpl -P /usr/local/bin/ + - docker pull $SRC_IMAGE + allow_failure: true + script: + # Build report using Trivy Docker image + - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $CI_PROJECT_DIR:/root aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 0 --format template --template "/usr/local/bin/gitlab.tpl" -o /root/gl-container-scanning-report.json $SRC_IMAGE + # Print report + - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 0 --severity HIGH $SRC_IMAGE + # Fail on severe vulnerabilities + - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 1 --severity CRITICAL $SRC_IMAGE + cache: + paths: + - .trivycache/ + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + +docker_push: + image: docker:latest + services: + - docker:dind + variables: + DOCKER_REPO: ruff + TARGET_IMAGE: swepy/$DOCKER_REPO + SRC_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + stage: deploy + tags: + - docker + before_script: + - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin lab.frogg.it:5050 + - VERSION=$(cat version.txt) + script: + # Pull the image + - docker pull $SRC_IMAGE + + # Get the Ruff version by running the container + - RUFF_VERSION=${VERSION} + - RUFF_MINOR=${RUFF_VERSION%.*} + - RUFF_MAJOR=${RUFF_VERSION%%.*} + - echo $RUFF_VERSION + + # Tag the image + - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_VERSION + - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_MINOR + - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_MAJOR + - docker tag $SRC_IMAGE $TARGET_IMAGE:latest + + # Login to Docker Hub using DOCKER_AUTH_CONFIG + - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD + - docker push $TARGET_IMAGE:$RUFF_VERSION + - docker push $TARGET_IMAGE:$RUFF_MINOR + - docker push $TARGET_IMAGE:$RUFF_MAJOR + - docker push $TARGET_IMAGE:latest + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..12e582062c9206231a412713e3d60d40b979b601 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to this job will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2024-06-24 + +* Initial version diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6a7e012169c58f7ea840e82e9f1282a4c30d49b3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,79 @@ +# This dockerfile can generate a docker image with ruff since ruff firsts version, +# with a few changes in configuration. Those changes are required since early versions +# of ruff compilation stage rely on "open64", wich is not available in recent alpine +# linux. + + +ARG APP_ROOT=/app +ARG APP_USER=app +ARG APP_UID=1000 +ARG APP_GID=1000 + +# Default environment with a non priviledged user. +# For ruff < 0.0.20, uncomment following line +FROM python:3.12.0-alpine3.18 as base +# For ruff < 0.0.20, comment following line +#FROM python:alpine as base + +ARG APP_ROOT +ARG APP_USER +ARG APP_UID +ARG APP_GID + +RUN mkdir -p $APP_ROOT/.local/bin && \ + adduser $APP_USER -h $APP_ROOT -u $APP_UID -g $APP_GID -DH && \ + chown -R $APP_UID:$APP_GID $APP_ROOT # force the user to be the owner of the app directory + +FROM base as builder + +ARG VERSION="" +ARG APP_UID +ARG APP_ROOT + +ENV VERSION=${VERSION} +ENV PATH=$APP_ROOT/venv/bin:$PATH + +# For ruff < 0.0.20, uncomment following line +RUN apk add --no-cache maturin cargo + +# Never build with root because we don't need priviledged access to +# build, preventing external source code from running as root. +USER $APP_UID + +WORKDIR $APP_ROOT + +RUN python3 -m venv --upgrade-deps $APP_ROOT/venv + +# disable pip cache to reduce the image size +RUN pip install --prefer-binary --no-cache-dir ruff==${VERSION} + + +FROM base + +ARG APP_ROOT +ARG APP_USER +ARG APP_UID +ARG APP_GID +ARG VERSION="" + +# For ruff < 0.0.20, uncomment following line +RUN apk add libgcc + +USER $APP_UID +WORKDIR $APP_ROOT + +ENV PATH=$APP_ROOT/venv/bin:$PATH +ENV VERSION=${VERSION} +COPY --from=builder --chown=$APP_UID:$APP_GID $APP_ROOT/venv ./venv + +LABEL maintainers="Dorian Turba <contact.docker.cblm4@simplelogin.com>, osoc@devkontrol.slmail.me" +LABEL description="Lightweight image to run Ruff, the extremely fast Python linter and code formatter, written in Rust." +LABEL version="${VERSION}" +LABEL license="MIT" +LABEL org.opencontainers.image.source="https://lab.frogg.it/swepy/containers/ruff" +LABEL org.opencontainers.image.source.gitlab_com="https://gitlab.com/swepy/containers/ruff" +LABEL org.opencontainers.image.issues="https://lab.frogg.it/swepy/containers/ruff/-/issues" +LABEL org.opencontainers.image.created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" +LABEL org.opencontainers.image.title="Ruff" +LABEL org.opencontainers.image.url="https://astral.sh/ruff" +LABEL org.opencontainers.image.revision="0.1.0" diff --git a/LICENCE.md b/LICENCE.md new file mode 100644 index 0000000000000000000000000000000000000000..dabc166f600f5f6461e330abb57270c42da81c18 --- /dev/null +++ b/LICENCE.md @@ -0,0 +1,18 @@ +Copyright 2024 dorian turba + +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 597f3c4a93d46c98c2a1467b00922fabae700893..e26e41e4a3a94514c4ccf5df1b1e75ee58b85fb3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Python Ruff -Docker image for python with Ruff installed \ No newline at end of file +Docker image for python with Ruff installed