Publish distributions to Gitea PyPI registry alongside the release

This commit is contained in:
2026-07-31 16:57:08 +08:00
parent fe1a6ed749
commit caf5c01d5a
5 changed files with 69 additions and 6 deletions
+48
View File
@@ -341,3 +341,51 @@ jobs:
throw "release asset mismatch: $($publishedNames -join ', ')" throw "release asset mismatch: $($publishedNames -join ', ')"
} }
Write-Host "release verified: $tag ($($localNames.Count) files)" Write-Host "release verified: $tag ($($localNames.Count) files)"
- name: Publish packages to Gitea PyPI
shell: pwsh
env:
TWINE_USERNAME: antior
TWINE_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
TWINE_REPOSITORY_URL: ${{ gitea.server_url }}/api/packages/antior/pypi
run: |
$ErrorActionPreference = "Stop"
if ([string]::IsNullOrWhiteSpace($env:TWINE_PASSWORD)) {
throw "GITHUB_TOKEN is required to publish packages"
}
$headers = @{ Authorization = "token $env:TWINE_PASSWORD" }
$simpleUrl = "$env:TWINE_REPOSITORY_URL/simple/dlp-io/"
function Get-RegistryFileNames {
try {
$response = Invoke-WebRequest -Headers $headers -Uri $simpleUrl
} catch {
if ([int]$_.Exception.Response.StatusCode -eq 404) { return @() }
throw
}
return @([regex]::Matches($response.Content, '<a\s+href="[^"]+"[^>]*>(?<name>[^<]+)</a>', "IgnoreCase") |
ForEach-Object { [Net.WebUtility]::HtmlDecode($_.Groups["name"].Value) })
}
$published = @(Get-RegistryFileNames)
$missing = @(Get-ChildItem -LiteralPath dist -File | Where-Object { $published -notcontains $_.Name })
if ($missing.Count -gt 0) {
$uploadArgs = @("-3.13", "-m", "twine", "upload", "--non-interactive", "--disable-progress-bar") +
@($missing | ForEach-Object FullName)
& py @uploadArgs
if ($LASTEXITCODE -ne 0) { throw "twine upload failed" }
} else {
Write-Host "all registry artifacts already published"
}
$expected = @(Get-ChildItem -LiteralPath dist -File | ForEach-Object Name)
for ($attempt = 1; $attempt -le 20; $attempt++) {
$published = @(Get-RegistryFileNames)
$unpublished = @($expected | Where-Object { $published -notcontains $_ })
if ($unpublished.Count -eq 0) { break }
if ($attempt -lt 20) { Start-Sleep -Seconds 3 }
}
if ($unpublished.Count -ne 0) {
throw "registry package files missing after upload: $($unpublished -join ', ')"
}
Write-Host "registry verified: dlp-io==$($env:DLP_IO_PACKAGE_VERSION) ($($expected.Count) files)"
+2
View File
@@ -20,6 +20,8 @@ pytest
- `dlp_io-X.Y.Z.tar.gz`sdist - `dlp_io-X.Y.Z.tar.gz`sdist
- `dlp_io.py` 和 5 个裸 `dlp_io.cp3XX-win_amd64.pyd`(免安装单文件,放进项目目录即可 import) - `dlp_io.py` 和 5 个裸 `dlp_io.cp3XX-win_amd64.pyd`(免安装单文件,放进项目目录即可 import)
同一批 wheel/sdist 还会同步发布到 Gitea PyPI registry`https://gitea.docker.antior.cn/api/packages/antior/pypi/simple`),可直接 `pip install --index-url ... dlp-io`
安装方式见 [docs/DLP_IO_LIBRARY.md](docs/DLP_IO_LIBRARY.md)。 安装方式见 [docs/DLP_IO_LIBRARY.md](docs/DLP_IO_LIBRARY.md)。
本地只构建纯 Python 版: 本地只构建纯 Python 版:
+1 -1
View File
@@ -17,7 +17,7 @@ from enum import Enum
from multiprocessing.context import AuthenticationError from multiprocessing.context import AuthenticationError
from multiprocessing.connection import Client from multiprocessing.connection import Client
__version__ = "0.1.0" __version__ = "0.1.1"
__all__ = [ __all__ = [
"DlpConfig", "DlpConfig",
+17 -5
View File
@@ -2,11 +2,23 @@
`dlp-io` 为 Windows DLP 透明加密环境提供接近 `io.open` 的 Python 文件 API。非白名单进程负责业务逻辑和写入;文件读取由已获 DLP 白名单授权的 Python helper 完成,再通过经过认证的 Windows Named Pipe 返回明文字节。 `dlp-io` 为 Windows DLP 透明加密环境提供接近 `io.open` 的 Python 文件 API。非白名单进程负责业务逻辑和写入;文件读取由已获 DLP 白名单授权的 Python helper 完成,再通过经过认证的 Windows Named Pipe 返回明文字节。
当前版本为 `dlp-io==0.1.0`distribution 名是 `dlp-io`import 名是 `dlp_io` 当前版本为 `dlp-io==0.1.1`distribution 名是 `dlp-io`import 名是 `dlp_io`
## 安装 ## 安装
发布物托管在 Gitea Release 页面,由 `.gitea/workflows/publish-dlp-io.yaml` 从 immutable tag `dlp-io-vX.Y.Z` 自动构建并上传,全部产物在 CI runner 上编译,不依赖本地环境。 ### 方式一:Gitea PyPI registry(推荐)
registry 已开放匿名下载,一条命令即可安装;pip 会自动选择最匹配的 wheelWindows 上 CPython 3.103.14 会得到对应的 pyd wheel(整个库编译为单个 `.pyd`),其余环境得到纯 Python wheel。
```powershell
py -m pip install --index-url https://gitea.docker.antior.cn/api/packages/antior/pypi/simple dlp-io==0.1.1
```
Package 页面:<https://gitea.docker.antior.cn/antior/-/packages/pypi/dlp-io>
### 方式二:Gitea Release 页面
发布物由 `.gitea/workflows/publish-dlp-io.yaml` 从 immutable tag `dlp-io-vX.Y.Z` 自动构建并上传,全部产物在 CI runner 上编译,不依赖本地环境。
Release 页面:<https://gitea.docker.antior.cn/antior/dlp-io/releases> Release 页面:<https://gitea.docker.antior.cn/antior/dlp-io/releases>
@@ -21,13 +33,13 @@ Release 页面:<https://gitea.docker.antior.cn/antior/dlp-io/releases>
```powershell ```powershell
# 纯 Python 版(任意平台) # 纯 Python 版(任意平台)
py -m pip install https://gitea.docker.antior.cn/antior/dlp-io/releases/download/dlp-io-v0.1.0/dlp_io-0.1.0-py3-none-any.whl py -m pip install https://gitea.docker.antior.cn/antior/dlp-io/releases/download/dlp-io-v0.1.1/dlp_io-0.1.1-py3-none-any.whl
# pyd 版(示例为 CPython 3.13,其它版本替换文件名中的 cp313) # pyd 版(示例为 CPython 3.13,其它版本替换文件名中的 cp313)
py -3.13 -m pip install https://gitea.docker.antior.cn/antior/dlp-io/releases/download/dlp-io-v0.1.0/dlp_io-0.1.0-cp313-cp313-win_amd64.whl py -3.13 -m pip install https://gitea.docker.antior.cn/antior/dlp-io/releases/download/dlp-io-v0.1.1/dlp_io-0.1.1-cp313-cp313-win_amd64.whl
``` ```
也可以先从 Release 页面下载 wheel,再 `py -m pip install <文件路径>`。下载 Release 资产不需要 token;发布只能在 CI 中通过注入的 `GITHUB_TOKEN` 完成,发布凭据不得写入命令行、URL 或 tracked 文件。 也可以先从 Release 页面下载 wheel,再 `py -m pip install <文件路径>`。下载 Release 资产和 registry 包都不需要 token;发布只能在 CI 中通过注入的 `GITHUB_TOKEN` 完成,发布凭据不得写入命令行、URL 或 tracked 文件。
调用方需要安装 `dlp-io`。白名单 Python 无需安装该包:helper 源码由调用方以 UTF-8 通过 stdin 注入,且只依赖 Python 标准库,不会在磁盘创建临时 `.py` 文件。 调用方需要安装 `dlp-io`。白名单 Python 无需安装该包:helper 源码由调用方以 UTF-8 通过 stdin 注入,且只依赖 Python 标准库,不会在磁盘创建临时 `.py` 文件。
+1
View File
@@ -46,6 +46,7 @@ class WorkflowStep(Enum):
BUILD = "Build and check distribution" BUILD = "Build and check distribution"
SMOKE = "Smoke test release artifacts" SMOKE = "Smoke test release artifacts"
RELEASE = "Create Gitea release with wheels" RELEASE = "Create Gitea release with wheels"
PYPI = "Publish packages to Gitea PyPI"
@dataclass(frozen=True) @dataclass(frozen=True)