diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 517abe0..fd12bdd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,66 @@ -name: Safety Action Build And Publish +name: Publish Docker image on: - push: - tags: - - "^(\\d+\\.\\d+(\\.\\d+)?)$" - branches: [image-builds] - -env: - DOCKER_BUILDKIT: 1 + workflow_dispatch: jobs: - build: - runs-on: ubuntu-20.04 - environment: main + build-and-push: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + cache: "pip" + + - name: Safety Version + run: | + pip install packaging + package_version=$(cat safety/VERSION) + echo $package_version + echo "SAFETY_VERSION=$package_version" >> $GITHUB_ENV + + - name: Extract Major and Minor Version + run: | + python scripts/extract_version.py + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:v0.10.6 + + - name: Log into registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/pyupio/safety + tags: | + type=raw,value=${{ env.SAFETY_VERSION }},suffix=-{{ sha }} + type=raw,value=${{ env.SAFETY_VERSION }} + type=raw,value=${{ env.SAFETY_MAJOR_VERSION }}.${{ env.SAFETY_MINOR_VERSION }} + type=raw,value=${{ env.SAFETY_MAJOR_VERSION }} + type=raw,value=latest + labels: | + org.opencontainers.image.title=Safety CLI + org.opencontainers.image.description=Safety CLI is a Python dependency vulnerability scanner that enhances software supply chain security at every stage of development. + org.opencontainers.image.vendor=Safety Cybersecurity + org.opencontainers.image.licenses=MIT - - name: Build image - run: docker build -t pyupio/safety-v2-beta:latest . + - name: Build and Push image + uses: docker/build-push-action@v4 + with: + context: . + push: true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: SAFETY_VERSION=${{ env.SAFETY_VERSION }} - - name: Upload image - env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin && - docker push pyupio/safety-v2-beta:latest diff --git a/.github/workflows/gh-action-integration-matrix.json b/.github/workflows/gh-action-integration-matrix.json deleted file mode 100644 index fe87227..0000000 --- a/.github/workflows/gh-action-integration-matrix.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - {"version": "2.3.4"}, - {"version": "2.3.5"}, - {"version": ""} -] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 05acff1..b1f9577 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] steps: - uses: actions/checkout@v3 - name: Set up Python @@ -33,7 +33,7 @@ jobs: needs: test runs-on: ubuntu-20.04 - if: contains(fromJson('["refs/heads/main", "refs/heads/develop", "refs/heads/binaries-fixes"]'), github.ref) || startsWith(github.ref, 'refs/tags') + if: contains(fromJson('["refs/heads/main", "refs/heads/binaries-fixes"]'), github.ref) || startsWith(github.ref, 'refs/tags') steps: - name: Slack trigger @@ -45,8 +45,6 @@ jobs: needs: test runs-on: ${{ matrix.os }} - if: contains(fromJson('["refs/heads/main", "refs/heads/develop", "refs/heads/binaries-fixes"]'), github.ref) || startsWith(github.ref, 'refs/tags') - strategy: matrix: os: [ 'windows-latest', 'ubuntu-20.04', 'macos-latest' ] diff --git a/.github/workflows/test-insecure.yml b/.github/workflows/test-insecure.yml deleted file mode 100644 index ee0e3dc..0000000 --- a/.github/workflows/test-insecure.yml +++ /dev/null @@ -1,156 +0,0 @@ -######## Insecure test cases. All of these use insecure packages, and should fail - so they have `continue-on-error` -######## set on the action step, and a further step to ensure the previous step failed (and actually fail if it _didn't_) -name: Safety Action Insecure Tests - -on: - push: - branches: [main, develop, image-ci] - -jobs: - matrix: - runs-on: ubuntu-20.04 - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - uses: actions/checkout@v3 - - id: set-matrix - run: | - TASKS=$(echo $(cat .github/workflows/gh-action-integration-matrix.json) | sed 's/ //g' ) - echo "matrix=$TASKS" >> $GITHUB_OUTPUT - - ##### Auto mode tests - ### File scanning - # Scans a requirements.txt in the repo; the simplest case. We contort one into existing for this test - # case, to avoid confusion - test-auto-requirements-txt-insecure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/requirements.txt-insecure requirements.txt - - - uses: ./ - id: scan-1 - continue-on-error: true - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - - if: steps.scan-1.outcome != 'failure' || steps.scan-1.outputs.exit-code != '64' - run: exit 1 - - # Same as above, but for a poetry lock file - test-auto-poetry-insecure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/poetry.lock-insecure poetry.lock && cp tests/action/pyproject.toml-insecure pyproject.toml - - - uses: ./ - id: scan-2 - continue-on-error: true - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - - if: steps.scan-2.outcome != 'failure' || steps.scan-2.outputs.exit-code != '64' - run: exit 1 - - # Same as above, but for a Pipfile.lock - test-auto-pipfile-insecure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/Pipfile.lock-insecure Pipfile.lock - - - uses: ./ - id: scan-3 - continue-on-error: true - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - - if: steps.scan-3.outcome != 'failure' || steps.scan-3.outputs.exit-code != '64' - run: exit 1 - - ### Env scanning: - ### Scans the runner environment. Here, the Github action `actions/setup-python@v3` actually - ### installs things in the root VM that the action runs on; this is what gets scanned. - test-auto-environment-insecure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - uses: actions/setup-python@v3 - with: - python-version: '3.10' - architecture: 'x64' - - - run: python -m pip install -r tests/action/requirements.txt-insecure - - - uses: ./ - id: scan-4 - continue-on-error: true - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - - if: steps.scan-4.outcome != 'failure' || steps.scan-4.outputs.exit-code != '64' - run: exit 1 - - ### Docker scanning: - ### Scans a recently built Docker container. This uses a few heuristics, defined in entrypoint.sh - test-auto-docker-insecure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - name: Build image - run: DOCKER_BUILDKIT=1 docker build -t my-insecure-image tests/action/docker-insecure - - - uses: ./ - id: scan-5 - continue-on-error: true - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - - if: steps.scan-5.outcome != 'failure' || steps.scan-5.outputs.exit-code != '64' - run: exit 1 diff --git a/.github/workflows/test-secure.yml b/.github/workflows/test-secure.yml deleted file mode 100644 index 93b3b90..0000000 --- a/.github/workflows/test-secure.yml +++ /dev/null @@ -1,138 +0,0 @@ -######## Secure test cases. All of these use secure packages, and shouldn't fail. Easier than our insecure -######## case, as we don't need anything else. We test against Safety and its deps here; if these tests -######## fail, the pinned version might need to be updated. -name: Safety Action Secure Tests - -on: - push: - branches: [main, develop, image-ci] - -jobs: - matrix: - runs-on: ubuntu-20.04 - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - uses: actions/checkout@v3 - - id: set-matrix - run: | - TASKS=$(echo $(cat .github/workflows/gh-action-integration-matrix.json) | sed 's/ //g' ) - echo "matrix=$TASKS" >> $GITHUB_OUTPUT - - ##### Auto mode tests - ### File scanning - # Scans a requirements.txt in the repo; the simplest case. We contort one into existing for this test - # case, to avoid confusion - test-auto-requirements-txt-secure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/requirements.txt-secure requirements.txt - - - uses: ./ - id: scan-1 - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - # Same as above, but for a poetry lock file - test-auto-poetry-secure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/poetry.lock-secure poetry.lock && cp tests/action/pyproject.toml-secure pyproject.toml - - - uses: ./ - id: scan-2 - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - # Same as above, but for a Pipfile.lock - test-auto-pipfile-secure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - run: cp tests/action/Pipfile.lock-secure Pipfile.lock - - - uses: ./ - id: scan-3 - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - - ### Env scanning: - ### Scans the runner environment. Here, the Github action `actions/setup-python@v3` actually - ### installs things in the root VM that the action runs on; this is what gets scanned. - test-auto-environment-secure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - uses: actions/setup-python@v3 - with: - python-version: '3.10' - architecture: 'x64' - - - run: python -m pip install -r tests/action/requirements.txt-secure - - - uses: ./ - id: scan-4 - with: - api-key: ${{ secrets.SAFETY_API_KEY }} - args: '-i 52495' - - ### Docker scanning: - ### Scans a recently built Docker container. This uses a few heuristics, defined in entrypoint.sh - test-auto-docker-secure: - needs: [ matrix ] - runs-on: ubuntu-20.04 - environment: main - strategy: - matrix: - safety: ${{ fromJson(needs.matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.safety.version }} - - - name: Build image - run: DOCKER_BUILDKIT=1 docker build -t my-secure-image tests/action/docker-secure - - - uses: ./ - id: scan-5 - with: - api-key: ${{ secrets.SAFETY_API_KEY }} diff --git a/Dockerfile b/Dockerfile index 9fd9981..883a189 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,17 @@ -FROM python:3.10-slim +FROM python:3.12-slim +ARG SAFETY_VERSION # Don't use WORKDIR here as per Github's docs RUN mkdir /app -RUN apt-get update && apt-get -y install docker.io jq && apt-get clean && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get -y install docker.io jq git && apt-get clean && rm -rf /var/lib/apt/lists/* -# Install poetry and pipenv; used for converting their respective lockfile formats to generic requirements.txt -RUN cd /app && python3 -m pip install poetry==1.1.13 pipenv==2022.6.7 +COPY entrypoint.sh /app/entrypoint.sh -# Install this project dependencies -COPY . /app -RUN cd /app && python3 -m pip install -e .[github] +RUN cd /app && python3 -m pip install safety==$SAFETY_VERSION ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 ENV PYTHONPATH="/app" -LABEL safety_autodetect=ignore - ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index af62ea5..577836a 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,214 +1,8 @@ #!/usr/bin/env bash set -eu -o pipefail -# If SAFETY_ACTION isn't set, just run safety directly and pass through any commands. -if [ "${SAFETY_ACTION:-}" != "true" ]; then - export SAFETY_OS_TYPE="docker" - export SAFETY_OS_RELEASE="" - export SAFETY_OS_DESCRIPTION="run" - - exec python -m safety $@ -fi - -find_best_docker_image () { - for image_id in $(docker images --filter "dangling=false" --format="{{.ID}}"); do - json=$(docker inspect $image_id) - - safety_autodetect_ignore=$(echo "${json}" | jq -r .[0].Config.Labels.safety_autodetect) - - created_at=$(date --date="$(echo "${json}" | jq -r .[0].Created)" +%s) - now=$(date +%s) - - # Skip the action itself - if [[ "${safety_autodetect_ignore}" == "ignore" ]]; then - continue - fi - - # Limit of 1 hour to scan back - if [[ "$(($now-$created_at))" -gt 3600 ]]; then - break - fi - - echo $image_id - break - done -} - -get_repo_tags () { - docker inspect "${1}" | jq -r "(.[0].RepoTags // [\"${1}\"]) | join(\",\")" -} - -export SAFETY_OS_TYPE="docker action" +export SAFETY_OS_TYPE="docker" export SAFETY_OS_RELEASE="" -export SAFETY_OS_DESCRIPTION="" - -# Compatibility default values, this avoids breaking old GitHub actions. -if [[ "${SAFETY_ACTION_CREATE_PR:-}" == "" ]]; then - export SAFETY_ACTION_CREATE_PR="false" -fi - -if [[ "${SAFETY_ACTION_CREATE_ISSUE:-}" == "" ]]; then - export SAFETY_ACTION_CREATE_ISSUE="false" -fi - -# auto / docker / env / file -if [ "${SAFETY_ACTION_SCAN}" = "auto" ]; then - echo "[Safety Action] Autodetecting Mode..." 1>&2 - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} mode_auto" - - if [ "$(find_best_docker_image)" != "" ]; then - SAFETY_ACTION_SCAN="docker" - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} docker_detected" - echo "[Safety Action] Autodetected mode: docker - Safety will scan a recently built Docker container. If this is not what you want, set the scan variable in the action configuration manually." 1>&2 - elif [ "${pythonLocation:-}" != "" ]; then - SAFETY_ACTION_SCAN="env" - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} env_detected" - echo "[Safety Action] Autodetected mode: env - Safety will scan the Action CI environment. If this is not what you want, set the scan variable in the action configuration manually." 1>&2 - elif [ -e "Pipfile.lock" ] || [ -e "poetry.lock" ] || [ -e "requirements.txt" ]; then - SAFETY_ACTION_SCAN="file" - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_detected" - echo "[Safety Action] Autodetected mode: file - Safety will scan a lock file commited in this repo. If this is not what you want, set the scan variable in the action configuration manually." 1>&2 - else - echo "[Safety Action] Could not autodetect mode. Please set the scan variable in the action configuration manually." 1>&2 - exit 1 - fi -fi - -# remediation mode -if [ "${SAFETY_ACTION_CREATE_PR}" = "true" ] && [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then - echo "[Safety Action] Can only create issues or PRs, not both." - exit 1 -fi - -if [ "${SAFETY_ACTION_CREATE_PR}" = "true" ] || [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then - if [ "${SAFETY_ACTION_SCAN}" != "file" ]; then - echo "[Safety Action] Creating PRs / issues is only supported when scanning a requirements file." - exit 1 - fi - - # TODO: Add info to env vars for telemetry... - - # Build up a list of requirements files, or use SAFETY_ACTION_REQUIREMENTS if that's set. - # This will be moved into Safety proper in the future. - requirement_files=() - if [ -z "${SAFETY_ACTION_REQUIREMENTS}" ]; then - readarray -d '' matches < <(find . -type f -name requirements.txt -print0) - for match in ${matches[@]}; do - requirement_files+=("-r" "${match}") - done - else - requirement_files=("-r" "${SAFETY_ACTION_REQUIREMENTS}") - fi - - alert_action="github-pr" - if [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then - alert_action="github-issue" - fi - - # Continue on error is set because we're using Safety's output here for further processing. - python -m safety check "${requirement_files[@]}" --continue-on-error --output=json ${SAFETY_ACTION_ARGS} | python -m safety alert "${alert_action}" --repo "${GITHUB_REPOSITORY}" --token "${GITHUB_TOKEN}" --base-url "${GITHUB_API_URL}" - - exit 0 -fi - -if [ "${SAFETY_ACTION_SCAN}" = "docker" ]; then - if [[ "${SAFETY_ACTION_DOCKER_IMAGE}" == "" ]]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} docker_image_scan" - - SAFETY_ACTION_DOCKER_IMAGE="$(find_best_docker_image)" - SAFETY_ACTION_DOCKER_IMAGE_FRIENDLY="$(get_repo_tags "${SAFETY_ACTION_DOCKER_IMAGE}")" - else - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} docker_image_specified" - - SAFETY_ACTION_DOCKER_IMAGE_FRIENDLY="${SAFETY_ACTION_DOCKER_IMAGE}" - fi - - echo "[Safety Action] Scanning Docker Image: ${SAFETY_ACTION_DOCKER_IMAGE_FRIENDLY}" 1>&2 - docker run --rm --entrypoint /bin/sh "${SAFETY_ACTION_DOCKER_IMAGE}" -c "python -m pip list --format=freeze" > /tmp/requirements.txt - SAFETY_ACTION_REQUIREMENTS="/tmp/requirements.txt" -elif [ "${SAFETY_ACTION_SCAN}" = "env" ]; then - echo "[Safety Action] Scanning Current Environment" 1>&2 - - # We're running inside an isolated container, but we have access to the Docker socket. - # Run a command, in the root NS, to pip freeze and send that through for scanning. - # Somewhat based on the idea at https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389 - # Use pip list --format=freeze instead of pip freeze due to things like - # https://stackoverflow.com/questions/64194634/why-pip-freeze-returns-some-gibberish-instead-of-package-version - if [ "${pythonLocation:-}" != "" ]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} env_pythonLocation" - - docker run --rm --privileged --pid=host justincormack/nsenter1 "${pythonLocation}/bin/python" -m pip list --format=freeze > /tmp/requirements.txt - else - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} env_root" - - docker run --rm --privileged --pid=host justincormack/nsenter1 /bin/sh -c "pip list --format=freeze" > /tmp/requirements.txt - fi - SAFETY_ACTION_REQUIREMENTS="/tmp/requirements.txt" -elif [ "${SAFETY_ACTION_SCAN}" = "file" ]; then - if [[ "${SAFETY_ACTION_REQUIREMENTS}" == "" ]]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_path_scan" - - if [ -e "poetry.lock" ]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_path_scan_poetry" - SAFETY_ACTION_REQUIREMENTS="$(pwd)/poetry.lock" - elif [ -e "Pipfile.lock" ]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_path_scan_pipfile" - SAFETY_ACTION_REQUIREMENTS="$(pwd)/Pipfile.lock" - elif [ -e "requirements.txt" ]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_path_scan_requirementstxt" - SAFETY_ACTION_REQUIREMENTS="$(pwd)/requirements.txt" - else - echo "[Safety Action] Could not autodetect a poetry.lock / Pipfile.lock / requirements.txt automatically. Try set the requirements variables in the action configuration." 1>&2 - exit 1 - fi - - echo "[Safety Action] Autodetecting Requirements File: ${SAFETY_ACTION_REQUIREMENTS}" 1>&2 - else - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_path_specified" - fi - - echo "[Safety Action] Scanning Requirements File: ${SAFETY_ACTION_REQUIREMENTS}" 1>&2 - - if [[ "$(basename ${SAFETY_ACTION_REQUIREMENTS})" == "Pipfile.lock" ]]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_pipfile_converted" - - cd "$(dirname ${SAFETY_ACTION_REQUIREMENTS})" - pipenv requirements > /tmp/requirements.txt - echo "[Safety Action] Converted ${SAFETY_ACTION_REQUIREMENTS} to /tmp/requirements.txt using pipenv." 1>&2 - SAFETY_ACTION_REQUIREMENTS="/tmp/requirements.txt" - elif [[ $(basename ${SAFETY_ACTION_REQUIREMENTS}) == "poetry.lock" ]]; then - SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} file_poetry_converted" - - cd "$(dirname ${SAFETY_ACTION_REQUIREMENTS})" - poetry export -f requirements.txt --without-hashes > /tmp/requirements.txt - echo "[Safety Action] Converted ${SAFETY_ACTION_REQUIREMENTS} to /tmp/requirements.txt using poetry." 1>&2 - SAFETY_ACTION_REQUIREMENTS="/tmp/requirements.txt" - fi - - if [[ "${SAFETY_ACTION_CONTINUE_ON_ERROR,,}" == "yes" || "${SAFETY_ACTION_CONTINUE_ON_ERROR,,}" == "true" ]]; then - SAFETY_ACTION_CONTINUE_ON_ERROR="--continue-on-error" - fi -fi - -if [[ "${SAFETY_API_KEY:-}" == "" ]]; then - echo "[Safety Action] An API key is required to use this action. Please sign up for an account at https://pyup.io/" 1>&2 - exit 1 -fi - -# Don't hard fail from here on out; so we can return the exit code and output -set +e - -# This sends the output to both stdout and our variable, without buffering like echo would. -exec 5>&1 -output=$(python -m safety check -r "${SAFETY_ACTION_REQUIREMENTS}" --output="${SAFETY_ACTION_OUTPUT_FORMAT}" ${SAFETY_ACTION_CONTINUE_ON_ERROR} ${SAFETY_ACTION_ARGS} | tee >(cat - >&5)) -exit_code=$? - -# https://github.community/t/set-output-truncates-multiline-strings/16852/3 -output="${output//'%'/'%25'}" -output="${output//$'\n'/'%0A'}" -output="${output//$'\r'/'%0D'}" - -echo "::set-output name=exit-code::$exit_code" -echo "::set-output name=cli-output::$output" +export SAFETY_OS_DESCRIPTION="run" -exit $exit_code +exec python -m safety $@ diff --git a/scripts/extract_version.py b/scripts/extract_version.py new file mode 100644 index 0000000..0840d9a --- /dev/null +++ b/scripts/extract_version.py @@ -0,0 +1,13 @@ +import os +from packaging.version import Version + +raw_version = os.environ.get('SAFETY_VERSION', None) +if not raw_version: + raise ValueError("Missing SAFETY_VERSION environment variable") + +v = Version(raw_version) +major, minor = v.major, v.minor + +with open(os.getenv('GITHUB_ENV'), "a") as env: + print(f"SAFETY_MAJOR_VERSION={major}", file=env) + print(f"SAFETY_MINOR_VERSION={minor}", file=env)