Browse Source

Add script for deleting packages from Space

pull/613/head
Alexey Tsvetkov 3 years ago committed by Alexey Tsvetkov
parent
commit
d496ca9ff5
  1. 3
      ci/delete-packages-from-space/.gitignore
  2. 37
      ci/delete-packages-from-space/README.md
  3. 220
      ci/delete-packages-from-space/build.gradle.kts
  4. 3
      ci/delete-packages-from-space/buildSrc/build.gradle.kts
  5. 43
      ci/delete-packages-from-space/buildSrc/src/main/groovy/ConfirmDeletionDialog.groovy
  6. 1
      ci/delete-packages-from-space/gradle.properties
  7. BIN
      ci/delete-packages-from-space/gradle/wrapper/gradle-wrapper.jar
  8. 5
      ci/delete-packages-from-space/gradle/wrapper/gradle-wrapper.properties
  9. 185
      ci/delete-packages-from-space/gradlew
  10. 89
      ci/delete-packages-from-space/gradlew.bat
  11. 17
      ci/delete-packages-from-space/settings.gradle.kts
  12. 7
      ci/delete-packages-from-space/template.local.properties

3
ci/delete-packages-from-space/.gitignore vendored

@ -0,0 +1,3 @@
local.properties
.gradle
build

37
ci/delete-packages-from-space/README.md

@ -0,0 +1,37 @@
## How to delete packages from Space Maven repo
1. Requirements:
* JDK 9+ is required to run the script.
2. Generate a personal token:
* Add `ReadRepository`, `WriteRepository`, `ViewProject` permissions
3. Create `local.properties` from template:
```
cp template.local.properties local.properties
```
4. Set parameters in `local.properties`:
* `space.server.url` - URL of space installation;
* `space.auth.token` - your token;
5. Find out project ID and package repo ID by running:
```
./gradlew listProjectsAndPackageRepositories
```
6. Set parameters in `local.properties`:
* `space.project.id` - Project ID;
* `space.repo.id` - Repo ID;
7. Generate list of packages to delete:
```
./gradlew generateListOfPackagesToDelete -Pspace.package.version=0.4.0-preview-*
```
8. Uncomment packages to be deleted in `build/packages-to-delete.txt`.
9. Run:
```
./gradlew deletePackages
```

220
ci/delete-packages-from-space/build.gradle.kts

@ -0,0 +1,220 @@
import io.ktor.client.*
import io.ktor.client.engine.jetty.*
import kotlinx.coroutines.runBlocking
import org.apache.commons.io.FilenameUtils
import space.jetbrains.api.runtime.*
import space.jetbrains.api.runtime.resources.projects
import space.jetbrains.api.runtime.types.*
import java.util.*
import kotlin.collections.ArrayList
val checkJavaVersion = tasks.register("checkJavaVersion") {
doLast {
check(JavaVersion.current() >= JavaVersion.VERSION_1_9) {
"Use JDK 9+ to run this task"
}
}
}
tasks.register("listProjectsAndPackageRepositories") {
dependsOn(checkJavaVersion)
doLast {
Space().listProjectsAndPackageRepositories()
}
}
val packagesToDeleteFile: File
get() = project.buildDir.resolve("packages-to-delete.txt")
tasks.register("generateListOfPackagesToDelete") {
dependsOn(checkJavaVersion)
doLast {
Space().preparePackagesToDelete(packagesToDeleteFile)
}
}
tasks.register("deletePackages") {
dependsOn(checkJavaVersion)
doLast {
Space().deletePackages(packagesToDeleteFile)
}
}
fun getLocalProperties() =
Properties().apply {
val file = project.file("local.properties")
if (file.exists()) {
file.inputStream().buffered().use { input ->
load(input)
}
}
}
class Space {
private val localProperties = getLocalProperties()
fun property(name: String): String =
(localProperties.getProperty(name))
?: (project.findProperty(name) as? String)
?: error("Property '$name' is not set")
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") }
fun withSpaceClient(fn: suspend SpaceHttpClientWithCallContext.() -> Unit) {
runBlocking<Unit> {
HttpClient(Jetty).use { client ->
val space = SpaceHttpClient(client).withPermanentToken(
token = token,
serverUrl = server
)
space.fn()
}
}
}
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
}
}
suspend fun SpaceHttpClientWithCallContext.forEachPackage(fn: suspend (PackageData) -> Unit) {
forAllInAllBatches({ batch ->
projects.packages.repositories.packages.getAllPackages(
project = projectId,
repository = repoId,
query = "",
batchInfo = batch
)
}, fn)
}
suspend fun SpaceHttpClientWithCallContext.forEachVersion(packageName: String, fn: suspend (String) -> Unit) {
forAllInAllBatches({ batch ->
projects.packages.repositories.packages.versions.getAllPackageVersions(
projectId, repoId,
packageName,
query = "",
sortColumn = PackagesSortColumn.Created,
sortOrder = ColumnSortOrder.DESC,
batchInfo = batch
)
}) { fn(it.version) }
}
suspend fun SpaceHttpClientWithCallContext.forEachProject(fn: suspend (PR_Project) -> Unit) {
forAllInAllBatches({ batch ->
projects.getAllProjects(batchInfo = batch)
}, fn)
}
suspend fun SpaceHttpClientWithCallContext.forEachPackageRepository(
proj: PR_Project,
fn: (ProjectPackageRepository) -> Unit
) {
projects.packages.repositories.getRepositories(
ProjectIdentifier.Id(proj.id)
).forEach(fn)
}
}
fun Space.listProjectsAndPackageRepositories() {
withSpaceClient {
forEachProject { proj ->
logger.quiet("Project '${proj.name}'(id: ${proj.id})")
forEachPackageRepository(proj) { repo ->
logger.quiet(" Package repository '${repo.name}(id: ${repo.id})'")
}
}
}
}
fun Space.preparePackagesToDelete(packagesFile: File) {
logger.quiet("Preparing list of packages for deletion. This may take some time...")
val packagesToDelete = ArrayList<PackageInfo>()
withSpaceClient {
forEachPackage { pkg ->
forEachVersion(pkg.name) { version ->
if (FilenameUtils.wildcardMatch(version, packageVersionToDelete)) {
packagesToDelete.add(PackageInfo(name = pkg.name, version = version))
}
}
logger.quiet("Analyzed package: ${pkg.name}")
}
}
packagesFile.parentFile.mkdirs()
packagesFile.writer().buffered().use { writer ->
packagesToDelete.forEach {
writer.write("#")
writer.write(it.name)
writer.write(":")
writer.write(it.version)
writer.newLine()
}
}
logger.quiet("List of packages to delete is written to:\n $packagesFile")
logger.quiet("Uncomment packages you want to delete and rerun the task!")
}
fun Space.deletePackages(packagesFile: File) {
if (!packagesFile.exists()) {
error("A list of packages to delete does not exist, run 'generateListOfPackagesToDelete' first")
}
val packagesToDelete = ArrayList<PackageInfo>()
packagesFile.forEachLine { line ->
if (!line.startsWith("#")) {
val split = line.split(":")
if (split.size == 2) {
packagesToDelete.add(PackageInfo(name = split[0], version = split[1]))
}
}
}
if (packagesToDelete.isEmpty()) {
logger.quiet("No packages to delete!")
logger.quiet("Uncomment packages to delete them: ${packagesFile}")
} else {
val allPackagesToBeDeletedText = packagesToDelete.joinToString("\n") { "${it.name}:${it.version}" }
if (ConfirmDeletionDialog.confirm(allPackagesToBeDeletedText)) {
logger.quiet("Deleting ${packagesToDelete.size} packages...")
withSpaceClient {
for (pkg in packagesToDelete) {
projects.packages.repositories.packages.versions.deletePackageVersion(
projectId, repoId, packageName = pkg.name, packageVersion = pkg.version
)
logger.quiet("Deleted package: ${pkg.name}:${pkg.version}")
}
}
packagesFile.copyTo(packagesFile.resolveSibling(packagesFile.nameWithoutExtension + ".deleted.txt"))
packagesFile.delete()
System.exit(0)
}
}
}
class PackageInfo(
val name: String,
val version: String
)

