使用 Space Maven 仓库清理工具批量删除 JetBrains Space 包版本(compose-multiplatform CI 实践)
2026/9/13 14:40:59 网站建设 项目流程

使用 Space Maven 仓库清理工具批量删除 JetBrains Space 包版本(compose-multiplatform CI 实践)

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

导读

本文围绕 compose-multiplatform 仓库中ci/delete-packages-from-space这个独立的 Gradle 子项目展开,完整讲解如何通过其封装好的四个 Gradle 任务从 JetBrains Space Maven 仓库中批量删除指定版本的包(例如清理0.4.0-preview-*预览版本)。读完本文,你将掌握 Space 个人令牌的权限配置、项目 ID 与仓库 ID 的查询方法、基于通配符生成待删除清单的安全流程,以及带图形化确认的删除执行机制,可直接迁移用于你自己的 Space 包管理场景。

一、工具定位与整体流程

ci/delete-packages-from-space是一个完全独立的 Gradle 项目(根项目名为space-delete-package),核心思路是:借助 JetBrains Space 官方 SDK 提供的 REST API 封装,先在本地生成一份待删除清单,人工确认(取消注释)后再执行删除。整个流程被刻意拆成多个任务,避免"一条命令误删所有包"的风险:

  1. 准备 JDK 9+、生成 Space 个人令牌并配置local.properties
  2. 运行listProjectsAndPackageRepositories查询项目与仓库的 ID;
  3. 运行generateListOfPackagesToDelete(带版本通配符)生成待删清单;
  4. 人工编辑清单,取消注释需要删除的条目;
  5. 运行deletePackages,在弹出的确认对话框中二次确认后真正删除。

该项目的入口构建文件为 build.gradle.kts,其中注册了 4 个任务:checkJavaVersionlistProjectsAndPackageRepositoriesgenerateListOfPackagesToDeletedeletePackages。下面按 README 的步骤逐一展开。

二、前置环境:JDK 9+ 与个人令牌

2.1 JDK 版本约束

README 明确要求JDK 9+。这一约束由构建脚本中的checkJavaVersion任务强制执行:

val checkJavaVersion = tasks.register("checkJavaVersion") { doLast { check(JavaVersion.current() >= JavaVersion.VERSION_1_9) { "Use JDK 9+ to run this task" } } }

后续三个任务都通过dependsOn(checkJavaVersion)依赖它,因此 JDK 版本不满足时会直接构建失败。可用java -version先确认本机环境。

2.2 生成 Space 个人令牌

在 JetBrains Space 中创建 personal token,需要授予以下三项权限:

权限作用
ReadRepository读取仓库内容与包信息(用于查询、生成清单)
WriteRepository写入仓库内容(用于执行版本删除)
ViewProject查看项目信息(用于遍历项目、查询项目 ID)

三、配置 local.properties

3.1 从模板创建

项目提供了现成模板 template.local.properties,复制即可:

cp template.local.properties local.properties

模板内容如下(注意local.properties本身通常不进版本库):

#space.server.url=https://public.jetbrains.space #space.auth.token= #space.project.id= #space.repo.id= # package version to be deleted. Wilcards (*) can be used . #space.package.version=0.0.0-example-*

3.2 参数说明

参数必填说明
space.server.urlSpace 服务地址,如https://public.jetbrains.space
space.auth.token步骤 2 生成的个人令牌
space.project.id是(第 4 步后)目标项目 ID,由查询任务获得
space.repo.id是(第 4 步后)目标 Maven 仓库 ID,由查询任务获得
space.package.version是(生成清单时)待删除版本,支持*通配符,如0.4.0-preview-*

3.3 属性解析的底层逻辑

从源码看,配置解析由Space类中的property()方法完成,同时支持local.properties文件与 Gradle-P命令行参数,且文件优先级更高:

fun property(name: String): String = (localProperties.getProperty(name)) ?: (project.findProperty(name) as? String) ?: error("Property '$name' is not set")

