diff --git a/Dockerfile b/Dockerfile index 569e2fd..1810ff0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,16 @@ FROM python:3-alpine LABEL org.opencontainers.image.authors="socket.dev" ARG CLI_VERSION +ARG PIP_INDEX_URL=https://pypi.org/simple RUN apk update \ && apk add --no-cache git nodejs npm yarn -RUN pip install socketsecurity --upgrade \ + +RUN pip install --index-url ${PIP_INDEX_URL} socketsecurity==$CLI_VERSION \ && socketcli -v \ - && socketcli -v | grep -q $CLI_VERSION \ No newline at end of file + && socketcli -v | grep -q $CLI_VERSION + +# !! Uncomment to test local build - requires running `python -m build` first (and correct version number) +# COPY dist/socketsecurity-1.0.34-py3-none-any.whl /tmp/ +# RUN pip install /tmp/socketsecurity-1.0.34-py3-none-any.whl \ +# && socketcli -v \ +# && socketcli -v | grep -q $CLI_VERSION \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 8d6d6e4..abb816e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,13 +14,9 @@ dependencies = [ 'GitPython >= 3.1.43', 'packaging >= 24.1', 'python-dotenv >= 1.0.1', + 'socket-sdk-python >= 1.0.14' ] -# modern, faster linter and language server. install with `pip install -e ".[dev]"` -[project.optional-dependencies] -dev = [ - "ruff>=0.3.0", -] readme = "README.md" description = "Socket Security CLI for CI/CD" @@ -40,6 +36,12 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] +# modern, faster linter and language server. install with `pip install -e ".[dev]"` +[project.optional-dependencies] +dev = [ + "ruff>=0.3.0", +] + [project.scripts] socketcli = "socketsecurity.socketcli:cli" diff --git a/scripts/build_container.sh b/scripts/build_container.sh index a0c2a1b..9a609c4 100755 --- a/scripts/build_container.sh +++ b/scripts/build_container.sh @@ -1,43 +1,142 @@ #!/bin/sh + +check_pypi_version() { + local version=$1 + local repo=$2 + local url="https://test.pypi.org/pypi/socketsecurity/$version/json" + + if [ "$repo" = "prod" ]; then + url="https://pypi.org/pypi/socketsecurity/$version/json" + fi + + if curl --output /dev/null --silent --head --fail "$url"; then + return 0 + else + return 1 + fi +} + +build_and_push_docker() { + local version=$1 + local tags=$2 + local dry_run=$3 + local is_test=$4 + + local pip_index="https://pypi.org/simple" + if [ "$is_test" = "true" ]; then + pip_index="https://test.pypi.org/simple --extra-index-url https://pypi.org/simple" + fi + + if [ "$dry_run" = "dry-run=true" ]; then + echo "[DRY RUN] Would execute the following commands:" + echo " docker buildx create --use --name multi-platform-builder" + echo " docker buildx build --push --no-cache \\" + echo " --build-arg CLI_VERSION=$version \\" + echo " --build-arg PIP_INDEX_URL=\"$pip_index\" \\" + echo " --platform linux/amd64,linux/arm64 \\" + echo " $tags ." + echo " docker buildx rm multi-platform-builder" + else + echo "Building docker image for version $version with tags: $tags" + docker buildx create --use --name multi-platform-builder + docker buildx build --push --no-cache \ + --build-arg CLI_VERSION=$version \ + --build-arg PIP_INDEX_URL="$pip_index" \ + --platform linux/amd64,linux/arm64 \ + $tags . + docker buildx rm multi-platform-builder + fi +} + +wait_for_pypi_version() { + local version=$1 + local repo=$2 + local timeout=300 # 5 minutes + local interval=10 # 10 seconds + local elapsed=0 + + echo "Waiting for version $version to be available on ${repo}PyPI..." + while [ $elapsed -lt $timeout ]; do + if check_pypi_version "$version" "$repo"; then + echo "Version $version is now available!" + return 0 + fi + echo "Version not available yet, waiting ${interval} seconds... (${elapsed}s/${timeout}s)" + sleep $interval + elapsed=$((elapsed + interval)) + done + + echo "Timeout waiting for version to appear on PyPI after ${timeout} seconds" + return 1 +} + VERSION=$(grep -o "__version__.*" socketsecurity/__init__.py | awk '{print $3}' | tr -d "'") ENABLE_PYPI_BUILD=$1 STABLE_VERSION=$2 -echo $VERSION -if [ -z $ENABLE_PYPI_BUILD ] || [ -z $STABLE_VERSION ]; then - echo "$0 pypi-build=enable stable=true" - echo "\tpypi-build: Build and publish a new version of the package to pypi. Options are prod or test" - echo "\tstable: Only build and publish a new version for the stable docker tag if it has been tested and going on the changelog" - exit -fi - -if [ $ENABLE_PYPI_BUILD = "pypi-build=prod" ]; then - echo "Doing production build" - python -m build --wheel --sdist - twine upload dist/*$VERSION* - sleep 120 - docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:$VERSION . \ - && docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:latest . \ - && docker push socketdev/cli:$VERSION \ - && docker push socketdev/cli:latest -fi +DRY_RUN=$3 -if [ $ENABLE_PYPI_BUILD = "pypi-build=test" ]; then - echo "Doing test build" - python -m build --wheel --sdist - twine upload --repository testpypi dist/*$VERSION* - sleep 120 - docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:$VERSION . \ - && docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:latest . \ - && docker push socketdev/cli:$VERSION-test \ - && docker push socketdev/cli:test +if [ -z $ENABLE_PYPI_BUILD ] || [ -z $STABLE_VERSION ]; then + echo "$0 pypi-build=enable stable=true [dry-run=true]" + echo "\tpypi-build: Build and publish a new version $VERSION of the package to pypi. Options are prod or test" + echo "\tstable: Only build and publish a new version for the stable docker tag if it has been tested and going on the changelog" + echo "\tdry-run: Optional. If true, only print commands without executing" + exit 1 fi +echo "Running with: ENABLE_PYPI_BUILD=$ENABLE_PYPI_BUILD STABLE_VERSION=$STABLE_VERSION DRY_RUN=$DRY_RUN" -if [ $STABLE_VERSION = "stable=true" ]; then - if [ $ENABLE_PYPI_BUILD = "pypi-build=enable" ]; then - sleep 120 - fi - docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:stable . \ - && docker push socketdev/cli:stable - fi +case $ENABLE_PYPI_BUILD in + "pypi-build=prod") + echo "Doing production build" + if ! check_pypi_version "$VERSION" "prod"; then + if [ "$DRY_RUN" = "dry-run=true" ]; then + echo "[DRY RUN] Would execute: python -m build --wheel --sdist && twine upload dist/*$VERSION*" + else + if python -m build --wheel --sdist && twine upload dist/*$VERSION*; then + if wait_for_pypi_version "$VERSION" "prod"; then + build_and_push_docker "$VERSION" "-t socketdev/cli:$VERSION -t socketdev/cli:latest" "$DRY_RUN" "false" + if [ "$STABLE_VERSION" = "stable=true" ]; then + build_and_push_docker "$VERSION" "-t socketdev/cli:stable" "$DRY_RUN" "false" + fi + else + echo "Failed to verify package on PyPI" + exit 1 + fi + else + echo "Failed to build or upload to PyPI" + exit 1 + fi + fi + else + echo "Version $VERSION already exists on PyPI, skipping upload" + build_and_push_docker "$VERSION" "-t socketdev/cli:$VERSION -t socketdev/cli:latest" "$DRY_RUN" "false" + if [ "$STABLE_VERSION" = "stable=true" ]; then + build_and_push_docker "$VERSION" "-t socketdev/cli:stable" "$DRY_RUN" "false" + fi + fi + ;; + "pypi-build=test") + echo "Doing test build" + if ! check_pypi_version "$VERSION" "test"; then + if [ "$DRY_RUN" = "dry-run=true" ]; then + echo "[DRY RUN] Would execute: python -m build --wheel --sdist && twine upload --repository testpypi dist/*$VERSION*" + else + if python -m build --wheel --sdist && twine upload --repository testpypi dist/*$VERSION*; then + if wait_for_pypi_version "$VERSION" "test"; then + build_and_push_docker "$VERSION" "-t socketdev/cli:$VERSION-test -t socketdev/cli:test" "$DRY_RUN" "true" + else + echo "Failed to verify package on TestPyPI" + exit 1 + fi + else + echo "Failed to build or upload to TestPyPI" + exit 1 + fi + fi + else + echo "Version $VERSION already exists on TestPyPI, skipping upload" + build_and_push_docker "$VERSION" "-t socketdev/cli:$VERSION-test -t socketdev/cli:test" "$DRY_RUN" "true" + fi + ;; +esac diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index 5c40c13..729f943 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,2 +1,2 @@ __author__ = 'socket.dev' -__version__ = '1.0.33' +__version__ = '1.0.34'