ominfra 0.0.0.dev171__py3-none-any.whl → 0.0.0.dev173__py3-none-any.whl
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.
- ominfra/manage/deploy/apps.py +48 -80
- ominfra/manage/deploy/conf.py +55 -50
- ominfra/manage/deploy/deploy.py +45 -1
- ominfra/manage/deploy/paths/paths.py +39 -45
- ominfra/manage/deploy/paths/types.py +8 -0
- ominfra/manage/deploy/specs.py +47 -13
- ominfra/manage/deploy/tags.py +225 -0
- ominfra/manage/deploy/types.py +0 -30
- ominfra/manage/targets/bestpython.sh +6 -9
- ominfra/scripts/manage.py +489 -200
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/RECORD +16 -14
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev171.dist-info → ominfra-0.0.0.dev173.dist-info}/top_level.txt +0 -0
    
        ominfra/manage/deploy/specs.py
    CHANGED
    
    | @@ -7,11 +7,16 @@ import typing as ta | |
| 7 7 | 
             
            from omlish.lite.cached import cached_nullary
         | 
| 8 8 | 
             
            from omlish.lite.check import check
         | 
| 9 9 |  | 
| 10 | 
            -
            from . | 
| 11 | 
            -
            from . | 
| 10 | 
            +
            from .tags import DeployApp
         | 
| 11 | 
            +
            from .tags import DeployAppKey
         | 
| 12 | 
            +
            from .tags import DeployKey
         | 
| 13 | 
            +
            from .tags import KeyDeployTag  # noqa
         | 
| 12 14 | 
             
            from .types import DeployRev
         | 
| 13 15 |  | 
| 14 16 |  | 
| 17 | 
            +
            KeyDeployTagT = ta.TypeVar('KeyDeployTagT', bound='KeyDeployTag')
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 15 20 | 
             
            ##
         | 
| 16 21 |  | 
| 17 22 |  | 
| @@ -23,6 +28,16 @@ def check_valid_deploy_spec_path(s: str) -> str: | |
| 23 28 | 
             
                return s
         | 
| 24 29 |  | 
| 25 30 |  | 
| 31 | 
            +
            class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
         | 
| 32 | 
            +
                @cached_nullary
         | 
| 33 | 
            +
                def _key_str(self) -> str:
         | 
| 34 | 
            +
                    return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                @abc.abstractmethod
         | 
| 37 | 
            +
                def key(self) -> KeyDeployTagT:
         | 
| 38 | 
            +
                    raise NotImplementedError
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 26 41 | 
             
            ##
         | 
| 27 42 |  | 
| 28 43 |  | 
| @@ -68,7 +83,7 @@ class DeployVenvSpec: | |
| 68 83 |  | 
| 69 84 |  | 
| 70 85 | 
             
            @dc.dataclass(frozen=True)
         | 
| 71 | 
            -
            class  | 
| 86 | 
            +
            class DeployAppConfFile:
         | 
| 72 87 | 
             
                path: str
         | 
| 73 88 | 
             
                body: str
         | 
| 74 89 |  | 
| @@ -80,7 +95,7 @@ class DeployConfFile: | |
| 80 95 |  | 
| 81 96 |  | 
| 82 97 | 
             
            @dc.dataclass(frozen=True)
         | 
| 83 | 
            -
            class  | 
| 98 | 
            +
            class DeployAppConfLink(abc.ABC):  # noqa
         | 