这意味着你也可以用./gradlew generateListOfPackagesToDelete -Pspace.package.version=0.4.0-preview-*这样的方式临时覆盖配置,而不是每次都改文件。配置项被解析后通过 lazy 属性统一建模:

val token by lazy { property("space.auth.token") } val server by lazy { property("space.server.url") } val projectId by lazy { ProjectIdentifier.Id(property("space.project.id")) } val repoId by lazy { PackageRepositoryIdentifier.Id(property("space.repo.id")) } val packageVersionToDelete by lazy { property("space.package.version") }

四、查询项目 ID 与仓库 ID

运行:

./gradlew listProjectsAndPackageRepositories

该任务遍历 Space 中当前令牌可见的所有项目,并列出每个项目下的包仓库,输出形如:

Project 'my-project'(id: <project-id>) Package repository 'maven'(id: <repo-id>)

底层实现是分页遍历:forEachProject使用projects.getAllProjects(batchInfo = batch)按批拉取项目,forEachPackageRepository调用projects.packages.repositories.getRepositories(...)获取仓库列表(见 build.gradle.kts)。分页封装在forAllInAllBatches中,默认每批 100 条,按batch.totalCount判断是否遍历完成:

fun batches(batchSize: Int = 100) = generateSequence(0) { it + batchSize } .map { BatchInfo(it.toString(), batchSize) } suspend fun <T> forAllInAllBatches( getBatch: suspend (BatchInfo) -> Batch<T>, fn: suspend (T) -> Unit ) { for (batchInfo in batches()) { val batch = getBatch(batchInfo) for (element in batch.data) { fn(element) } if (batch.data.isEmpty() || (batch.next.toIntOrNull() ?: 0) >= (batch.totalCount ?: 0)) return } }

拿到结果后,把对应的值填入local.properties

space.project.id=<project-id> space.repo.id=<repo-id>

五、生成待删除包清单(安全第一)

运行:

./gradlew generateListOfPackagesToDelete -Pspace.package.version=0.4.0-preview-*

-Pspace.package.version是版本过滤条件,*为通配符(底层使用 Apache Commons IO 的FilenameUtils.wildcardMatch做匹配)。执行逻辑如下:

  1. 遍历目标仓库中的所有包(projects.packages.repositories.packages.getAllPackages);
  2. 对每个包按创建时间倒序分页拉取全部版本(getAllPackageVersionssortColumn = PackagesSortColumn.CreatedsortOrder = DESC);
  3. 若某个版本的字符串与通配符匹配,则记入待删集合PackageInfo(name, version)
  4. 将结果写入build/packages-to-delete.txt每一行默认以#注释掉
#com.example:foo:0.4.0-preview-1 #com.example:bar:0.4.0-preview-2

清单文件路径由构建脚本固定为project.buildDir.resolve("packages-to-delete.txt")(即build/packages-to-delete.txt)。任务结束时日志会提示你取消注释后再运行删除任务:

List of packages to delete is written to: <project>/build/packages-to-delete.txt Uncomment packages you want to delete and rerun the task!

5.1 格式约定

清单中每行格式为包名:版本号。删除任务读取时只处理不以#开头的行,并按:拆分,要求恰好拆成两段:

packagesFile.forEachLine { line -> if (!line.startsWith("#")) { val split = line.split(":") if (split.size == 2) { packagesToDelete.add(PackageInfo(name = split[0], version = split[1])) } } }

因此删除前请先编辑该文件,去掉想要删除条目行首的#

六、执行删除:图形化二次确认

运行:

./gradlew deletePackages

该任务的行为分两种情况:

  • 如果build/packages-to-delete.txt不存在,直接报错提示先运行generateListOfPackagesToDelete
  • 如果文件存在但没有任何未注释条目,则输出 "No packages to delete!" 提示;
  • 否则弹出 Swing 确认对话框(ConfirmDeletionDialog,位于 buildSrc/src/main/groovy/ConfirmDeletionDialog.groovy),在 800x600 的模态窗口中展示全部待删条目(包名:版本号逐行列出),提供Confirm / Cancel两个按钮,且窗口置顶显示,强制人工确认。

