diff --git a/.gitea/workflows/publish-dlp-io.yaml b/.gitea/workflows/publish-dlp-io.yaml index 44329bd..ed59957 100644 --- a/.gitea/workflows/publish-dlp-io.yaml +++ b/.gitea/workflows/publish-dlp-io.yaml @@ -341,3 +341,51 @@ jobs: throw "release asset mismatch: $($publishedNames -join ', ')" } 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, ']*>(?[^<]+)', "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)" diff --git a/README.md b/README.md index 89c1582..c4e6801 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ pytest - `dlp_io-X.Y.Z.tar.gz`(sdist) - `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)。 本地只构建纯 Python 版: diff --git a/dlp_io.py b/dlp_io.py index b61c9aa..d33aa4c 100644 --- a/dlp_io.py +++ b/dlp_io.py @@ -17,7 +17,7 @@ from enum import Enum from multiprocessing.context import AuthenticationError from multiprocessing.connection import Client -__version__ = "0.1.0" +__version__ = "0.1.1" __all__ = [ "DlpConfig", diff --git a/docs/DLP_IO_LIBRARY.md b/docs/DLP_IO_LIBRARY.md index 7118094..ed8ff85 100644 --- a/docs/DLP_IO_LIBRARY.md +++ b/docs/DLP_IO_LIBRARY.md @@ -2,11 +2,23 @@ `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 会自动选择最匹配的 wheel:Windows 上 CPython 3.10–3.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 页面: + +### 方式二:Gitea Release 页面 + +发布物由 `.gitea/workflows/publish-dlp-io.yaml` 从 immutable tag `dlp-io-vX.Y.Z` 自动构建并上传,全部产物在 CI runner 上编译,不依赖本地环境。 Release 页面: @@ -21,13 +33,13 @@ Release 页面: ```powershell # 纯 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) -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` 文件。 diff --git a/tests/test_dlp_io_packaging.py b/tests/test_dlp_io_packaging.py index e92df39..4151727 100644 --- a/tests/test_dlp_io_packaging.py +++ b/tests/test_dlp_io_packaging.py @@ -46,6 +46,7 @@ class WorkflowStep(Enum): BUILD = "Build and check distribution" SMOKE = "Smoke test release artifacts" RELEASE = "Create Gitea release with wheels" + PYPI = "Publish packages to Gitea PyPI" @dataclass(frozen=True)