omdev 0.0.0.dev212__tar.gz → 0.0.0.dev214__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {omdev-0.0.0.dev212/omdev.egg-info → omdev-0.0.0.dev214}/PKG-INFO +2 -2
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/.manifests.json +1 -1
- omdev-0.0.0.dev214/omdev/cc/cdeps.py +71 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cc/cdeps.toml +19 -2
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cc/cli.py +13 -1
- omdev-0.0.0.dev214/omdev/ci/cache.py +147 -0
- omdev-0.0.0.dev214/omdev/ci/ci.py +274 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/cli.py +62 -30
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/compose.py +26 -62
- omdev-0.0.0.dev214/omdev/ci/consts.py +1 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/docker.py +39 -22
- omdev-0.0.0.dev212/omdev/ci/github/cacheapi.py → omdev-0.0.0.dev214/omdev/ci/github/api.py +1 -2
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/github/bootstrap.py +8 -1
- omdev-0.0.0.dev214/omdev/ci/github/cache.py +71 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/github/cli.py +9 -5
- omdev-0.0.0.dev214/omdev/ci/github/client.py +492 -0
- omdev-0.0.0.dev214/omdev/ci/github/env.py +21 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/requirements.py +0 -1
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/shell.py +0 -1
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/utils.py +2 -14
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/git/shallow.py +1 -1
- omdev-0.0.0.dev214/omdev/pyproject/__init__.py +1 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/ci.py +1602 -887
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/interp.py +23 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/pyproject.py +23 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tokens/tokenizert.py +1 -3
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/docker.py +6 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214/omdev.egg-info}/PKG-INFO +2 -2
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev.egg-info/SOURCES.txt +4 -1
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev.egg-info/requires.txt +1 -1
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/pyproject.toml +2 -2
- omdev-0.0.0.dev212/omdev/cc/cdeps.py +0 -38
- omdev-0.0.0.dev212/omdev/ci/cache.py +0 -168
- omdev-0.0.0.dev212/omdev/ci/ci.py +0 -249
- omdev-0.0.0.dev212/omdev/ci/github/cache.py +0 -355
- omdev-0.0.0.dev212/omdev/tools/pawk/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/LICENSE +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/MANIFEST.in +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/README.rst +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/__about__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/gen.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/imports.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/main.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/manifests.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/resources.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/srcfiles.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/strip.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/types.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/amalg/typing.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/antlr/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/antlr/consts.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/antlr/gen.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/bracepy.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/cache.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/contexts.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/currents.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/fns.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/resolvers.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/storage.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/compute/types.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/actions.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/cache.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/consts.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/defaults.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/manifests.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cache/data/specs.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cc/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cc/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_boilerplate.cc +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/LICENSE +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/build_ext.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/options.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/dir_util.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/errors.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/extension.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/file_util.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/modified.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/spawn.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/sysconfig.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/util.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/version.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/build.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/cmake.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/importhook.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/magic.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cexts/scan.py +0 -0
- {omdev-0.0.0.dev212/omdev/interp → omdev-0.0.0.dev214/omdev/ci}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ci/__main__.py +0 -0
- {omdev-0.0.0.dev212/omdev/ci → omdev-0.0.0.dev214/omdev/ci/github}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/classdot.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/_pathhack.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/clicli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/install.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/main.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/managers.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cli/types.py +0 -0
- {omdev-0.0.0.dev212/omdev/ci/github → omdev-0.0.0.dev214/omdev/clipboard}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/clipboard/clipboard.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/clipboard/darwin_cf.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/clipboard/linux_x11.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/cmake.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/findimports.py +0 -0
- {omdev-0.0.0.dev212/omdev/clipboard → omdev-0.0.0.dev214/omdev/git}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/git/revisions.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/git/status.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/imgur.py +0 -0
- {omdev-0.0.0.dev212/omdev/manifests → omdev-0.0.0.dev214/omdev/interp}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/cli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/default.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/inject.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/inspect.py +0 -0
- {omdev-0.0.0.dev212/omdev/git → omdev-0.0.0.dev214/omdev/interp/providers}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/providers/base.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/providers/inject.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/providers/running.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/providers/standalone.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/providers/system.py +0 -0
- {omdev-0.0.0.dev212/omdev/interp/providers → omdev-0.0.0.dev214/omdev/interp/pyenv}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/pyenv/inject.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/pyenv/install.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/pyenv/provider.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/pyenv/pyenv.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/resolvers.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/types.py +0 -0
- {omdev-0.0.0.dev212/omdev/interp/pyenv → omdev-0.0.0.dev214/omdev/interp/uv}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/uv/inject.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/uv/provider.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/uv/uv.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/interp/venvs.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/cli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/find.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/magic.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/prepare.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/magic/styles.py +0 -0
- {omdev-0.0.0.dev212/omdev/pyproject → omdev-0.0.0.dev214/omdev/manifests}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/manifests/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/manifests/build.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/manifests/main.py +0 -0
- {omdev-0.0.0.dev212/omdev/interp/uv → omdev-0.0.0.dev214/omdev/mypy}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/mypy/debug.py +0 -0
- {omdev-0.0.0.dev212/omdev/mypy → omdev-0.0.0.dev214/omdev/packaging}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/packaging/marshal.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/packaging/names.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/packaging/requires.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/packaging/specifiers.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/packaging/versions.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pip.py +0 -0
- {omdev-0.0.0.dev212/omdev/packaging → omdev-0.0.0.dev214/omdev/precheck}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/base.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/git.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/lite.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/main.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/manifests.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/precheck/scripts.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ptk/__init__.py +0 -0
- {omdev-0.0.0.dev212/omdev/precheck → omdev-0.0.0.dev214/omdev/ptk/apps}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/ptk/apps/ncdu.py +0 -0
- {omdev-0.0.0.dev212/omdev/ptk/apps → omdev-0.0.0.dev214/omdev/pycharm}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pycharm/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pycharm/cli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/cexts.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/cli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/configs.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/inject.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/pkg.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/reqs.py +0 -0
- {omdev-0.0.0.dev212/omdev/pycharm → omdev-0.0.0.dev214/omdev/pyproject/resources}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/resources/docker-dev.sh +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/resources/python.sh +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/pyproject/venvs.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/revisions.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/bumpversion.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/execrss.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/exectime.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/importtrace.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/slowcat.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/scripts/tmpexec.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/secrets.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tagstrings.py +0 -0
- {omdev-0.0.0.dev212/omdev/pyproject/resources → omdev-0.0.0.dev214/omdev/tokens}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tokens/all.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tokens/utils.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/cloc.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/doc.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/git.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/importscan.py +0 -0
- {omdev-0.0.0.dev212/omdev/tokens → omdev-0.0.0.dev214/omdev/tools/json}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/cli.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/formats.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/io.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/parsing.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/processing.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/json/rendering.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/linehisto.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/mkenv.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/mkrelimp.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/notebook.py +0 -0
- {omdev-0.0.0.dev212/omdev/tools/json → omdev-0.0.0.dev214/omdev/tools/pawk}/__init__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/pawk/__main__.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/pawk/pawk.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/pip.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/prof.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/qr.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/tools/sqlrepl.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev/wheelfile.py +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev.egg-info/dependency_links.txt +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev.egg-info/entry_points.txt +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/omdev.egg-info/top_level.txt +0 -0
- {omdev-0.0.0.dev212 → omdev-0.0.0.dev214}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: omdev
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev214
|
|
4
4
|
Summary: omdev
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Classifier: Operating System :: POSIX
|
|
13
13
|
Requires-Python: >=3.12
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: omlish==0.0.0.
|
|
15
|
+
Requires-Dist: omlish==0.0.0.dev214
|
|
16
16
|
Provides-Extra: all
|
|
17
17
|
Requires-Dist: black~=24.10; extra == "all"
|
|
18
18
|
Requires-Dist: pycparser~=2.22; extra == "all"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import tomllib
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from omlish import cached
|
|
6
|
+
from omlish import lang
|
|
7
|
+
from omlish import marshal as msh
|
|
8
|
+
from omlish.configs import all as configs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dc.dataclass(frozen=True)
|
|
12
|
+
class Cdep:
|
|
13
|
+
@dc.dataclass(frozen=True)
|
|
14
|
+
class Git:
|
|
15
|
+
url: str
|
|
16
|
+
rev: str
|
|
17
|
+
|
|
18
|
+
subtrees: ta.Sequence[str] | None = None
|
|
19
|
+
|
|
20
|
+
git: Git
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
|
|
24
|
+
sources: ta.Sequence[str] | None = None
|
|
25
|
+
include: ta.Sequence[str] | None = None
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
@dc.dataclass(frozen=True)
|
|
30
|
+
class Cmake:
|
|
31
|
+
@dc.dataclass(frozen=True)
|
|
32
|
+
class FetchContent:
|
|
33
|
+
url: str | None = None
|
|
34
|
+
|
|
35
|
+
@dc.dataclass(frozen=True)
|
|
36
|
+
class Git:
|
|
37
|
+
repository: str | None = None
|
|
38
|
+
tag: str | None = None
|
|
39
|
+
|
|
40
|
+
git: Git | None = None
|
|
41
|
+
|
|
42
|
+
fetch_content: FetchContent | None = None
|
|
43
|
+
|
|
44
|
+
cmake: Cmake | None = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def process_marshaled_cdep(obj: ta.Any) -> ta.Any:
|
|
48
|
+
obj = configs.processing.matched_rewrite(
|
|
49
|
+
lambda s: s if isinstance(s, str) else ''.join(s),
|
|
50
|
+
obj,
|
|
51
|
+
('sources', None),
|
|
52
|
+
('include', None),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return obj
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@cached.function
|
|
59
|
+
def load_cdeps() -> ta.Mapping[str, Cdep]:
|
|
60
|
+
src = lang.get_relative_resources(globals=globals())['cdeps.toml'].read_text()
|
|
61
|
+
dct = tomllib.loads(src)
|
|
62
|
+
|
|
63
|
+
dct = {
|
|
64
|
+
**dct,
|
|
65
|
+
'deps': {
|
|
66
|
+
k: process_marshaled_cdep(v)
|
|
67
|
+
for k, v in dct.get('deps', {}).items()
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return msh.unmarshal(dct.get('deps', {}), ta.Mapping[str, Cdep]) # type: ignore
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
[deps.httplib]
|
|
2
|
+
include = ['.']
|
|
3
|
+
|
|
4
|
+
[deps.httplib.git]
|
|
5
|
+
url = 'https://github.com/yhirose/cpp-httplib'
|
|
6
|
+
rev = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
|
|
7
|
+
subtrees = ['httplib.h']
|
|
8
|
+
|
|
9
|
+
[deps.httplib.cmake.fetch_content.git]
|
|
10
|
+
repository = 'https://github.com/yhirose/cpp-httplib.git'
|
|
11
|
+
tag = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
|
|
1
16
|
[deps.json]
|
|
2
17
|
include = ['single_include']
|
|
3
18
|
|
|
@@ -6,8 +21,9 @@ url = 'https://github.com/nlohmann/json'
|
|
|
6
21
|
rev = '9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03'
|
|
7
22
|
subtrees = ['single_include']
|
|
8
23
|
|
|
9
|
-
[deps.json.cmake]
|
|
10
|
-
|
|
24
|
+
[deps.json.cmake.fetch_content]
|
|
25
|
+
url = 'https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz'
|
|
26
|
+
|
|
11
27
|
|
|
12
28
|
#
|
|
13
29
|
|
|
@@ -19,6 +35,7 @@ url = 'https://github.com/pybind/pybind11'
|
|
|
19
35
|
rev = '945e251a6ce7273058b36214d94415e9c9530b8e'
|
|
20
36
|
subtrees = ['include']
|
|
21
37
|
|
|
38
|
+
|
|
22
39
|
#
|
|
23
40
|
|
|
24
41
|
[deps.ut]
|
|
@@ -18,6 +18,8 @@ Freestanding options:
|
|
|
18
18
|
TODO:
|
|
19
19
|
- cext interop
|
|
20
20
|
- gen cmake
|
|
21
|
+
- fix CFLAGS/CCFLAGS/CPPFLAGS/CXXFLAGS
|
|
22
|
+
- jit-gen cmake mode? multi-src builds
|
|
21
23
|
"""
|
|
22
24
|
import os
|
|
23
25
|
import shlex
|
|
@@ -35,6 +37,7 @@ from .. import magic
|
|
|
35
37
|
from ..cache import data as dcache
|
|
36
38
|
from .cdeps import Cdep
|
|
37
39
|
from .cdeps import load_cdeps
|
|
40
|
+
from .cdeps import process_marshaled_cdep
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
class Cli(ap.Cli):
|
|
@@ -64,16 +67,22 @@ class Cli(ap.Cli):
|
|
|
64
67
|
if src_magic.key == '@omlish-cdeps':
|
|
65
68
|
for dep in check.isinstance(src_magic.prepared, ta.Sequence):
|
|
66
69
|
if isinstance(dep, ta.Mapping):
|
|
67
|
-
dep =
|
|
70
|
+
dep = process_marshaled_cdep(dep)
|
|
71
|
+
dep = msh.unmarshal(dep, Cdep)
|
|
68
72
|
else:
|
|
69
73
|
dep = load_cdeps()[check.isinstance(dep, str)]
|
|
70
74
|
|
|
75
|
+
if dep.sources:
|
|
76
|
+
# TODO
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
|
|
71
79
|
dep_spec = dcache.GitSpec(
|
|
72
80
|
url=dep.git.url,
|
|
73
81
|
rev=dep.git.rev,
|
|
74
82
|
subtrees=dep.git.subtrees,
|
|
75
83
|
)
|
|
76
84
|
dep_dir = dcache.default().get(dep_spec)
|
|
85
|
+
|
|
77
86
|
for dep_inc in dep.include or []:
|
|
78
87
|
inc_dir = os.path.join(dep_dir, dep_inc)
|
|
79
88
|
check.state(os.path.isdir(inc_dir))
|
|
@@ -96,6 +105,9 @@ class Cli(ap.Cli):
|
|
|
96
105
|
if cflags := os.environ.get('CFLAGS'):
|
|
97
106
|
sh_parts.append(cflags) # Explicitly shell-unquoted
|
|
98
107
|
|
|
108
|
+
if ldflags := os.environ.get('LDFLAGS'):
|
|
109
|
+
sh_parts.append(ldflags) # Explicitly shell-unquoted
|
|
110
|
+
|
|
99
111
|
sh_parts.extend([
|
|
100
112
|
'-std=c++20',
|
|
101
113
|
shlex.quote(os.path.abspath(src_file)),
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
|
2
|
+
import abc
|
|
3
|
+
import os.path
|
|
4
|
+
import shutil
|
|
5
|
+
import typing as ta
|
|
6
|
+
|
|
7
|
+
from omlish.lite.cached import cached_nullary
|
|
8
|
+
from omlish.lite.check import check
|
|
9
|
+
from omlish.lite.logs import log
|
|
10
|
+
|
|
11
|
+
from .consts import CI_CACHE_VERSION
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
class FileCache(abc.ABC):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
version: int = CI_CACHE_VERSION,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
|
|
26
|
+
check.isinstance(version, int)
|
|
27
|
+
check.arg(version >= 0)
|
|
28
|
+
self._version = version
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def version(self) -> int:
|
|
32
|
+
return self._version
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
|
|
36
|
+
@abc.abstractmethod
|
|
37
|
+
def get_file(self, key: str) -> ta.Awaitable[ta.Optional[str]]:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
@abc.abstractmethod
|
|
41
|
+
def put_file(
|
|
42
|
+
self,
|
|
43
|
+
key: str,
|
|
44
|
+
file_path: str,
|
|
45
|
+
*,
|
|
46
|
+
steal: bool = False,
|
|
47
|
+
) -> ta.Awaitable[str]:
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DirectoryFileCache(FileCache):
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
dir: str, # noqa
|
|
58
|
+
*,
|
|
59
|
+
no_create: bool = False,
|
|
60
|
+
no_purge: bool = False,
|
|
61
|
+
**kwargs: ta.Any,
|
|
62
|
+
) -> None: # noqa
|
|
63
|
+
super().__init__(**kwargs)
|
|
64
|
+
|
|
65
|
+
self._dir = dir
|
|
66
|
+
self._no_create = no_create
|
|
67
|
+
self._no_purge = no_purge
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
|
|
71
|
+
VERSION_FILE_NAME = '.ci-cache-version'
|
|
72
|
+
|
|
73
|
+
@cached_nullary
|
|
74
|
+
def setup_dir(self) -> None:
|
|
75
|
+
version_file = os.path.join(self._dir, self.VERSION_FILE_NAME)
|
|
76
|
+
|
|
77
|
+
if self._no_create:
|
|
78
|
+
check.state(os.path.isdir(self._dir))
|
|
79
|
+
|
|
80
|
+
elif not os.path.isdir(self._dir):
|
|
81
|
+
os.makedirs(self._dir)
|
|
82
|
+
with open(version_file, 'w') as f:
|
|
83
|
+
f.write(str(self._version))
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
with open(version_file) as f:
|
|
87
|
+
dir_version = int(f.read().strip())
|
|
88
|
+
|
|
89
|
+
if dir_version == self._version:
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
if self._no_purge:
|
|
93
|
+
raise RuntimeError(f'{dir_version=} != {self._version=}')
|
|
94
|
+
|
|
95
|
+
dirs = [n for n in sorted(os.listdir(self._dir)) if os.path.isdir(os.path.join(self._dir, n))]
|
|
96
|
+
if dirs:
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
f'Refusing to remove stale cache dir {self._dir!r} '
|
|
99
|
+
f'due to present directories: {", ".join(dirs)}',
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
for n in sorted(os.listdir(self._dir)):
|
|
103
|
+
if n.startswith('.'):
|
|
104
|
+
continue
|
|
105
|
+
fp = os.path.join(self._dir, n)
|
|
106
|
+
check.state(os.path.isfile(fp))
|
|
107
|
+
log.debug('Purging stale cache file: %s', fp)
|
|
108
|
+
os.unlink(fp)
|
|
109
|
+
|
|
110
|
+
os.unlink(version_file)
|
|
111
|
+
|
|
112
|
+
with open(version_file, 'w') as f:
|
|
113
|
+
f.write(str(self._version))
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
|
|
117
|
+
def get_cache_file_path(
|
|
118
|
+
self,
|
|
119
|
+
key: str,
|
|
120
|
+
) -> str:
|
|
121
|
+
self.setup_dir()
|
|
122
|
+
return os.path.join(self._dir, key)
|
|
123
|
+
|
|
124
|
+
def format_incomplete_file(self, f: str) -> str:
|
|
125
|
+
return os.path.join(os.path.dirname(f), f'_{os.path.basename(f)}.incomplete')
|
|
126
|
+
|
|
127
|
+
#
|
|
128
|
+
|
|
129
|
+
async def get_file(self, key: str) -> ta.Optional[str]:
|
|
130
|
+
cache_file_path = self.get_cache_file_path(key)
|
|
131
|
+
if not os.path.exists(cache_file_path):
|
|
132
|
+
return None
|
|
133
|
+
return cache_file_path
|
|
134
|
+
|
|
135
|
+
async def put_file(
|
|
136
|
+
self,
|
|
137
|
+
key: str,
|
|
138
|
+
file_path: str,
|
|
139
|
+
*,
|
|
140
|
+
steal: bool = False,
|
|
141
|
+
) -> str:
|
|
142
|
+
cache_file_path = self.get_cache_file_path(key)
|
|
143
|
+
if steal:
|
|
144
|
+
shutil.move(file_path, cache_file_path)
|
|
145
|
+
else:
|
|
146
|
+
shutil.copyfile(file_path, cache_file_path)
|
|
147
|
+
return cache_file_path
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
|
2
|
+
import dataclasses as dc
|
|
3
|
+
import os.path
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
from omlish.lite.cached import async_cached_nullary
|
|
7
|
+
from omlish.lite.cached import cached_nullary
|
|
8
|
+
from omlish.lite.check import check
|
|
9
|
+
from omlish.lite.contextmanagers import AsyncExitStacked
|
|
10
|
+
from omlish.os.temp import temp_file_context
|
|
11
|
+
|
|
12
|
+
from .cache import FileCache
|
|
13
|
+
from .compose import DockerComposeRun
|
|
14
|
+
from .compose import get_compose_service_dependencies
|
|
15
|
+
from .docker import build_docker_file_hash
|
|
16
|
+
from .docker import build_docker_image
|
|
17
|
+
from .docker import is_docker_image_present
|
|
18
|
+
from .docker import load_docker_tar_cmd
|
|
19
|
+
from .docker import pull_docker_image
|
|
20
|
+
from .docker import save_docker_tar_cmd
|
|
21
|
+
from .docker import tag_docker_image
|
|
22
|
+
from .requirements import build_requirements_hash
|
|
23
|
+
from .shell import ShellCmd
|
|
24
|
+
from .utils import log_timing_context
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Ci(AsyncExitStacked):
|
|
28
|
+
KEY_HASH_LEN = 16
|
|
29
|
+
|
|
30
|
+
@dc.dataclass(frozen=True)
|
|
31
|
+
class Config:
|
|
32
|
+
project_dir: str
|
|
33
|
+
|
|
34
|
+
docker_file: str
|
|
35
|
+
|
|
36
|
+
compose_file: str
|
|
37
|
+
service: str
|
|
38
|
+
|
|
39
|
+
cmd: ShellCmd
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
|
|
43
|
+
requirements_txts: ta.Optional[ta.Sequence[str]] = None
|
|
44
|
+
|
|
45
|
+
always_pull: bool = False
|
|
46
|
+
always_build: bool = False
|
|
47
|
+
|
|
48
|
+
no_dependencies: bool = False
|
|
49
|
+
|
|
50
|
+
run_options: ta.Optional[ta.Sequence[str]] = None
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
|
|
54
|
+
def __post_init__(self) -> None:
|
|
55
|
+
check.not_isinstance(self.requirements_txts, str)
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
cfg: Config,
|
|
60
|
+
*,
|
|
61
|
+
file_cache: ta.Optional[FileCache] = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
super().__init__()
|
|
64
|
+
|
|
65
|
+
self._cfg = cfg
|
|
66
|
+
self._file_cache = file_cache
|
|
67
|
+
|
|
68
|
+
#
|
|
69
|
+
|
|
70
|
+
async def _load_docker_image(self, image: str) -> None:
|
|
71
|
+
if not self._cfg.always_pull and (await is_docker_image_present(image)):
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
dep_suffix = image
|
|
75
|
+
for c in '/:.-_':
|
|
76
|
+
dep_suffix = dep_suffix.replace(c, '-')
|
|
77
|
+
|
|
78
|
+
cache_key = f'docker-{dep_suffix}'
|
|
79
|
+
if (await self._load_cache_docker_image(cache_key)) is not None:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
await pull_docker_image(image)
|
|
83
|
+
|
|
84
|
+
await self._save_cache_docker_image(cache_key, image)
|
|
85
|
+
|
|
86
|
+
async def load_docker_image(self, image: str) -> None:
|
|
87
|
+
with log_timing_context(f'Load docker image: {image}'):
|
|
88
|
+
await self._load_docker_image(image)
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
|
|
92
|
+
async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
|
93
|
+
if self._file_cache is None:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
cache_file = await self._file_cache.get_file(key)
|
|
97
|
+
if cache_file is None:
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
get_cache_cmd = ShellCmd(f'cat {cache_file} | zstd -cd --long')
|
|
101
|
+
|
|
102
|
+
return await load_docker_tar_cmd(get_cache_cmd)
|
|
103
|
+
|
|
104
|
+
async def _save_cache_docker_image(self, key: str, image: str) -> None:
|
|
105
|
+
if self._file_cache is None:
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
with temp_file_context() as tmp_file:
|
|
109
|
+
write_tmp_cmd = ShellCmd(f'zstd > {tmp_file}')
|
|
110
|
+
|
|
111
|
+
await save_docker_tar_cmd(image, write_tmp_cmd)
|
|
112
|
+
|
|
113
|
+
await self._file_cache.put_file(key, tmp_file, steal=True)
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
|
|
117
|
+
async def _resolve_docker_image(
|
|
118
|
+
self,
|
|
119
|
+
cache_key: str,
|
|
120
|
+
build_and_tag: ta.Callable[[str], ta.Awaitable[str]],
|
|
121
|
+
) -> str:
|
|
122
|
+
image_tag = f'{self._cfg.service}:{cache_key}'
|
|
123
|
+
|
|
124
|
+
if not self._cfg.always_build and (await is_docker_image_present(image_tag)):
|
|
125
|
+
return image_tag
|
|
126
|
+
|
|
127
|
+
if (cache_image_id := await self._load_cache_docker_image(cache_key)) is not None:
|
|
128
|
+
await tag_docker_image(
|
|
129
|
+
cache_image_id,
|
|
130
|
+
image_tag,
|
|
131
|
+
)
|
|
132
|
+
return image_tag
|
|
133
|
+
|
|
134
|
+
image_id = await build_and_tag(image_tag)
|
|
135
|
+
|
|
136
|
+
await self._save_cache_docker_image(cache_key, image_id)
|
|
137
|
+
|
|
138
|
+
return image_tag
|
|
139
|
+
|
|
140
|
+
#
|
|
141
|
+
|
|
142
|
+
@cached_nullary
|
|
143
|
+
def docker_file_hash(self) -> str:
|
|
144
|
+
return build_docker_file_hash(self._cfg.docker_file)[:self.KEY_HASH_LEN]
|
|
145
|
+
|
|
146
|
+
async def _resolve_ci_base_image(self) -> str:
|
|
147
|
+
async def build_and_tag(image_tag: str) -> str:
|
|
148
|
+
return await build_docker_image(
|
|
149
|
+
self._cfg.docker_file,
|
|
150
|
+
tag=image_tag,
|
|
151
|
+
cwd=self._cfg.project_dir,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
cache_key = f'ci-base-{self.docker_file_hash()}'
|
|
155
|
+
|
|
156
|
+
return await self._resolve_docker_image(cache_key, build_and_tag)
|
|
157
|
+
|
|
158
|
+
@async_cached_nullary
|
|
159
|
+
async def resolve_ci_base_image(self) -> str:
|
|
160
|
+
with log_timing_context('Resolve ci base image') as ltc:
|
|
161
|
+
image_id = await self._resolve_ci_base_image()
|
|
162
|
+
ltc.set_description(f'Resolve ci base image: {image_id}')
|
|
163
|
+
return image_id
|
|
164
|
+
|
|
165
|
+
#
|
|
166
|
+
|
|
167
|
+
@cached_nullary
|
|
168
|
+
def requirements_txts(self) -> ta.Sequence[str]:
|
|
169
|
+
return [
|
|
170
|
+
os.path.join(self._cfg.project_dir, rf)
|
|
171
|
+
for rf in check.not_none(self._cfg.requirements_txts)
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
@cached_nullary
|
|
175
|
+
def requirements_hash(self) -> str:
|
|
176
|
+
return build_requirements_hash(self.requirements_txts())[:self.KEY_HASH_LEN]
|
|
177
|
+
|
|
178
|
+
async def _resolve_ci_image(self) -> str:
|
|
179
|
+
async def build_and_tag(image_tag: str) -> str:
|
|
180
|
+
base_image = await self.resolve_ci_base_image()
|
|
181
|
+
|
|
182
|
+
setup_cmds = [
|
|
183
|
+
' '.join([
|
|
184
|
+
'pip install',
|
|
185
|
+
'--no-cache-dir',
|
|
186
|
+
'--root-user-action ignore',
|
|
187
|
+
'uv',
|
|
188
|
+
]),
|
|
189
|
+
' '.join([
|
|
190
|
+
'uv pip install',
|
|
191
|
+
'--no-cache',
|
|
192
|
+
'--index-strategy unsafe-best-match',
|
|
193
|
+
'--system',
|
|
194
|
+
*[f'-r /project/{rf}' for rf in self._cfg.requirements_txts or []],
|
|
195
|
+
]),
|
|
196
|
+
]
|
|
197
|
+
setup_cmd = ' && '.join(setup_cmds)
|
|
198
|
+
|
|
199
|
+
docker_file_lines = [
|
|
200
|
+
f'FROM {base_image}',
|
|
201
|
+
'RUN mkdir /project',
|
|
202
|
+
*[f'COPY {rf} /project/{rf}' for rf in self._cfg.requirements_txts or []],
|
|
203
|
+
f'RUN {setup_cmd}',
|
|
204
|
+
'RUN rm /project/*',
|
|
205
|
+
'WORKDIR /project',
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
with temp_file_context() as docker_file:
|
|
209
|
+
with open(docker_file, 'w') as f: # noqa
|
|
210
|
+
f.write('\n'.join(docker_file_lines))
|
|
211
|
+
|
|
212
|
+
return await build_docker_image(
|
|
213
|
+
docker_file,
|
|
214
|
+
tag=image_tag,
|
|
215
|
+
cwd=self._cfg.project_dir,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
cache_key = f'ci-{self.docker_file_hash()}-{self.requirements_hash()}'
|
|
219
|
+
|
|
220
|
+
return await self._resolve_docker_image(cache_key, build_and_tag)
|
|
221
|
+
|
|
222
|
+
@async_cached_nullary
|
|
223
|
+
async def resolve_ci_image(self) -> str:
|
|
224
|
+
with log_timing_context('Resolve ci image') as ltc:
|
|
225
|
+
image_id = await self._resolve_ci_image()
|
|
226
|
+
ltc.set_description(f'Resolve ci image: {image_id}')
|
|
227
|
+
return image_id
|
|
228
|
+
|
|
229
|
+
#
|
|
230
|
+
|
|
231
|
+
@async_cached_nullary
|
|
232
|
+
async def load_dependencies(self) -> None:
|
|
233
|
+
deps = get_compose_service_dependencies(
|
|
234
|
+
self._cfg.compose_file,
|
|
235
|
+
self._cfg.service,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
for dep_image in deps.values():
|
|
239
|
+
await self.load_docker_image(dep_image)
|
|
240
|
+
|
|
241
|
+
#
|
|
242
|
+
|
|
243
|
+
async def _run_compose_(self) -> None:
|
|
244
|
+
async with DockerComposeRun(DockerComposeRun.Config(
|
|
245
|
+
compose_file=self._cfg.compose_file,
|
|
246
|
+
service=self._cfg.service,
|
|
247
|
+
|
|
248
|
+
image=await self.resolve_ci_image(),
|
|
249
|
+
|
|
250
|
+
cmd=self._cfg.cmd,
|
|
251
|
+
|
|
252
|
+
run_options=[
|
|
253
|
+
'-v', f'{os.path.abspath(self._cfg.project_dir)}:/project',
|
|
254
|
+
*(self._cfg.run_options or []),
|
|
255
|
+
],
|
|
256
|
+
|
|
257
|
+
cwd=self._cfg.project_dir,
|
|
258
|
+
|
|
259
|
+
no_dependencies=self._cfg.no_dependencies,
|
|
260
|
+
)) as ci_compose_run:
|
|
261
|
+
await ci_compose_run.run()
|
|
262
|
+
|
|
263
|
+
async def _run_compose(self) -> None:
|
|
264
|
+
with log_timing_context('Run compose'):
|
|
265
|
+
await self._run_compose_()
|
|
266
|
+
|
|
267
|
+
#
|
|
268
|
+
|
|
269
|
+
async def run(self) -> None:
|
|
270
|
+
await self.resolve_ci_image()
|
|
271
|
+
|
|
272
|
+
await self.load_dependencies()
|
|
273
|
+
|
|
274
|
+
await self._run_compose()
|