确认后,脚本按清单逐条调用 Space SDK 的deletePackageVersion接口执行删除,并实时输出进度:

projects.packages.repositories.packages.versions.deletePackageVersion( projectId, repoId, packageName = pkg.name, packageVersion = pkg.version ) logger.quiet("Deleted package: ${pkg.name}:${pkg.version}")

删除完成后,已执行清单会归档为同目录下的packages-to-delete.deleted.txt,原清单文件被移除,便于事后审计。因此整个删除链路存在三层防护:注释默认关闭 → 仅匹配版本通配符 → Swing 对话框二次确认。

七、SDK 依赖与运行机制补充

构建脚本依赖集中在 settings.gradle.kts 的pluginManagement.buildscript中,均作为 buildscript classpath 提供:

依赖用途
org.jetbrains:space-sdk-jvm:68349-betaJetBrains Space 官方 JVM SDK,提供SpaceHttpClientprojects.packages等类型化 API
io.ktor:ktor-client-jetty:1.5.0Ktor Jetty HTTP 引擎,作为 SDK 的底层 HTTP 客户端
org.jetbrains.kotlinx:kotlinx-datetime:0.1.1时间处理(版本排序等场景)
commons-io:commons-io:2.8.0通配符匹配(FilenameUtils.wildcardMatch)与 IO 工具

withSpaceClient使用SpaceHttpClient(client).withPermanentToken(token, serverUrl)建立带个人令牌的客户端连接,并在runBlocking+HttpClient(Jetty).use { }中保证连接资源正确释放:

fun withSpaceClient(fn: suspend SpaceHttpClientWithCallContext.() -> Unit) { runBlocking<Unit> { HttpClient(Jetty).use { client -> val space = SpaceHttpClient(client).withPermanentToken( token = token, serverUrl = server ) space.fn() } } }

项目使用 Gradle Wrapper(版本 8.13,见 gradle-wrapper.properties),在 Linux/macOS 上直接使用./gradlew,Windows 上使用gradlew.bat,无需预先安装 Gradle。

八、典型应用场景与注意事项

这套工具在 compose-multiplatform 的 CI 体系中用于清理 Space Maven 仓库中的历史/预览版本,其设计对任何需要批量治理包仓库的团队都有直接参考价值:

  • 批量清理预览版:如-Pspace.package.version=1.0.0-rc-*一键生成所有 RC 候选版本清单;
  • 磁盘与存储治理:Space 包仓库会占用存储配额,定期清理废弃快照可控制成本;
  • 安全审计:删除前先产出清单文件、删除后归档.deleted.txt,保证可追溯。

注意事项:

  1. 令牌权限最小化:仅授予删除所需的最小权限集(ReadRepositoryWriteRepositoryViewProject),避免使用全权限令牌;
  2. 通配符务必谨慎*会匹配任意字符串,使用前建议先不带通配符小范围验证,或先在清单文件中人工核对再取消注释;
  3. 删除不可恢复deletePackageVersion是真正的删除操作,Space 侧不会进入回收站,务必保证二次确认环节不被跳过;
  4. JDK 版本:运行环境必须是 JDK 9+,否则checkJavaVersion会直接失败;
  5. local.properties 不入库:令牌等敏感信息仅存在于本机local.properties,不要提交到版本控制。

九、小结

ci/delete-packages-from-space用不足两百行 Kotlin 构建脚本,把"查询项目/仓库 → 通配符生成清单 → 人工确认 → 批量删除"这条完整的 Space 包治理流水线固化成了 4 个可复用 Gradle 任务。其核心价值在于把不可逆的批量删除操作拆解为可审查、可确认的多个阶段,同时依托 JetBrains Space 官方 SDK 保持了 API 调用的类型安全与稳定性。无论你是需要为 compose-multiplatform 这类多版本发布项目清理快照,还是要为自建 Space 实例设计包清理流程,这套任务编排与安全确认模式都值得直接借鉴。

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询