omdev 0.0.0.dev213__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.dev213/omdev.egg-info → omdev-0.0.0.dev214}/PKG-INFO +2 -2
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/.manifests.json +1 -1
- omdev-0.0.0.dev214/omdev/ci/cache.py +147 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/ci.py +120 -118
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/cli.py +50 -24
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/compose.py +1 -8
- omdev-0.0.0.dev214/omdev/ci/consts.py +1 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/docker.py +4 -6
- omdev-0.0.0.dev213/omdev/ci/github/cacheapi.py → omdev-0.0.0.dev214/omdev/ci/github/api.py +0 -1
- {omdev-0.0.0.dev213 → 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.dev213 → 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.dev213 → omdev-0.0.0.dev214}/omdev/ci/requirements.py +0 -1
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/shell.py +0 -1
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/utils.py +2 -14
- omdev-0.0.0.dev214/omdev/pyproject/__init__.py +1 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/ci.py +1149 -922
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/docker.py +6 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214/omdev.egg-info}/PKG-INFO +2 -2
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/SOURCES.txt +4 -2
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/requires.txt +1 -1
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/pyproject.toml +2 -2
- omdev-0.0.0.dev213/omdev/ci/cache.py +0 -168
- omdev-0.0.0.dev213/omdev/ci/github/cache.py +0 -324
- omdev-0.0.0.dev213/omdev/ci/github/curl.py +0 -209
- omdev-0.0.0.dev213/omdev/tools/pawk/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/LICENSE +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/MANIFEST.in +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/README.rst +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/__about__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/gen.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/imports.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/main.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/manifests.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/resources.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/srcfiles.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/strip.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/types.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/amalg/typing.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/consts.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/antlr/gen.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/bracepy.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/cache.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/contexts.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/currents.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/fns.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/resolvers.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/storage.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/compute/types.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/actions.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/cache.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/consts.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/defaults.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/manifests.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cache/data/specs.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cdeps.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cdeps.toml +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cc/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_boilerplate.cc +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/LICENSE +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/build_ext.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/options.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/dir_util.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/errors.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/extension.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/file_util.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/modified.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/spawn.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/sysconfig.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/util.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/_distutils/version.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/build.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/cmake.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/importhook.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/magic.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cexts/scan.py +0 -0
- {omdev-0.0.0.dev213/omdev/interp → omdev-0.0.0.dev214/omdev/ci}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ci/__main__.py +0 -0
- {omdev-0.0.0.dev213/omdev/ci → omdev-0.0.0.dev214/omdev/ci/github}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/classdot.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/_pathhack.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/clicli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/install.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/main.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/managers.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cli/types.py +0 -0
- {omdev-0.0.0.dev213/omdev/ci/github → omdev-0.0.0.dev214/omdev/clipboard}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/clipboard.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/darwin_cf.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/clipboard/linux_x11.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/cmake.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/findimports.py +0 -0
- {omdev-0.0.0.dev213/omdev/clipboard → omdev-0.0.0.dev214/omdev/git}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/revisions.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/shallow.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/git/status.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/imgur.py +0 -0
- {omdev-0.0.0.dev213/omdev/manifests → omdev-0.0.0.dev214/omdev/interp}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/default.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/inject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/inspect.py +0 -0
- {omdev-0.0.0.dev213/omdev/git → omdev-0.0.0.dev214/omdev/interp/providers}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/base.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/inject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/running.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/standalone.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/providers/system.py +0 -0
- {omdev-0.0.0.dev213/omdev/interp/providers → omdev-0.0.0.dev214/omdev/interp/pyenv}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/inject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/install.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/provider.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/pyenv/pyenv.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/resolvers.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/types.py +0 -0
- {omdev-0.0.0.dev213/omdev/interp/pyenv → omdev-0.0.0.dev214/omdev/interp/uv}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/inject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/provider.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/uv/uv.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/interp/venvs.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/find.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/magic.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/prepare.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/magic/styles.py +0 -0
- {omdev-0.0.0.dev213/omdev/pyproject → omdev-0.0.0.dev214/omdev/manifests}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/build.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/manifests/main.py +0 -0
- {omdev-0.0.0.dev213/omdev/interp/uv → omdev-0.0.0.dev214/omdev/mypy}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/mypy/debug.py +0 -0
- {omdev-0.0.0.dev213/omdev/mypy → omdev-0.0.0.dev214/omdev/packaging}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/marshal.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/names.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/requires.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/specifiers.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/packaging/versions.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pip.py +0 -0
- {omdev-0.0.0.dev213/omdev/packaging → omdev-0.0.0.dev214/omdev/precheck}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/base.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/git.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/lite.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/main.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/manifests.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/precheck/scripts.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ptk/__init__.py +0 -0
- {omdev-0.0.0.dev213/omdev/precheck → omdev-0.0.0.dev214/omdev/ptk/apps}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/ptk/apps/ncdu.py +0 -0
- {omdev-0.0.0.dev213/omdev/ptk/apps → omdev-0.0.0.dev214/omdev/pycharm}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pycharm/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pycharm/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/cexts.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/configs.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/inject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/pkg.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/reqs.py +0 -0
- {omdev-0.0.0.dev213/omdev/pycharm → omdev-0.0.0.dev214/omdev/pyproject/resources}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/resources/docker-dev.sh +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/resources/python.sh +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/pyproject/venvs.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/revisions.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/bumpversion.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/execrss.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/exectime.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/importtrace.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/interp.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/pyproject.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/slowcat.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/scripts/tmpexec.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/secrets.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tagstrings.py +0 -0
- {omdev-0.0.0.dev213/omdev/pyproject/resources → omdev-0.0.0.dev214/omdev/tokens}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/all.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/tokenizert.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tokens/utils.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/cloc.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/doc.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/git.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/importscan.py +0 -0
- {omdev-0.0.0.dev213/omdev/tokens → omdev-0.0.0.dev214/omdev/tools/json}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/cli.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/formats.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/io.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/parsing.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/processing.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/json/rendering.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/linehisto.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/mkenv.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/mkrelimp.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/notebook.py +0 -0
- {omdev-0.0.0.dev213/omdev/tools/json → omdev-0.0.0.dev214/omdev/tools/pawk}/__init__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pawk/__main__.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pawk/pawk.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/pip.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/prof.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/qr.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/tools/sqlrepl.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev/wheelfile.py +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/dependency_links.txt +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/entry_points.txt +0 -0
- {omdev-0.0.0.dev213 → omdev-0.0.0.dev214}/omdev.egg-info/top_level.txt +0 -0
- {omdev-0.0.0.dev213 → 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,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
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
|
2
|
-
# @omlish-lite
|
|
3
2
|
import dataclasses as dc
|
|
4
3
|
import os.path
|
|
5
|
-
import shutil
|
|
6
|
-
import tarfile
|
|
7
|
-
import tempfile
|
|
8
4
|
import typing as ta
|
|
9
5
|
|
|
10
6
|
from omlish.lite.cached import async_cached_nullary
|
|
11
7
|
from omlish.lite.cached import cached_nullary
|
|
12
8
|
from omlish.lite.check import check
|
|
13
9
|
from omlish.lite.contextmanagers import AsyncExitStacked
|
|
14
|
-
from omlish.
|
|
10
|
+
from omlish.os.temp import temp_file_context
|
|
15
11
|
|
|
16
12
|
from .cache import FileCache
|
|
17
|
-
from .cache import ShellCache
|
|
18
13
|
from .compose import DockerComposeRun
|
|
19
14
|
from .compose import get_compose_service_dependencies
|
|
20
15
|
from .docker import build_docker_file_hash
|
|
@@ -25,13 +20,12 @@ from .docker import pull_docker_image
|
|
|
25
20
|
from .docker import save_docker_tar_cmd
|
|
26
21
|
from .docker import tag_docker_image
|
|
27
22
|
from .requirements import build_requirements_hash
|
|
28
|
-
from .requirements import download_requirements
|
|
29
23
|
from .shell import ShellCmd
|
|
30
24
|
from .utils import log_timing_context
|
|
31
25
|
|
|
32
26
|
|
|
33
27
|
class Ci(AsyncExitStacked):
|
|
34
|
-
|
|
28
|
+
KEY_HASH_LEN = 16
|
|
35
29
|
|
|
36
30
|
@dc.dataclass(frozen=True)
|
|
37
31
|
class Config:
|
|
@@ -44,6 +38,8 @@ class Ci(AsyncExitStacked):
|
|
|
44
38
|
|
|
45
39
|
cmd: ShellCmd
|
|
46
40
|
|
|
41
|
+
#
|
|
42
|
+
|
|
47
43
|
requirements_txts: ta.Optional[ta.Sequence[str]] = None
|
|
48
44
|
|
|
49
45
|
always_pull: bool = False
|
|
@@ -51,6 +47,10 @@ class Ci(AsyncExitStacked):
|
|
|
51
47
|
|
|
52
48
|
no_dependencies: bool = False
|
|
53
49
|
|
|
50
|
+
run_options: ta.Optional[ta.Sequence[str]] = None
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
|
|
54
54
|
def __post_init__(self) -> None:
|
|
55
55
|
check.not_isinstance(self.requirements_txts, str)
|
|
56
56
|
|
|
@@ -58,42 +58,15 @@ class Ci(AsyncExitStacked):
|
|
|
58
58
|
self,
|
|
59
59
|
cfg: Config,
|
|
60
60
|
*,
|
|
61
|
-
shell_cache: ta.Optional[ShellCache] = None,
|
|
62
61
|
file_cache: ta.Optional[FileCache] = None,
|
|
63
62
|
) -> None:
|
|
64
63
|
super().__init__()
|
|
65
64
|
|
|
66
65
|
self._cfg = cfg
|
|
67
|
-
self._shell_cache = shell_cache
|
|
68
66
|
self._file_cache = file_cache
|
|
69
67
|
|
|
70
68
|
#
|
|
71
69
|
|
|
72
|
-
async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
|
73
|
-
if self._shell_cache is None:
|
|
74
|
-
return None
|
|
75
|
-
|
|
76
|
-
get_cache_cmd = self._shell_cache.get_file_cmd(key)
|
|
77
|
-
if get_cache_cmd is None:
|
|
78
|
-
return None
|
|
79
|
-
|
|
80
|
-
get_cache_cmd = dc.replace(get_cache_cmd, s=f'{get_cache_cmd.s} | zstd -cd --long') # noqa
|
|
81
|
-
|
|
82
|
-
return await load_docker_tar_cmd(get_cache_cmd)
|
|
83
|
-
|
|
84
|
-
async def _save_cache_docker_image(self, key: str, image: str) -> None:
|
|
85
|
-
if self._shell_cache is None:
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
with self._shell_cache.put_file_cmd(key) as put_cache:
|
|
89
|
-
put_cache_cmd = put_cache.cmd
|
|
90
|
-
|
|
91
|
-
put_cache_cmd = dc.replace(put_cache_cmd, s=f'zstd | {put_cache_cmd.s}')
|
|
92
|
-
|
|
93
|
-
await save_docker_tar_cmd(image, put_cache_cmd)
|
|
94
|
-
|
|
95
|
-
#
|
|
96
|
-
|
|
97
70
|
async def _load_docker_image(self, image: str) -> None:
|
|
98
71
|
if not self._cfg.always_pull and (await is_docker_image_present(image)):
|
|
99
72
|
return
|
|
@@ -114,24 +87,38 @@ class Ci(AsyncExitStacked):
|
|
|
114
87
|
with log_timing_context(f'Load docker image: {image}'):
|
|
115
88
|
await self._load_docker_image(image)
|
|
116
89
|
|
|
117
|
-
|
|
118
|
-
async def load_compose_service_dependencies(self) -> None:
|
|
119
|
-
deps = get_compose_service_dependencies(
|
|
120
|
-
self._cfg.compose_file,
|
|
121
|
-
self._cfg.service,
|
|
122
|
-
)
|
|
90
|
+
#
|
|
123
91
|
|
|
124
|
-
|
|
125
|
-
|
|
92
|
+
async def _load_cache_docker_image(self, key: str) -> ta.Optional[str]:
|
|
93
|
+
if self._file_cache is None:
|
|
94
|
+
return None
|
|
126
95
|
|
|
127
|
-
|
|
96
|
+
cache_file = await self._file_cache.get_file(key)
|
|
97
|
+
if cache_file is None:
|
|
98
|
+
return None
|
|
128
99
|
|
|
129
|
-
|
|
130
|
-
def docker_file_hash(self) -> str:
|
|
131
|
-
return build_docker_file_hash(self._cfg.docker_file)[:self.FILE_NAME_HASH_LEN]
|
|
100
|
+
get_cache_cmd = ShellCmd(f'cat {cache_file} | zstd -cd --long')
|
|
132
101
|
|
|
133
|
-
|
|
134
|
-
|
|
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:
|
|
135
122
|
image_tag = f'{self._cfg.service}:{cache_key}'
|
|
136
123
|
|
|
137
124
|
if not self._cfg.always_build and (await is_docker_image_present(image_tag)):
|
|
@@ -144,21 +131,35 @@ class Ci(AsyncExitStacked):
|
|
|
144
131
|
)
|
|
145
132
|
return image_tag
|
|
146
133
|
|
|
147
|
-
image_id = await
|
|
148
|
-
self._cfg.docker_file,
|
|
149
|
-
tag=image_tag,
|
|
150
|
-
cwd=self._cfg.project_dir,
|
|
151
|
-
)
|
|
134
|
+
image_id = await build_and_tag(image_tag)
|
|
152
135
|
|
|
153
136
|
await self._save_cache_docker_image(cache_key, image_id)
|
|
154
137
|
|
|
155
138
|
return image_tag
|
|
156
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
|
+
|
|
157
158
|
@async_cached_nullary
|
|
158
|
-
async def
|
|
159
|
-
with log_timing_context('Resolve ci image') as ltc:
|
|
160
|
-
image_id = await self.
|
|
161
|
-
ltc.set_description(f'Resolve ci image: {image_id}')
|
|
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}')
|
|
162
163
|
return image_id
|
|
163
164
|
|
|
164
165
|
#
|
|
@@ -172,82 +173,85 @@ class Ci(AsyncExitStacked):
|
|
|
172
173
|
|
|
173
174
|
@cached_nullary
|
|
174
175
|
def requirements_hash(self) -> str:
|
|
175
|
-
return build_requirements_hash(self.requirements_txts())[:self.
|
|
176
|
-
|
|
177
|
-
async def _resolve_requirements_dir(self) -> str:
|
|
178
|
-
tar_file_key = f'requirements-{self.docker_file_hash()}-{self.requirements_hash()}'
|
|
179
|
-
tar_file_name = f'{tar_file_key}.tar'
|
|
180
|
-
|
|
181
|
-
temp_dir = tempfile.mkdtemp()
|
|
182
|
-
self._enter_context(defer(lambda: shutil.rmtree(temp_dir))) # noqa
|
|
176
|
+
return build_requirements_hash(self.requirements_txts())[:self.KEY_HASH_LEN]
|
|
183
177
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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)
|
|
187
221
|
|
|
188
|
-
|
|
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
|
|
189
228
|
|
|
190
|
-
|
|
191
|
-
os.makedirs(temp_requirements_dir)
|
|
229
|
+
#
|
|
192
230
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
self.
|
|
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,
|
|
197
236
|
)
|
|
198
237
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
with tarfile.open(temp_tar_file, 'w') as tar:
|
|
203
|
-
for requirement_file in os.listdir(temp_requirements_dir):
|
|
204
|
-
tar.add(
|
|
205
|
-
os.path.join(temp_requirements_dir, requirement_file),
|
|
206
|
-
arcname=requirement_file,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
self._file_cache.put_file(os.path.basename(tar_file_key), temp_tar_file)
|
|
210
|
-
|
|
211
|
-
return temp_requirements_dir
|
|
212
|
-
|
|
213
|
-
@async_cached_nullary
|
|
214
|
-
async def resolve_requirements_dir(self) -> str:
|
|
215
|
-
with log_timing_context('Resolve requirements dir') as ltc:
|
|
216
|
-
requirements_dir = await self._resolve_requirements_dir()
|
|
217
|
-
ltc.set_description(f'Resolve requirements dir: {requirements_dir}')
|
|
218
|
-
return requirements_dir
|
|
238
|
+
for dep_image in deps.values():
|
|
239
|
+
await self.load_docker_image(dep_image)
|
|
219
240
|
|
|
220
241
|
#
|
|
221
242
|
|
|
222
243
|
async def _run_compose_(self) -> None:
|
|
223
|
-
setup_cmds = [
|
|
224
|
-
'pip install --root-user-action ignore --find-links /requirements --no-index uv',
|
|
225
|
-
(
|
|
226
|
-
'uv pip install --system --find-links /requirements ' +
|
|
227
|
-
' '.join(f'-r /project/{rf}' for rf in self._cfg.requirements_txts or [])
|
|
228
|
-
),
|
|
229
|
-
]
|
|
230
|
-
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
ci_cmd = dc.replace(self._cfg.cmd, s=' && '.join([
|
|
234
|
-
*setup_cmds,
|
|
235
|
-
f'({self._cfg.cmd.s})',
|
|
236
|
-
]))
|
|
237
|
-
|
|
238
|
-
#
|
|
239
|
-
|
|
240
244
|
async with DockerComposeRun(DockerComposeRun.Config(
|
|
241
245
|
compose_file=self._cfg.compose_file,
|
|
242
246
|
service=self._cfg.service,
|
|
243
247
|
|
|
244
248
|
image=await self.resolve_ci_image(),
|
|
245
249
|
|
|
246
|
-
cmd=
|
|
250
|
+
cmd=self._cfg.cmd,
|
|
247
251
|
|
|
248
252
|
run_options=[
|
|
249
253
|
'-v', f'{os.path.abspath(self._cfg.project_dir)}:/project',
|
|
250
|
-
|
|
254
|
+
*(self._cfg.run_options or []),
|
|
251
255
|
],
|
|
252
256
|
|
|
253
257
|
cwd=self._cfg.project_dir,
|
|
@@ -263,10 +267,8 @@ class Ci(AsyncExitStacked):
|
|
|
263
267
|
#
|
|
264
268
|
|
|
265
269
|
async def run(self) -> None:
|
|
266
|
-
await self.load_compose_service_dependencies()
|
|
267
|
-
|
|
268
270
|
await self.resolve_ci_image()
|
|
269
271
|
|
|
270
|
-
await self.
|
|
272
|
+
await self.load_dependencies()
|
|
271
273
|
|
|
272
274
|
await self._run_compose()
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# @omlish-amalg ../scripts/ci.py
|
|
2
2
|
# ruff: noqa: UP006 UP007
|
|
3
|
-
# @omlish-lite
|
|
4
3
|
"""
|
|
5
4
|
Inputs:
|
|
6
5
|
- requirements.txt
|
|
@@ -9,9 +8,11 @@ Inputs:
|
|
|
9
8
|
|
|
10
9
|
==
|
|
11
10
|
|
|
12
|
-
./python -m ci run --cache-dir ci/cache ci/project omlish-ci
|
|
11
|
+
./python -m omdev.ci run --cache-dir omdev/ci/tests/cache omdev/ci/tests/project omlish-ci
|
|
13
12
|
"""
|
|
13
|
+
import argparse
|
|
14
14
|
import asyncio
|
|
15
|
+
import itertools
|
|
15
16
|
import os.path
|
|
16
17
|
import sys
|
|
17
18
|
import typing as ta
|
|
@@ -20,15 +21,15 @@ from omlish.argparse.cli import ArgparseCli
|
|
|
20
21
|
from omlish.argparse.cli import argparse_arg
|
|
21
22
|
from omlish.argparse.cli import argparse_cmd
|
|
22
23
|
from omlish.lite.check import check
|
|
24
|
+
from omlish.lite.logs import log
|
|
23
25
|
from omlish.logs.standard import configure_standard_logging
|
|
24
26
|
|
|
25
27
|
from .cache import DirectoryFileCache
|
|
26
|
-
from .cache import DirectoryShellCache
|
|
27
28
|
from .cache import FileCache
|
|
28
|
-
from .cache import ShellCache
|
|
29
29
|
from .ci import Ci
|
|
30
30
|
from .compose import get_compose_service_dependencies
|
|
31
|
-
from .github.
|
|
31
|
+
from .github.bootstrap import is_in_github_actions
|
|
32
|
+
from .github.cache import GithubFileCache
|
|
32
33
|
from .github.cli import GithubCli
|
|
33
34
|
from .requirements import build_requirements_hash
|
|
34
35
|
from .shell import ShellCmd
|
|
@@ -65,8 +66,8 @@ class CiCli(ArgparseCli):
|
|
|
65
66
|
@argparse_cmd(
|
|
66
67
|
accepts_unknown=True,
|
|
67
68
|
)
|
|
68
|
-
def github(self) -> ta.Optional[int]:
|
|
69
|
-
return GithubCli(self.unknown_args).
|
|
69
|
+
async def github(self) -> ta.Optional[int]:
|
|
70
|
+
return await GithubCli(self.unknown_args).async_cli_run()
|
|
70
71
|
|
|
71
72
|
#
|
|
72
73
|
|
|
@@ -77,13 +78,20 @@ class CiCli(ArgparseCli):
|
|
|
77
78
|
argparse_arg('--compose-file'),
|
|
78
79
|
argparse_arg('-r', '--requirements-txt', action='append'),
|
|
79
80
|
|
|
80
|
-
argparse_arg('--github-cache', action='store_true'),
|
|
81
81
|
argparse_arg('--cache-dir'),
|
|
82
82
|
|
|
83
|
+
argparse_arg('--github', action='store_true'),
|
|
84
|
+
argparse_arg('--github-detect', action='store_true'),
|
|
85
|
+
|
|
83
86
|
argparse_arg('--always-pull', action='store_true'),
|
|
84
87
|
argparse_arg('--always-build', action='store_true'),
|
|
85
88
|
|
|
86
89
|
argparse_arg('--no-dependencies', action='store_true'),
|
|
90
|
+
|
|
91
|
+
argparse_arg('-e', '--env', action='append'),
|
|
92
|
+
argparse_arg('-v', '--volume', action='append'),
|
|
93
|
+
|
|
94
|
+
argparse_arg('cmd', nargs=argparse.REMAINDER),
|
|
87
95
|
)
|
|
88
96
|
async def run(self) -> None:
|
|
89
97
|
project_dir = self.args.project_dir
|
|
@@ -94,6 +102,11 @@ class CiCli(ArgparseCli):
|
|
|
94
102
|
|
|
95
103
|
#
|
|
96
104
|
|
|
105
|
+
cmd = ' '.join(self.args.cmd)
|
|
106
|
+
check.non_empty_str(cmd)
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
|
|
97
110
|
check.state(os.path.isdir(project_dir))
|
|
98
111
|
|
|
99
112
|
#
|
|
@@ -102,6 +115,7 @@ class CiCli(ArgparseCli):
|
|
|
102
115
|
for alt in alts:
|
|
103
116
|
alt_file = os.path.abspath(os.path.join(project_dir, alt))
|
|
104
117
|
if os.path.isfile(alt_file):
|
|
118
|
+
log.debug('Using %s', alt_file)
|
|
105
119
|
return alt_file
|
|
106
120
|
return None
|
|
107
121
|
|
|
@@ -135,6 +149,7 @@ class CiCli(ArgparseCli):
|
|
|
135
149
|
'requirements-ci.txt',
|
|
136
150
|
]:
|
|
137
151
|
if os.path.exists(os.path.join(project_dir, rf)):
|
|
152
|
+
log.debug('Using %s', rf)
|
|
138
153
|
requirements_txts.append(rf)
|
|
139
154
|
else:
|
|
140
155
|
for rf in requirements_txts:
|
|
@@ -142,21 +157,34 @@ class CiCli(ArgparseCli):
|
|
|
142
157
|
|
|
143
158
|
#
|
|
144
159
|
|
|
145
|
-
|
|
160
|
+
github = self.args.github
|
|
161
|
+
if not github and self.args.github_detect:
|
|
162
|
+
github = is_in_github_actions()
|
|
163
|
+
if github:
|
|
164
|
+
log.debug('Github detected')
|
|
165
|
+
|
|
166
|
+
#
|
|
167
|
+
|
|
146
168
|
file_cache: ta.Optional[FileCache] = None
|
|
147
169
|
if cache_dir is not None:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
cache_dir = os.path.abspath(cache_dir)
|
|
171
|
+
log.debug('Using cache dir %s', cache_dir)
|
|
172
|
+
if github:
|
|
173
|
+
file_cache = GithubFileCache(cache_dir)
|
|
174
|
+
else:
|
|
175
|
+
file_cache = DirectoryFileCache(cache_dir)
|
|
153
176
|
|
|
154
|
-
|
|
177
|
+
#
|
|
155
178
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
179
|
+
run_options: ta.List[str] = []
|
|
180
|
+
for run_arg, run_arg_vals in [
|
|
181
|
+
('-e', self.args.env or []),
|
|
182
|
+
('-v', self.args.volume or []),
|
|
183
|
+
]:
|
|
184
|
+
run_options.extend(itertools.chain.from_iterable(
|
|
185
|
+
[run_arg, run_arg_val]
|
|
186
|
+
for run_arg_val in run_arg_vals
|
|
187
|
+
))
|
|
160
188
|
|
|
161
189
|
#
|
|
162
190
|
|
|
@@ -171,18 +199,16 @@ class CiCli(ArgparseCli):
|
|
|
171
199
|
|
|
172
200
|
requirements_txts=requirements_txts,
|
|
173
201
|
|
|
174
|
-
cmd=ShellCmd(
|
|
175
|
-
'cd /project',
|
|
176
|
-
'python3 -m pytest -svv test.py',
|
|
177
|
-
])),
|
|
202
|
+
cmd=ShellCmd(cmd),
|
|
178
203
|
|
|
179
204
|
always_pull=self.args.always_pull,
|
|
180
205
|
always_build=self.args.always_build,
|
|
181
206
|
|
|
182
207
|
no_dependencies=self.args.no_dependencies,
|
|
208
|
+
|
|
209
|
+
run_options=run_options,
|
|
183
210
|
),
|
|
184
211
|
file_cache=file_cache,
|
|
185
|
-
shell_cache=shell_cache,
|
|
186
212
|
) as ci:
|
|
187
213
|
await ci.run()
|
|
188
214
|
|