具有构建提升功能的 Artifactory PyPi 回购布局

Artifactory PyPi repo layout with build promotion

Q1: 我有一个启用了 Artifactory PyPi 的 repo my-pypi-repo,我可以在其中发布我的包。通过 python setup.py -sdist 上传时,我得到这样的结构:

my-pypi-repo|
            |my_package|
                       |x.y.z|
                             |my_package-x.y.z.tar.gz

问题是此结构将不匹配 Artifactory 中的任何 "allowed" 回购布局,因为 [org][orgPath] 是强制性的:

Pattern '[module]/[baseRev]/[module]-[baseRev].[ext]' must at-least contain the tokens 'module', 'baseRev' and 'org' or 'orgPath'.

我设法通过 'hacking' 包名发布到 myorg/my_package 的路径,但是 pip 找不到它,所以它很有用。

Q2: 有没有人使用 Artifactory 尝试过 "ci-repo" 和 "releases-repo" 并提升 Python?

我想达到的目标:

CI 回购: my_package-1.2.3+build90.tar.gz 当这个工件被提升时构建元数据被删除

发布回购: my_package-1.2.3.tar.gz

我可以通过回购布局实现这一点(前提是我解决了 Q1)。问题是如何处理 Python 脚本中的 "embedded" 版本,硬编码在 setup.py 中。

为了最佳实践,我不想再次重建包。

A1:Artifactory 布局不是强制性的,您可以将任何路径下的任何文件部署到任何存储库。一些与布局相关的功能,例如快照清理将无法使用,但我认为您无论如何都不需要它们。

A2:最好的解决方案是在 a promotion user plugin 中编写促销代码。在将工件提升到另一个存储库期间即时重命名工件是此类插件最流行的场景之一。

关于你的第一个 question/problem,我 运行 遇到了同样的问题。配置我的系统以使用 pip 发布到 artifactory 时,它使用您描述的格式。

如您所述,[org][orgPath] 是强制性的,这基本上破坏了所有 REST API 功能,例如搜索最新版本等。我目前正在使用这是我的工件路径模式:

[org]/[module]/[baseRev].([fileItegRev])/[module]-[baseRev].([fileItegRev]).[ext]

问题是pip在这种情况下不理解[org]的概念。我暂时使用 python 脚本将我的包发布到 Artifactory 来解决这个问题。希望 jFrog 团队可以解决这个问题。

python 脚本简单地使用 Artifactory 的 REST API 发布到我的本地 pypi 存储库,附加一些属性以便一些 REST API 功能正常工作,比如Artifact Latest Version Search Based on Properties.

我需要能够使用该调用,因为我们正在内部使用 Chef,并且我们使用该方法来获取最新版本。通过 python setup.py sdist upload -r local 发布时添加的 pypi.version 属性 不适用于 REST API 所以我必须手动添加 version 属性.说实话很痛苦,因为在 setup.py 中使用上传选项时我们无法添加属性。理想情况下,我希望能够使用 pip 完成所有操作,但目前这是不可能的。

我正在使用 requests package and the upload method in the Artifactory documentation here。这是我用来发布添加一些属性的函数(如果需要,可以随意添加更多):

def _publish_artifact(name, version, path, summary):
    base_url = 'http://server:8081/artifactory/{0}'.format(PYPI_REPOSITORY)
    properties = ';version={0};pypi.name={1};pypi.version={0};pypi.summary={2}'\
             .format(version, name, summary)
    url_path = '/Company/{0}/{1}/{0}-{1}.zip'.format(name, version)
    url = '{0}{1}{2}'.format(base_url, properties, url_path)

    dist_file = r'{0}\dist\{1}-{2}.zip'.format(path, name, version)
    files = {'upload_file': open(dist_file, 'rb')}
    s = requests.Session()
    s.auth = ('username', 'password')
    reply = s.put(url, files=files)
    logger.info('HTTP reply: {0}'.format(reply))