Skip to content

Commit

Permalink
uv copy of the python folder and tested dry run
Browse files Browse the repository at this point in the history
This commit is after copying the python folder and running:

./bin/docker-dev-shell uv --rebuild
./bin/dry-run.rb uv dsp-testing/dependabot-uv

Co-authored-by: Gastón Avila <avila.gas@gmail.com>
Co-authored-by: Edgar Ramírez Mondragón <16805946+edgarrmondragon@users.noreply.github.com>
Co-authored-by: skshetry <18718008+skshetry@users.noreply.github.com>
Co-authored-by: Albert Ferràs <albert.ferras@veriff.net>
Co-authored-by: beagold <86345081+beagold@users.noreply.github.com>
  • Loading branch information
6 people committed Feb 28, 2025
1 parent 7d80315 commit a0e76f9
Show file tree
Hide file tree
Showing 463 changed files with 56,432 additions and 0 deletions.
1 change: 1 addition & 0 deletions uv/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BUNDLE_GEMFILE: "../dependabot-updater/Gemfile"
6 changes: 6 additions & 0 deletions uv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/.bundle/*
!.bundle/config
/.env
/tmp
/dependabot-*.gem
helpers/install-dir
1 change: 1 addition & 0 deletions uv/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
inherit_from: ../.rubocop.yml
181 changes: 181 additions & 0 deletions uv/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# This list must match the versions specified in
# python/lib/dependabot/uv/language_version_manager.rb: PRE_INSTALLED_PYTHON_VERSIONS
ARG PY_3_13=3.13.2
ARG PY_3_12=3.12.9
ARG PY_3_11=3.11.11
ARG PY_3_10=3.10.16
ARG PY_3_9=3.9.21
ARG PYENV_VERSION=v2.5.2

FROM ghcr.io/dependabot/dependabot-updater-core AS python-core
ARG PY_3_13
ARG PY_3_12
ARG PY_3_11
ARG PY_3_10
ARG PY_3_9
ARG PYENV_VERSION
USER root

COPY --chown=dependabot:dependabot uv/helpers /opt/python/helpers

# TODO: Now that switched from `pyenv install` which compiled from source to downloading / copying a pre-compiled python
# we could entirely drop pyenv if we change our ruby code that calls `pyenv exec` to track which version of python to
# call and uses the full python paths.
ENV PYENV_ROOT=/usr/local/.pyenv \
PATH="/usr/local/.pyenv/bin:$PATH"
RUN mkdir -p "$PYENV_ROOT" && chown dependabot:dependabot "$PYENV_ROOT"
USER dependabot
ENV DEPENDABOT_NATIVE_HELPERS_PATH="/opt"
RUN git -c advice.detachedHead=false clone https://github.com/pyenv/pyenv.git --branch $PYENV_VERSION --single-branch --depth=1 /usr/local/.pyenv

# We used to use `pyenv install 3.x.y` but it's really slow because it compiles from source (~500s). So instead, we hack
# around that by downloading pre-compiled versions, then placing them where pyenv expects it in the `versions` subfolder.
# In the future, we should consider dropping pyenv completely, as it's mostly used here for legacy reasons...
# Although it is convenient when debugging to be able to quickly flip through environments.
RUN mkdir "${PYENV_ROOT}/versions"

## 3.9
# Docker doesn't support parametrizing `COPY --from:python:$PY_1_23-bookworm`, so work around it using an alias.
# TODO: If upstream adds support for Ubuntu, use that instead of Debian as the base suffix: https://github.com/docker-library/python/pull/791
FROM docker.io/library/python:$PY_3_9-bookworm AS upstream-python-3.9
FROM python-core AS python-3.9
ARG PYTHON_INSTALL_LOCATION="$PYENV_ROOT/versions/$PY_3_9"
COPY --from=upstream-python-3.9 --chown=dependabot:dependabot /usr/local/bin $PYTHON_INSTALL_LOCATION/bin
COPY --from=upstream-python-3.9 --chown=dependabot:dependabot /usr/local/include $PYTHON_INSTALL_LOCATION/include
COPY --from=upstream-python-3.9 --chown=dependabot:dependabot /usr/local/lib $PYTHON_INSTALL_LOCATION/lib
# `pip` and other scripts need their shebangs rewritten for the new location
RUN find $PYTHON_INSTALL_LOCATION/bin -type f -exec sed -i "1s|^#!/usr/local/bin/python|#!$PYTHON_INSTALL_LOCATION/bin/python|" {} +
# Ensure pyenv works and it's the python version we expect
RUN PYENV_VERSION=$PY_3_9 pyenv exec python --version | grep "Python $PY_3_9" || exit 1
RUN bash /opt/python/helpers/build $PY_3_9
# This python environment occupies ~0.5 GB and gets used for a fraction of jobs, so store it compressed.
RUN cd $PYENV_ROOT/versions \
&& tar -acf $PY_3_9.tar.zst $PY_3_9

## 3.10
# Docker doesn't support parametrizing `COPY --from:python:$PY_1_23-bookworm`, so work around it using an alias.
# TODO: If upstream adds support for Ubuntu, use that instead of Debian as the base suffix: https://github.com/docker-library/python/pull/791
FROM docker.io/library/python:$PY_3_10-bookworm AS upstream-python-3.10
FROM python-core AS python-3.10
ARG PYTHON_INSTALL_LOCATION="$PYENV_ROOT/versions/$PY_3_10"
COPY --from=upstream-python-3.10 --chown=dependabot:dependabot /usr/local/bin $PYTHON_INSTALL_LOCATION/bin
COPY --from=upstream-python-3.10 --chown=dependabot:dependabot /usr/local/include $PYTHON_INSTALL_LOCATION/include
COPY --from=upstream-python-3.10 --chown=dependabot:dependabot /usr/local/lib $PYTHON_INSTALL_LOCATION/lib
# `pip` and other scripts need their shebangs rewritten for the new location
RUN find $PYTHON_INSTALL_LOCATION/bin -type f -exec sed -i "1s|^#!/usr/local/bin/python|#!$PYTHON_INSTALL_LOCATION/bin/python|" {} +
# Ensure pyenv works and it's the python version we expect
RUN PYENV_VERSION=$PY_3_10 pyenv exec python --version | grep "Python $PY_3_10" || exit 1
RUN bash /opt/python/helpers/build $PY_3_10
# This python environment occupies ~0.5 GB and gets used for a fraction of jobs, so store it compressed.
RUN cd $PYENV_ROOT/versions \
&& tar -acf $PY_3_10.tar.zst $PY_3_10

## 3.11
# Docker doesn't support parametrizing `COPY --from:python:$PY_1_23-bookworm`, so work around it using an alias.
# TODO: If upstream adds support for Ubuntu, use that instead of Debian as the base suffix: https://github.com/docker-library/python/pull/791
FROM docker.io/library/python:$PY_3_11-bookworm AS upstream-python-3.11
FROM python-core AS python-3.11
ARG PYTHON_INSTALL_LOCATION="$PYENV_ROOT/versions/$PY_3_11"
COPY --from=upstream-python-3.11 --chown=dependabot:dependabot /usr/local/bin $PYTHON_INSTALL_LOCATION/bin
COPY --from=upstream-python-3.11 --chown=dependabot:dependabot /usr/local/include $PYTHON_INSTALL_LOCATION/include
COPY --from=upstream-python-3.11 --chown=dependabot:dependabot /usr/local/lib $PYTHON_INSTALL_LOCATION/lib
# `pip` and other scripts need their shebangs rewritten for the new location
RUN find $PYTHON_INSTALL_LOCATION/bin -type f -exec sed -i "1s|^#!/usr/local/bin/python|#!${PYTHON_INSTALL_LOCATION}/bin/python|" {} +
# Ensure pyenv works and it's the python version we expect
RUN PYENV_VERSION=$PY_3_11 pyenv exec python --version | grep "Python $PY_3_11" || exit 1
RUN bash /opt/python/helpers/build $PY_3_11
# This python environment occupies ~0.5 GB and gets used for a fraction of jobs, so store it compressed.
RUN cd $PYENV_ROOT/versions \
&& tar -acf $PY_3_11.tar.zst $PY_3_11

## 3.12
# Docker doesn't support parametrizing `COPY --from:python:$PY_1_23-bookworm`, so work around it using an alias.
# TODO: If upstream adds support for Ubuntu, use that instead of Debian as the base suffix: https://github.com/docker-library/python/pull/791
FROM docker.io/library/python:$PY_3_12-bookworm AS upstream-python-3.12
FROM python-core AS python-3.12
ARG PYTHON_INSTALL_LOCATION="$PYENV_ROOT/versions/$PY_3_12"
COPY --from=upstream-python-3.12 --chown=dependabot:dependabot /usr/local/bin $PYTHON_INSTALL_LOCATION/bin
COPY --from=upstream-python-3.12 --chown=dependabot:dependabot /usr/local/include $PYTHON_INSTALL_LOCATION/include
COPY --from=upstream-python-3.12 --chown=dependabot:dependabot /usr/local/lib $PYTHON_INSTALL_LOCATION/lib
# `pip` and other scripts need their shebangs rewritten for the new location
RUN find $PYTHON_INSTALL_LOCATION/bin -type f -exec sed -i "1s|^#!/usr/local/bin/python|#!${PYTHON_INSTALL_LOCATION}/bin/python|" {} +
# Ensure pyenv works and it's the python version we expect
RUN PYENV_VERSION=$PY_3_12 pyenv exec python --version | grep "Python $PY_3_12" || exit 1
RUN bash /opt/python/helpers/build $PY_3_12
# This python environment occupies ~0.5 GB and gets used for a fraction of jobs, so store it compressed.
RUN cd $PYENV_ROOT/versions \
&& tar -acf $PY_3_12.tar.zst $PY_3_12

## 3.13
# Docker doesn't support parametrizing `COPY --from:python:$PY_1_23-bookworm`, so work around it using an alias.
# TODO: If upstream adds support for Ubuntu, use that instead of Debian as the base suffix: https://github.com/docker-library/python/pull/791
FROM docker.io/library/python:$PY_3_13-bookworm AS upstream-python-3.13
FROM python-core AS python-3.13
ARG PYTHON_INSTALL_LOCATION="$PYENV_ROOT/versions/$PY_3_13"
COPY --from=upstream-python-3.13 --chown=dependabot:dependabot /usr/local/bin $PYTHON_INSTALL_LOCATION/bin
COPY --from=upstream-python-3.13 --chown=dependabot:dependabot /usr/local/include $PYTHON_INSTALL_LOCATION/include
COPY --from=upstream-python-3.13 --chown=dependabot:dependabot /usr/local/lib $PYTHON_INSTALL_LOCATION/lib
# `pip` and other scripts need their shebangs rewritten for the new location
RUN find $PYTHON_INSTALL_LOCATION/bin -type f -exec sed -i "1s|^#!/usr/local/bin/python|#!${PYTHON_INSTALL_LOCATION}/bin/python|" {} +
# Ensure pyenv works and it's the python version we expect
RUN PYENV_VERSION=$PY_3_13 pyenv exec python --version | grep "Python $PY_3_13" || exit 1
RUN bash /opt/python/helpers/build $PY_3_13
# This is the default Python, so no need to tar it

FROM python-core
# Install C-libs needed to build users' Python packages. Please document why each package is needed.
USER root
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
# Used by pycurl
libcurl4-openssl-dev \
# Used by mysqlclient
libmysqlclient-dev \
pkg-config \
# Used by psycopg Postgres Client
libpq-dev \
# Used by python zoneinfo core lib
tzdata \
# Needed to build `gssapi`/`krb5`
libkrb5-dev \
&& rm -rf /var/lib/apt/lists/*

USER dependabot

COPY --chown=dependabot:dependabot uv $DEPENDABOT_HOME/uv
COPY --chown=dependabot:dependabot common $DEPENDABOT_HOME/common
COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater

# Running these steps last means that if the builds in the concurrent stages take longer it doesn't block the pipeline until the end.
COPY --from=python-3.9 $PYENV_ROOT/versions/$PY_3_9.tar.zst $PYENV_ROOT/versions/$PY_3_9.tar.zst
COPY --from=python-3.10 $PYENV_ROOT/versions/$PY_3_10.tar.zst $PYENV_ROOT/versions/$PY_3_10.tar.zst
COPY --from=python-3.11 $PYENV_ROOT/versions/$PY_3_11.tar.zst $PYENV_ROOT/versions/$PY_3_11.tar.zst
COPY --from=python-3.12 $PYENV_ROOT/versions/$PY_3_12.tar.zst $PYENV_ROOT/versions/$PY_3_12.tar.zst
COPY --from=python-3.13 $PYENV_ROOT/versions/ $PYENV_ROOT/versions/

# Copy the output of the build script, it should be identical across Python versions
COPY --from=python-3.13 /opt/python/ /opt/python/

RUN pyenv global $PY_3_13

USER root

# Install Rust
ENV RUSTUP_HOME=/opt/rust \
CARGO_HOME=/opt/rust \
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \
PATH="${PATH}:/opt/rust/bin"
RUN mkdir -p "$RUSTUP_HOME" && chown dependabot:dependabot "$RUSTUP_HOME"

USER dependabot

COPY --from=rust /usr/local/rustup $RUSTUP_HOME
COPY --from=rust /usr/local/cargo $CARGO_HOME

# Configure cargo to use Git CLI so the Git shim works
RUN mkdir -p ~/.cargo && printf "[net]\ngit-fetch-with-cli = true\n" >> ~/.cargo/config.toml

COPY --chown=dependabot:dependabot cargo $DEPENDABOT_HOME/cargo
COPY --chown=dependabot:dependabot common $DEPENDABOT_HOME/common
COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater
32 changes: 32 additions & 0 deletions uv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## `dependabot-uv`

Python `uv` support for [`dependabot-core`][core-repo].

### Updating supported Python version

We rely on `pyenv` to manage Python's versions.

Updating the list of known versions might be tricky, here are the steps:

1. Update the `pyenv` version in the [`Dockerfile`](https://github.com/dependabot/dependabot-core/blob/main/uv/Dockerfile), you may use a commit hash if a new `pyenv` version is not released yet.
2. Update the `pyenv global` version in the `Dockerfile`. We always use the latest (and greatest) Python version.
3. Update the list of known Python versions in [`language_version_manager.rb`](https://github.com/dependabot/dependabot-core/blob/main/uv/lib/dependabot/uv/language_version_manager.rb).
4. Fix any broken tests.

[Example PR](https://github.com/dependabot/dependabot-core/pull/8732) that does all these things for the `./python/` folder.

### Running locally

1. Start a development shell

```shell
$ bin/docker-dev-shell uv
```

2. Run tests

```shell
[dependabot-core-dev] ~ $ cd uv && rspec
```

[core-repo]: https://github.com/dependabot/dependabot-core
39 changes: 39 additions & 0 deletions uv/dependabot-uv.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

Gem::Specification.new do |spec|
common_gemspec =
Bundler.load_gemspec_uncached("../common/dependabot-common.gemspec")

spec.name = "dependabot-uv"
spec.summary = "Provides Dependabot support for Python uv"
spec.description = "Dependabot-uv provides support for bumping Python packages via Dependabot. " \
"If you want support for multiple package managers, you probably want the meta-gem " \
"dependabot-omnibus."

spec.author = common_gemspec.author
spec.email = common_gemspec.email
spec.homepage = common_gemspec.homepage
spec.license = common_gemspec.license

spec.metadata = {
"bug_tracker_uri" => common_gemspec.metadata["bug_tracker_uri"],
"changelog_uri" => common_gemspec.metadata["changelog_uri"]
}

spec.version = common_gemspec.version
spec.required_ruby_version = common_gemspec.required_ruby_version
spec.required_rubygems_version = common_gemspec.required_ruby_version

spec.require_path = "lib"
spec.files = []

spec.add_dependency "dependabot-common", Dependabot::VERSION

common_gemspec.development_dependencies.each do |dep|
spec.add_development_dependency dep.name, *dep.requirement.as_list
end

next unless File.exist?("../.gitignore")

spec.files += `git -C #{__dir__} ls-files lib helpers -z`.split("\x0")
end
34 changes: 34 additions & 0 deletions uv/helpers/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

set -e

if [ -z "$DEPENDABOT_NATIVE_HELPERS_PATH" ]; then
echo "Unable to build, DEPENDABOT_NATIVE_HELPERS_PATH is not set"
exit 1
fi

install_dir="$DEPENDABOT_NATIVE_HELPERS_PATH/python"
mkdir -p "$install_dir"

helpers_dir="$(dirname "${BASH_SOURCE[0]}")"
cp -r \
"$helpers_dir/lib" \
"$helpers_dir/run.py" \
"$helpers_dir/requirements.txt" \
"$install_dir"

cd "$install_dir"
PYENV_VERSION=$1 pyenv exec pip3 --disable-pip-version-check install --use-pep517 -r "requirements.txt"

# Remove the extra objects added during the previous install. Based on
# https://github.com/docker-library/python/blob/master/Dockerfile-linux.template
# And the image docker.io/library/python
find "${PYENV_ROOT:-/usr/local/.pyenv}/versions" -depth \
\( \
\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
\) -exec rm -rf '{}' +

find -L "${PYENV_ROOT:-/usr/local/.pyenv}/versions" -type f \
-name '*.so' \
-exec strip --preserve-dates {} +
Empty file added uv/helpers/lib/__init__.py
Empty file.
36 changes: 36 additions & 0 deletions uv/helpers/lib/hasher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import hashin
import json
import plette
import traceback
from poetry.factory import Factory


def get_dependency_hash(dependency_name, dependency_version, algorithm,
index_url=hashin.DEFAULT_INDEX_URL):
try:
hashes = hashin.get_package_hashes(
dependency_name,
version=dependency_version,
algorithm=algorithm,
index_url=index_url
)
return json.dumps({"result": hashes["hashes"]})
except hashin.PackageNotFoundError as e:
return json.dumps({
"error": repr(e),
"error_class:": e.__class__.__name__,
"trace:": ''.join(traceback.format_stack())
})


def get_pipfile_hash(directory):
with open(directory + '/Pipfile') as f:
pipfile = plette.Pipfile.load(f)

return json.dumps({"result": pipfile.get_hash().value})


def get_pyproject_hash(directory):
p = Factory().create_poetry(directory)

return json.dumps({"result": p.locker._get_content_hash()})
Loading

0 comments on commit a0e76f9

Please sign in to comment.