| 84 99 | 
             
                """
         | 
| 85 100 | 
             
                May be either:
         | 
| 86 101 | 
             
                 - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
         | 
| @@ -96,11 +111,11 @@ class DeployConfLink(abc.ABC):  # noqa | |
| 96 111 | 
             
                        check.equal(self.src.count('/'), 1)
         | 
| 97 112 |  | 
| 98 113 |  | 
| 99 | 
            -
            class  | 
| 114 | 
            +
            class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
         | 
| 100 115 | 
             
                pass
         | 
| 101 116 |  | 
| 102 117 |  | 
| 103 | 
            -
            class  | 
| 118 | 
            +
            class AllActiveDeployAppConfLink(DeployAppConfLink):
         | 
| 104 119 | 
             
                pass
         | 
| 105 120 |  | 
| 106 121 |  | 
| @@ -108,10 +123,10 @@ class TagDeployConfLink(DeployConfLink): | |
| 108 123 |  | 
| 109 124 |  | 
| 110 125 | 
             
            @dc.dataclass(frozen=True)
         | 
| 111 | 
            -
            class  | 
| 112 | 
            -
                files: ta.Optional[ta.Sequence[ | 
| 126 | 
            +
            class DeployAppConfSpec:
         | 
| 127 | 
            +
                files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
         | 
| 113 128 |  | 
| 114 | 
            -
                links: ta.Optional[ta.Sequence[ | 
| 129 | 
            +
                links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
         | 
| 115 130 |  | 
| 116 131 | 
             
                def __post_init__(self) -> None:
         | 
| 117 132 | 
             
                    if self.files:
         | 
| @@ -125,15 +140,34 @@ class DeployConfSpec: | |
| 125 140 |  | 
| 126 141 |  | 
| 127 142 | 
             
            @dc.dataclass(frozen=True)
         | 
| 128 | 
            -
            class  | 
| 143 | 
            +
            class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
         | 
| 129 144 | 
             
                app: DeployApp
         | 
| 130 145 |  | 
| 131 146 | 
             
                git: DeployGitSpec
         | 
| 132 147 |  | 
| 133 148 | 
             
                venv: ta.Optional[DeployVenvSpec] = None
         | 
| 134 149 |  | 
| 135 | 
            -
                conf: ta.Optional[ | 
| 150 | 
            +
                conf: ta.Optional[DeployAppConfSpec] = None
         | 
| 136 151 |  | 
| 137 | 
            -
                @ | 
| 152 | 
            +
                # @ta.override
         | 
| 153 | 
            +
                def key(self) -> DeployAppKey:
         | 
| 154 | 
            +
                    return DeployAppKey(self._key_str())
         | 
| 155 | 
            +
             | 
| 156 | 
            +
             | 
| 157 | 
            +
            ##
         | 
| 158 | 
            +
             | 
| 159 | 
            +
             | 
| 160 | 
            +
            @dc.dataclass(frozen=True)
         | 
| 161 | 
            +
            class DeploySpec(DeploySpecKeyed[DeployKey]):
         | 
| 162 | 
            +
                apps: ta.Sequence[DeployAppSpec]
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                def __post_init__(self) -> None:
         | 
| 165 | 
            +
                    seen: ta.Set[DeployApp] = set()
         | 
| 166 | 
            +
                    for a in self.apps:
         | 
| 167 | 
            +
                        if a.app in seen:
         | 
| 168 | 
            +
                            raise KeyError(a.app)
         | 
| 169 | 
            +
                        seen.add(a.app)
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                # @ta.override
         | 
| 138 172 | 
             
                def key(self) -> DeployKey:
         | 
| 139 | 
            -
                    return DeployKey( | 
| 173 | 
            +
                    return DeployKey(self._key_str())
         | 
| @@ -0,0 +1,225 @@ | |
| 1 | 
            +
            # ruff: noqa: UP006 UP007
         | 
| 2 | 
            +
            import abc
         | 
| 3 | 
            +
            import dataclasses as dc
         | 
| 4 | 
            +
            import typing as ta
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            from omlish.lite.check import check
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            ##
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            DEPLOY_TAG_SIGIL = '@'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            DEPLOY_TAG_SEPARATOR = '--'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            DEPLOY_TAG_DELIMITERS: ta.AbstractSet[str] = frozenset([
         | 
| 17 | 
            +
                DEPLOY_TAG_SEPARATOR,
         | 
| 18 | 
            +
                '.',
         | 
| 19 | 
            +
            ])
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            DEPLOY_TAG_ILLEGAL_STRS: ta.AbstractSet[str] = frozenset([
         | 
| 22 | 
            +
                DEPLOY_TAG_SIGIL,
         | 
| 23 | 
            +
                *DEPLOY_TAG_DELIMITERS,
         | 
| 24 | 
            +
                '/',
         | 
| 25 | 
            +
            ])
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
            ##
         | 
| 29 | 
            +
             | 
| 30 | 
            +
             | 
| 31 | 
            +
            @dc.dataclass(frozen=True)
         | 
| 32 | 
            +
            class DeployTag(abc.ABC):  # noqa
         | 
| 33 | 
            +
                s: str
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def __post_init__(self) -> None:
         | 
| 36 | 
            +
                    check.not_in(abc.ABC, type(self).__bases__)
         | 
| 37 | 
            +
                    check.non_empty_str(self.s)
         | 
| 38 | 
            +
                    for ch in DEPLOY_TAG_ILLEGAL_STRS:
         | 
| 39 | 
            +
                        check.state(ch not in self.s)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                tag_name: ta.ClassVar[str]
         | 
| 44 | 
            +
                tag_kwarg: ta.ClassVar[str]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def __init_subclass__(cls, **kwargs: ta.Any) -> None:
         | 
| 47 | 
            +
                    super().__init_subclass__(**kwargs)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    if abc.ABC in cls.__bases__:
         | 
| 50 | 
            +
                        return
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    for b in cls.__bases__:
         | 
| 53 | 
            +
                        if issubclass(b, DeployTag):
         | 
| 54 | 
            +
                            check.in_(abc.ABC, b.__bases__)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    check.non_empty_str(tn := cls.tag_name)
         | 
| 57 | 
            +
                    check.equal(tn, tn.lower().strip())
         | 
| 58 | 
            +
                    check.not_in('_', tn)
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    check.state(not hasattr(cls, 'tag_kwarg'))
         | 
| 61 | 
            +
                    cls.tag_kwarg = tn.replace('-', '_')
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             | 
| 64 | 
            +
            ##
         | 
| 65 | 
            +
             | 
| 66 | 
            +
             | 
| 67 | 
            +
            _DEPLOY_TAGS: ta.Set[ta.Type[DeployTag]] = set()
         | 
| 68 | 
            +
            DEPLOY_TAGS: ta.AbstractSet[ta.Type[DeployTag]] = _DEPLOY_TAGS
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            _DEPLOY_TAGS_BY_NAME: ta.Dict[str, ta.Type[DeployTag]] = {}
         | 
| 71 | 
            +
            DEPLOY_TAGS_BY_NAME: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_NAME
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            _DEPLOY_TAGS_BY_KWARG: ta.Dict[str, ta.Type[DeployTag]] = {}
         | 
| 74 | 
            +
            DEPLOY_TAGS_BY_KWARG: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_KWARG
         | 
| 75 | 
            +
             | 
| 76 | 
            +
             | 
| 77 | 
            +
            def _register_deploy_tag(cls):
         | 
| 78 | 
            +
                check.not_in(cls.tag_name, _DEPLOY_TAGS_BY_NAME)
         | 
| 79 | 
            +
                check.not_in(cls.tag_kwarg, _DEPLOY_TAGS_BY_KWARG)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                _DEPLOY_TAGS.add(cls)
         | 
| 82 | 
            +
                _DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
         | 
| 83 | 
            +
                _DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                return cls
         | 
| 86 | 
            +
             | 
| 87 | 
            +
             | 
| 88 | 
            +
            ##
         | 
| 89 | 
            +
             | 
| 90 | 
            +
             | 
| 91 | 
            +
            @_register_deploy_tag
         | 
| 92 | 
            +
            class DeployTime(DeployTag):
         | 
| 93 | 
            +
                tag_name: ta.ClassVar[str] = 'time'
         | 
| 94 | 
            +
             | 
| 95 | 
            +
             | 
| 96 | 
            +
            ##
         | 
| 97 | 
            +
             | 
| 98 | 
            +
             | 
| 99 | 
            +
            class NameDeployTag(DeployTag, abc.ABC):  # noqa
         | 
| 100 | 
            +
                pass
         | 
| 101 | 
            +
             | 
| 102 | 
            +
             | 
| 103 | 
            +
            @_register_deploy_tag
         | 
| 104 | 
            +
            class DeployApp(NameDeployTag):
         | 
| 105 | 
            +
                tag_name: ta.ClassVar[str] = 'app'
         | 
| 106 | 
            +
             | 
| 107 | 
            +
             | 
| 108 | 
            +
            @_register_deploy_tag
         | 
| 109 | 
            +
            class DeployConf(NameDeployTag):
         | 
| 110 | 
            +
                tag_name: ta.ClassVar[str] = 'conf'
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
            ##
         | 
| 114 | 
            +
             | 
| 115 | 
            +
             | 
| 116 | 
            +
            class KeyDeployTag(DeployTag, abc.ABC):  # noqa
         | 
| 117 | 
            +
                pass
         | 
| 118 | 
            +
             | 
| 119 | 
            +
             | 
| 120 | 
            +
            @_register_deploy_tag
         | 
| 121 | 
            +
            class DeployKey(KeyDeployTag):
         | 
| 122 | 
            +
                tag_name: ta.ClassVar[str] = 'deploy-key'
         | 
| 123 | 
            +
             | 
| 124 | 
            +
             | 
| 125 | 
            +
            @_register_deploy_tag
         | 
| 126 | 
            +
            class DeployAppKey(KeyDeployTag):
         | 
| 127 | 
            +
                tag_name: ta.ClassVar[str] = 'app-key'
         | 
| 128 | 
            +
             | 
| 129 | 
            +
             | 
| 130 | 
            +
            ##
         | 
| 131 | 
            +
             | 
| 132 | 
            +
             | 
| 133 | 
            +
            class RevDeployTag(DeployTag, abc.ABC):  # noqa
         | 
| 134 | 
            +
                pass
         | 
| 135 | 
            +
             | 
| 136 | 
            +
             | 
| 137 | 
            +
            @_register_deploy_tag
         | 
| 138 | 
            +
            class DeployAppRev(RevDeployTag):
         | 
| 139 | 
            +
                tag_name: ta.ClassVar[str] = 'app-rev'
         | 
| 140 | 
            +
             | 
| 141 | 
            +
             | 
| 142 | 
            +
            ##
         | 
| 143 | 
            +
             | 
| 144 | 
            +
             | 
| 145 | 
            +
            class DeployTagMap:
         | 
| 146 | 
            +
                def __init__(
         | 
| 147 | 
            +
                        self,
         | 
| 148 | 
            +
                        *args: DeployTag,
         | 
| 149 | 
            +
                        **kwargs: str,
         | 
| 150 | 
            +
                ) -> None:
         | 
| 151 | 
            +
                    super().__init__()
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    dct: ta.Dict[ta.Type[DeployTag], DeployTag] = {}
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    for a in args:
         | 
| 156 | 
            +
                        c = type(check.isinstance(a, DeployTag))
         | 
| 157 | 
            +
                        check.not_in(c, dct)
         | 
| 158 | 
            +
                        dct[c] = a
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                    for k, v in kwargs.items():
         | 
| 161 | 
            +
                        c = DEPLOY_TAGS_BY_KWARG[k]
         | 
| 162 | 
            +
                        check.not_in(c, dct)
         | 
| 163 | 
            +
                        dct[c] = c(v)
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                    self._dct = dct
         | 
| 166 | 
            +
                    self._tup = tuple(sorted((type(t).tag_kwarg, t.s) for t in dct.values()))
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                #
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                def add(self, *args: ta.Any, **kwargs: ta.Any) -> 'DeployTagMap':
         | 
| 171 | 
            +
                    return DeployTagMap(
         | 
| 172 | 
            +
                        *self,
         | 
| 173 | 
            +
                        *args,
         | 
| 174 | 
            +
                        **kwargs,
         | 
| 175 | 
            +
                    )
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                def remove(self, *tags_or_names: ta.Union[ta.Type[DeployTag], str]) -> 'DeployTagMap':
         | 
| 178 | 
            +
                    dcs = {
         | 
| 179 | 
            +
                        check.issubclass(a, DeployTag) if isinstance(a, type) else DEPLOY_TAGS_BY_NAME[a]
         | 
| 180 | 
            +
                        for a in tags_or_names
         | 
| 181 | 
            +
                    }
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                    return DeployTagMap(*[
         | 
| 184 | 
            +
                        t
         | 
| 185 | 
            +
                        for t in self._dct.values()
         | 
| 186 | 
            +
                        if t not in dcs
         | 
| 187 | 
            +
                    ])
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                #
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                def __repr__(self) -> str:
         | 
| 192 | 
            +
                    return f'{self.__class__.__name__}({", ".join(f"{k}={v!r}" for k, v in self._tup)})'
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                def __hash__(self) -> int:
         | 
| 195 | 
            +
                    return hash(self._tup)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                def __eq__(self, other: object) -> bool:
         | 
| 198 | 
            +
                    if isinstance(other, DeployTagMap):
         | 
| 199 | 
            +
                        return self._tup == other._tup
         | 
| 200 | 
            +
                    else:
         | 
| 201 | 
            +
                        return NotImplemented
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                #
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                def __len__(self) -> int:
         | 
| 206 | 
            +
                    return len(self._dct)
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                def __iter__(self) -> ta.Iterator[DeployTag]:
         | 
| 209 | 
            +
                    return iter(self._dct.values())
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                def __getitem__(self, key: ta.Union[ta.Type[DeployTag], str]) -> DeployTag:
         | 
| 212 | 
            +
                    if isinstance(key, str):
         | 
| 213 | 
            +
                        return self._dct[DEPLOY_TAGS_BY_NAME[key]]
         | 
| 214 | 
            +
                    elif isinstance(key, type):
         | 
| 215 | 
            +
                        return self._dct[key]
         | 
| 216 | 
            +
                    else:
         | 
| 217 | 
            +
                        raise TypeError(key)
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                def __contains__(self, key: ta.Union[ta.Type[DeployTag], str]) -> bool:
         | 
| 220 | 
            +
                    if isinstance(key, str):
         | 
| 221 | 
            +
                        return DEPLOY_TAGS_BY_NAME[key] in self._dct
         | 
| 222 | 
            +
                    elif isinstance(key, type):
         | 
| 223 | 
            +
                        return key in self._dct
         | 
| 224 | 
            +
                    else:
         | 
| 225 | 
            +
                        raise TypeError(key)
         | 
    
        ominfra/manage/deploy/types.py
    CHANGED
    
    | @@ -1,39 +1,9 @@ | |
| 1 | 
            -
            import dataclasses as dc
         | 
| 2 1 | 
             
            import typing as ta
         | 
| 3 2 |  | 
| 4 | 
            -
            from omlish.lite.check import check
         | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            DeployPathKind = ta.Literal['dir', 'file']  # ta.TypeAlias
         | 
| 8 | 
            -
            DeployPathPlaceholder = ta.Literal['app', 'tag', 'conf']  # ta.TypeAlias
         | 
| 9 | 
            -
             | 
| 10 3 |  | 
| 11 4 | 
             
            ##
         | 
| 12 5 |  | 
| 13 6 |  | 
| 14 7 | 
             
            DeployHome = ta.NewType('DeployHome', str)
         | 
| 15 8 |  | 
| 16 | 
            -
            DeployApp = ta.NewType('DeployApp', str)
         | 
| 17 | 
            -
            DeployTag = ta.NewType('DeployTag', str)
         | 
| 18 9 | 
             
            DeployRev = ta.NewType('DeployRev', str)
         | 
| 19 | 
            -
            DeployKey = ta.NewType('DeployKey', str)
         | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
            ##
         | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
            @dc.dataclass(frozen=True)
         | 
| 26 | 
            -
            class DeployAppTag:
         | 
| 27 | 
            -
                app: DeployApp
         | 
| 28 | 
            -
                tag: DeployTag
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                def __post_init__(self) -> None:
         | 
| 31 | 
            -
                    for s in [self.app, self.tag]:
         | 
| 32 | 
            -
                        check.non_empty_str(s)
         | 
| 33 | 
            -
                        check.equal(s, s.strip())
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                def placeholders(self) -> ta.Mapping[DeployPathPlaceholder, str]:
         | 
| 36 | 
            -
                    return {
         | 
| 37 | 
            -
                        'app': self.app,
         | 
| 38 | 
            -
                        'tag': self.tag,
         | 
| 39 | 
            -
                    }
         | 
| @@ -1,15 +1,12 @@ | |
| 1 1 | 
             
            bv=""
         | 
| 2 2 | 
             
            bx=""
         | 
| 3 3 |  | 
| 4 | 
            -
            for  | 
| 5 | 
            -
                x="python$ | 
| 6 | 
            -
                v=$($x -c "import sys; print(sys.version_info[:])" 2>/dev/null)
         | 
| 7 | 
            -
                if [ $? -eq 0 ]; then
         | 
| 8 | 
            -
                     | 
| 9 | 
            -
                     | 
| 10 | 
            -
                        bv=$cv
         | 
| 11 | 
            -
                        bx=$x
         | 
| 12 | 
            -
                    fi
         | 
| 4 | 
            +
            for v in "" 3 3.{8..13}; do
         | 
| 5 | 
            +
                x="python$v"
         | 
| 6 | 
            +
                v=$($x -c "import sys; print((\"%02d\" * 3) % sys.version_info[:3])" 2>/dev/null)
         | 
| 7 | 
            +
                if [ $? -eq 0 ] && ([ -z "$bv" ] || [ "$v" \> "$bv" ]); then
         | 
| 8 | 
            +
                    bv=$v
         | 
| 9 | 
            +
                    bx=$x
         | 
| 13 10 | 
             
                fi
         | 
| 14 11 | 
             
            done
         | 
| 15 12 |  |