-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathpublishFileByFile.kt
More file actions
160 lines (138 loc) · 6.6 KB
/
Copy pathpublishFileByFile.kt
File metadata and controls
160 lines (138 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package nmcp.transport
import gratatouille.tasks.FileWithPath
import gratatouille.tasks.GInputFiles
import java.security.MessageDigest
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import nmcp.internal.task.ArtifactMetadata
import nmcp.internal.task.Gav
import nmcp.internal.task.VersionMetadata
import nmcp.internal.task.replaceBuildNumber
import nmcp.internal.task.xml
import okio.ByteString.Companion.toByteString
fun publishFileByFile(
transport: Transport,
inputFiles: GInputFiles,
) {
val allFiles = inputFiles.filter { it.file.isFile }
val gavPaths = allFiles.filter { it.normalizedPath.endsWith(".pom") || it.normalizedPath.endsWith(".module") }
.map { it.normalizedPath.substringBeforeLast('/') }
.distinct()
gavPaths.forEach { gavPath ->
val gav = Gav.from(gavPath)
val version = gav.version
val gavFiles = allFiles.filter { it.normalizedPath.startsWith(gavPath) }
/**
* This is a proper directory containing artifacts
*/
if (version.endsWith("-SNAPSHOT")) {
/**
* This is a snapshot:
* - update the [version metadata](https://maven.apache.org/repositories/metadata.html).
* - path the file names to include the new build number.
*
* See https://s01.oss.sonatype.org/content/repositories/snapshots/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example.
*
* For snapshots, it's not 100% clear who owns the metadata as the repository might expire some snapshot and therefore need to rewrite the
* metadata to keep things consistent. This means, there are 2 possibly concurrent writers to maven-metadata.xml: the repository and the
* publisher. Hopefully it's not too much of a problem in practice.
*
* See https://github.com/gradle/gradle/blob/d1ee068b1ee7f62ffcbb549352469307781af72e/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java#L70.
*/
val versionMetadataPath = "$gavPath/maven-metadata.xml"
val localVersionMetadataFile = gavFiles.firstOrNull {
it.normalizedPath == versionMetadataPath
}
if (localVersionMetadataFile == null) {
error("Nmcp: cannot find version maven-metadata.xml in '$gavPath'")
}
val localVersionMetadata =
xml.decodeFromString<VersionMetadata>(localVersionMetadataFile.file.readText())
val remoteVersionMetadata = transport.get(versionMetadataPath)
val buildNumber = if (remoteVersionMetadata == null) {
1
} else {
xml.decodeFromString<VersionMetadata>(remoteVersionMetadata.use { it.readUtf8() }).versioning.snapshot.buildNumber + 1
}
val newVersionMetadata = localVersionMetadata.copy(
versioning = localVersionMetadata.versioning.copy(
snapshot = localVersionMetadata.versioning.snapshot.copy(buildNumber = buildNumber),
),
)
val renamedFiles = gavFiles.mapNotNull {
if (it.file.name.startsWith("maven-metadata.xml")) {
return@mapNotNull null
}
val newName = it.file.name.replaceBuildNumber(gav.artifactId, gav.version, buildNumber)
FileWithPath(it.file, "$gavPath/$newName")
}
transport.uploadFiles(renamedFiles)
val bytes = encodeToXml(newVersionMetadata).toByteArray()
transport.put(versionMetadataPath, bytes)
setOf("md5", "sha1", "sha256", "sha512").forEach {
transport.put("$versionMetadataPath.$it", bytes.digest(it.uppercase()))
}
} else {
/**
* Not a snapshot, plainly update all the files
*/
transport.uploadFiles(gavFiles)
}
/**
* Update the [artifact metadata](https://maven.apache.org/repositories/metadata.html).
*
* See https://repo1.maven.org/maven2/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example.
*/
val index = gavPath.lastIndexOf('/')
check (index != -1) {
"Nmcp: invalid gav path: '$gavPath'"
}
val artifactMetadataPath = "${gavPath.substring(0, index)}/maven-metadata.xml"
val localArtifactMetadataFile = allFiles.firstOrNull { it.normalizedPath == artifactMetadataPath }
if (localArtifactMetadataFile == null) {
error("Nmcp: cannot find artifact metadata at '${artifactMetadataPath}'")
}
val localArtifactMetadata =
xml.decodeFromString<ArtifactMetadata>(localArtifactMetadataFile.file.readText())
val remoteArtifactMetadata = transport.get(artifactMetadataPath)
val existingVersions = if (remoteArtifactMetadata != null) {
xml.decodeFromString<ArtifactMetadata>(remoteArtifactMetadata.use { it.readUtf8() }).versioning.versions
} else {
emptyList()
}
/**
* See https://github.com/gradle/gradle/blob/cb0c615fb8e3690971bb7f89ad80f58943360624/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java#L116.
*/
val versions = existingVersions.toMutableList()
if (!versions.none { it == gav.version }) {
versions.add(gav.version)
}
val newArtifactMetadata = localArtifactMetadata.copy(
versioning = localArtifactMetadata.versioning.copy(
versions = versions,
),
)
val bytes = encodeToXml(newArtifactMetadata).toByteArray()
transport.put(artifactMetadataPath, bytes)
setOf("md5", "sha1", "sha256", "sha512").forEach {
transport.put("$artifactMetadataPath.$it", bytes.digest(it.uppercase()))
}
}
}
private fun Transport.uploadFiles(filesWithPath: List<FileWithPath>) {
filesWithPath.sortedBy { it.normalizedPath }.forEach {
put(it.normalizedPath, it.file)
}
}
/**
* Helper function to add the `<?xml...` preamble as I haven't found how to do it with xmlutils
*/
internal inline fun <reified T> encodeToXml(t: T): String {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml.encodeToString(t)
}
private fun ByteArray.digest(name: String): String {
val md = MessageDigest.getInstance(name)
md.update(this, 0, size)
val digest = md.digest()
return digest.toByteString().hex()
}