3
ci/delete-packages-from-space/buildSrc/build.gradle.kts

@ -0,0 +1,3 @@
plugins {
groovy
}

43
ci/delete-packages-from-space/buildSrc/src/main/groovy/ConfirmDeletionDialog.groovy

@ -0,0 +1,43 @@
import groovy.swing.SwingBuilder
import javax.swing.*
class ConfirmDeletionDialog {
static Boolean confirm(String packagesToBeDeleted) {
def isConfirmed = false
def sb = new SwingBuilder()
sb.edt {
dialog(
modal: true,
title: 'Confirm packages deletion',
alwaysOnTop: true,
size: [800, 600],
resizable: false,
locationRelativeTo: null,
pack: true,
show: true
) {
vbox {
hbox() {
label(text: "Are you shure you want to delete these packages?")
}
scrollPane(verticalScrollBarPolicy: JScrollPane.VERTICAL_SCROLLBAR_ALWAYS) {
textArea(text: packagesToBeDeleted, columns: 60, rows: 20)
}
hbox() {
button(text: 'Confirm', actionPerformed: {
isConfirmed = true
dispose()
})
button(text: 'Cancel', actionPerformed: {
isConfirmed = false
dispose()
})
}
}
}
}
sb.dispose()
return isConfirmed
}
}

1
ci/delete-packages-from-space/gradle.properties

@ -0,0 +1 @@
kotlin.code.style=official

BIN
ci/delete-packages-from-space/gradle/wrapper/gradle-wrapper.jar vendored

Binary file not shown.

5
ci/delete-packages-from-space/gradle/wrapper/gradle-wrapper.properties vendored

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
ci/delete-packages-from-space/gradlew vendored

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
ci/delete-packages-from-space/gradlew.bat vendored

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

17
ci/delete-packages-from-space/settings.gradle.kts

@ -0,0 +1,17 @@
pluginManagement {
buildscript {
repositories {
maven("https://maven.pkg.jetbrains.space/public/p/space/maven")
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("io.ktor:ktor-client-jetty:1.5.0")
classpath("org.jetbrains:space-sdk-jvm:68349-beta")
classpath("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
classpath("commons-io:commons-io:2.8.0")
}
}
}
rootProject.name = "space-delete-package"

7
ci/delete-packages-from-space/template.local.properties

@ -0,0 +1,7 @@
#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-*
Loading…
Cancel
Save