Skip to content

Commit 0e76b93

Browse files
committed
Update Android documentation and release process for GitHub-published artifacts
1 parent c87abb8 commit 0e76b93

3 files changed

Lines changed: 112 additions & 179 deletions

File tree

‎README.md‎

Lines changed: 77 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -113,83 +113,70 @@ let config = ClientConfig::builder_with_provider(arc_crypto_provider)
113113
### Android
114114
Some manual setup is required, outside of `cargo`, to use this crate on Android. In order to
115115
use Android's certificate verifier, the crate needs to call into the JVM. A small Kotlin
116-
component must be included in your app's build to support `rustls-platform-verifier`. If distributing a library, that component will need to be bundled into your release jar
117-
[as it is not yet available on Maven](https://github.com/rustls/rustls-platform-verifier/issues/115).
116+
component must be included in your app's build to support `rustls-platform-verifier`.
118117

119118
#### Gradle Setup
120119

121-
`rustls-platform-verifier` bundles the required native components in the crate, but the project must be setup to locate them
120+
`rustls-platform-verifier` distributes the required native components in a Maven-compatible format via GitHub, but the project must be setup to locate them
122121
automatically and correctly. These steps assume you are using `.gradle` Groovy files because they're the most common, but if you are using
123122
Kotlin scripts (`.gradle.kts`) for configuration instead, an example snippet is included towards the end of this section.
124123

125-
Inside of your project's `build.gradle` file, add the following code and Maven repository definition. If applicable, this should only be the one "app" sub-project that
126-
will actually be using this crate at runtime. With multiple projects running this, your Gradle configuration performance may degrade.
124+
Each snippet includes a [`ValueSource`](https://docs.gradle.org/current/javadoc/org/gradle/api/provider/ValueSource.html) implementation that obtains a
125+
Cargo-synchronized dependency version performantly, and is also friendly to Gradle's configuration cache. The version can be be selected manually instead,
126+
but runtime crashes may occur if a SemVer incompatible version is used.
127+
128+
Inside of your project's `build.gradle` file, add the following code and Maven repository definition:
127129

128-
<details>
129-
130-
<summary>App Snippets</summary>
131-
132-
`$PATH_TO_DEPENDENT_CRATE` is the relative path to the Cargo manifest (`Cargo.toml`) of any crate in your workspace that depends on `rustls-platform-verifier` from
133-
the location of your `build.gradle` file:
130+
`$PATH_TO_LOCK_FILE` is the relative path to the Cargo lockfile of your crate or workspace (`Cargo.lock`).
134131

135132
```groovy
136-
import groovy.json.JsonSlurper
137-
138-
// ...Your own script code could be here...
139133
140134
repositories {
141-
// ... Your other repositories could be here...
142135
maven {
143-
url = findRustlsPlatformVerifierProject()
144-
metadataSources.artifact()
136+
url = "https://github.com/rustls/rustls-platform-verifier/raw/maven-archive/android-release-support/maven/"
145137
}
146138
}
147139
148-
String findRustlsPlatformVerifierProject() {
149-
def dependencyText = providers.exec {
150-
it.workingDir = new File("../")
151-
commandLine("cargo", "metadata", "--format-version", "1", "--filter-platform", "aarch64-linux-android", "--manifest-path", "$PATH_TO_DEPENDENT_CRATE/Cargo.toml")
152-
}.standardOutput.asText.get()
140+
abstract class RustlsVersion implements ValueSource<String, RustlsVersion.Params> {
141+
interface Params extends ValueSourceParameters {
142+
RegularFileProperty getLockFile()
143+
}
153144
154-
def dependencyJson = new JsonSlurper().parseText(dependencyText)
155-
def manifestPath = file(dependencyJson.packages.find { it.name == "rustls-platform-verifier-android" }.manifest_path)
156-
return new File(manifestPath.parentFile, "maven").path
145+
static final String CRATE_NAME = "rustls-platform-verifier-android"
146+
147+
@Override
148+
String obtain() {
149+
def lockFile = parameters.lockFile.get().asFile
150+
def lines = lockFile.readLines()
151+
def idx = lines.findIndexOf { it.trim() == "name = \"$CRATE_NAME\"" }
152+
def version = idx < 0 ? null : lines.drop(idx + 1)
153+
.find { it.stripLeading().startsWith("version = ") }
154+
?.find(/"([^"]*)"/) { match, v -> v }
155+
if (!version) throw new GradleException("$CRATE_NAME not found in $lockFile")
156+
return version
157+
}
157158
}
158-
```
159-
160-
Then, wherever you declare your dependencies, add the following:
161-
```groovy
162-
implementation "rustls:rustls-platform-verifier:latest.release"
163-
```
164-
165-
</details>
166159
167-
<details>
168-
<summary>Library Snippets</summary>
169-
170-
```groovy
171-
import groovy.json.JsonSlurper
172-
173-
// ...Your own script code could be here...
174-
175-
File findRustlsPlatformVerifierClasses() {
176-
def dependencyText = providers.exec {
177-
it.workingDir = new File("../")
178-
commandLine("cargo", "metadata", "--format-version", "1")
179-
}.standardOutput.asText.get()
160+
def rustlsPlatformVerifierVersion = providers.of(RustlsVersion) { spec ->
161+
spec.parameters.lockFile.set(layout.projectDirectory.file($PATH_TO_LOCK_FILE))
162+
}
180163
181-
def dependencyJson = new JsonSlurper().parseText(dependencyText)
182-
def manifestFile = file(dependencyJson.packages.find { it.name == "rustls-platform-verifier-android" }.manifest_path)
183-
return new File(manifestFile.parentFile, "classes.jar")
164+
configurations.configureEach { configuration ->
165+
configuration.resolutionStrategy.eachDependency { details ->
166+
if (details.requested.group == "org.rustls" && details.requested.name == "rustls-platform-verifier") {
167+
details.useVersion(rustlsPlatformVerifierVersion.get())
168+
details.because("native component version must be identical to version of ${RustlsVersion.CRATE_NAME}")
169+
}
170+
}
184171
}
185172
```
186173

187174
Then, wherever you declare your dependencies, add the following:
188175
```groovy
189-
implementation files(findRustlsPlatformVerifierClasses())
176+
implementation "rustls:rustls-platform-verifier"
190177
```
191178

192-
</details>
179+
The dependency intentionally has no static version, it is only resolved dynamically at configuration time by the build script.
193180

194181
Cargo automatically handles finding the downloaded crate in the correct location for your project. It also handles updating the version when
195182
new releases of `rustls-platform-verifier` are published. If you only use published releases, no extra maintenance should be required.
@@ -199,49 +186,51 @@ implementation part can be located on-disk.
199186

200187
##### Kotlin and Gradle
201188

202-
<details>
203-
<summary>Kotlin script App example</summary>
204-
205189
`build.gradle.kts`:
206190
```kotlin
207-
import kotlinx.serialization.decodeFromString
208-
import kotlinx.serialization.json.Json
209-
import kotlinx.serialization.json.JsonObject
210-
import kotlinx.serialization.json.jsonArray
211-
import kotlinx.serialization.json.jsonObject
212-
import kotlinx.serialization.json.jsonPrimitive
213-
214-
buildscript {
215-
dependencies {
216-
classpath(libs.kotlinx.serialization.json)
217-
}
218-
}
219191

220192
repositories {
221-
rustlsPlatformVerifier()
193+
maven {
194+
url = uri("https://github.com/rustls/rustls-platform-verifier/raw/maven-archive/android-release-support/maven/")
195+
}
222196
}
223197

224-
fun RepositoryHandler.rustlsPlatformVerifier(): MavenArtifactRepository {
225-
@Suppress("UnstableApiUsage")
226-
val manifestPath = let {
227-
val dependencyJson = providers.exec {
228-
workingDir = File(project.rootDir, "../")
229-
commandLine("cargo", "metadata", "--format-version", "1", "--filter-platform", "aarch64-linux-android", "--manifest-path", "$PATH_TO_DEPENDENT_CRATE/Cargo.toml")
230-
}.standardOutput.asText
231-
232-
val path = Json.decodeFromString<JsonObject>(dependencyJson.get())
233-
.getValue("packages")
234-
.jsonArray
235-
.first { element ->
236-
element.jsonObject.getValue("name").jsonPrimitive.content == "rustls-platform-verifier-android"
237-
}.jsonObject.getValue("manifest_path").jsonPrimitive.content
238-
239-
File(path)
198+
abstract class RustlsVersion : ValueSource<String, RustlsVersion.Params> {
199+
interface Params : ValueSourceParameters {
200+
val lockFile: RegularFileProperty
201+
}
202+
203+
companion object {
204+
const val CRATE_NAME = "rustls-platform-verifier-android"
205+
}
206+
207+
override fun obtain(): String {
208+
val version = parameters.lockFile.get().asFile.readLines().let { lines ->
209+
val nameIdx = lines.indexOfFirst { it.trim() == "name = \"$CRATE_NAME\"" }
210+
if (nameIdx < 0) {
211+
null
212+
} else {
213+
lines.drop(nameIdx + 1)
214+
.firstOrNull { it.trimStart().startsWith("version = ") }
215+
?.substringAfter('"', "")
216+
?.substringBefore('"', "")
217+
?.takeIf { it.isNotEmpty() }
218+
}
219+
}
220+
return version?: error("$CRATE_NAME not found in Cargo.lock")
240221
}
222+
}
223+
224+
val rustlsPlatformVerifierVersion = providers.of(RustlsVersion::class.java) {
225+
parameters.lockFile.set(layout.projectDirectory.file($PATH_TO_LOCK_FILE))
226+
}
241227

242-
return maven {
243-
url = uri(File(manifestPath.parentFile, "maven").path)
244-
metadataSources.artifact()
228+
configurations.configureEach {
229+
resolutionStrategy.eachDependency {
230+
if (requested.group == "org.rustls" && requested.name == "rustls-platform-verifier") {
231+
useVersion(rustlsPlatformVerifierVersion.get())
232+
because("native component version must be identical to version of ${RustlsVersion.CRATE_NAME}")
233+
}
245234
}
246235
}
247236

@@ -253,51 +242,9 @@ dependencies {
253242

254243
`libs.version.toml`:
255244
```toml
256-
# We always use the latest release because `cargo` keeps it in sync with the associated Rust crate's version.
257-
rustls-platform-verifier = { group = "rustls", name = "rustls-platform-verifier", version = "latest.release" }
258-
```
259-
</details>
260-
261-
<details>
262-
<summary>Kotlin script Library example</summary>
263-
264-
`build.gradle.kts`:
265-
```kotlin
266-
import kotlinx.serialization.decodeFromString
267-
import kotlinx.serialization.json.Json
268-
import kotlinx.serialization.json.JsonObject
269-
import kotlinx.serialization.json.jsonArray
270-
import kotlinx.serialization.json.jsonObject
271-
import kotlinx.serialization.json.jsonPrimitive
272-
273-
buildscript {
274-
dependencies {
275-
classpath(libs.kotlinx.serialization.json)
276-
}
277-
}
278-
279-
fun findRustlsPlatformVerifierClasses(): File {
280-
val dependencyJson = providers.exec {
281-
workingDir = File(project.rootDir, "../")
282-
commandLine("cargo", "metadata", "--format-version", "1")
283-
}.standardOutput.asText
284-
285-
val path = Json.decodeFromString<JsonObject>(dependencyJson.get())
286-
.getValue("packages")
287-
.jsonArray
288-
.first { element ->
289-
element.jsonObject.getValue("name").jsonPrimitive.content == "rustls-platform-verifier-android"
290-
}.jsonObject.getValue("manifest_path").jsonPrimitive.content
291-
292-
val manifestFile = File(path)
293-
return File(manifestFile.parentFile, "classes.jar")
294-
}
295-
296-
dependencies {
297-
implementation(files(findRustlsPlatformVerifierClasses()))
298-
}
245+
# We keep the dependency unversioned because its version is selected dynamically during configuration.
246+
rustls-platform-verifier = { group = "rustls", name = "rustls-platform-verifier" }
299247
```
300-
</details>
301248

302249
#### Proguard
303250

‎admin/RELEASING.md‎

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,25 @@ In the release preparation PR, the releaser may include the following checklist
2020

2121
1. Update main crate's version in `rustls-platform-verifier/Cargo.toml`.
2222
2. If any non-test changes have been made to the `android` directory since the last release:
23-
1. Update Android artifact version in `android-release-support/Cargo.toml`
24-
2. Bump dependency version of the Android support crate in `rustls-platform-verifier/Cargo.toml` to match the new one
25-
3. Commit version increase changes on the release branch
23+
1. Update Android artifact version in `android-release-support/Cargo.toml`, and in the main crate if creating an incompatible SemVer release.
24+
2. Commit version increase changes on the release branch
2625
* We typically name these branches `rel-xxx` where `xxx` is the major version.
2726
* We typically leave these branches around for future maintenance releases.
28-
4. Run `ci/package_android_release.sh` in a UNIX compatible shell
29-
5. (Optional) `cargo publish -p rustls-platform-verifier-android --dry-run --allow-dirty`
30-
* `--allow-dirty` is required because we don't check-in the generated Maven local repository.
31-
6. (Optional) Inspect extracted archive to ensure the local Maven repository artifacts are present
32-
1. Un-tar the `rustls-platform-verifier-android-*.crate` file inside of `target/package`.
33-
2. Verify `maven/rustls/rustls-platform-verifier` contains a single `*.RELEASE` directory and that contains a `.aar` file.
34-
3. (Optional) If the releaser has an external Gradle project that uses the configuration from the README, paste the path to the
35-
unzipped package's `Cargo.toml` as a replacement for the `manifestPath` variable. Run a Gradle Sync and observe everything works.
36-
7. **Ensure that all version changes are committed to the correct branch before proceeding**. All version increases should be checked in prior
27+
3. Run `ci/package_android_release.sh` in a UNIX compatible shell
28+
4. Commit the Maven metadata updates on their own: `git commit -am "Bump Maven release to x.x.x"`. Copy the new commit's short ID.
29+
5. **Ensure that all version changes are committed to the correct branch before proceeding**. All version increases should be checked in prior
3730
to publishing on crates.io.
38-
8. Publish the Android artifacts' new version: `cargo publish -p rustls-platform-verifier-android --allow-dirty`
31+
6. Checkout the Maven storage branch: `git checkout maven-archive`. The newly built artifacts are now ready to check in.
32+
7. Add the new artifacts to storage: `git add . && git commit -m "Prepare Maven release x.x.x"`
33+
8. Sync the Maven metadata to make the new artifacts visible: `git cherry-pick $MAVEN_BUMP_COMMIT_ID`
34+
9. Publish the new changes:
35+
* `git push && git checkout rel-xxx`
36+
* Publish the new Android marker version: `cargo publish -p rustls-platform-verifier-android`
3937

4038
3. Commit main crate's version increase on the release branch
4139
4. **Ensure that all version changes are committed to the correct branch before proceeding**. All version increases should be checked in prior
4240
to publishing on crates.io.
4341
5. Publish the main crate's new version: `cargo publish -p rustls-platform-verifier`
44-
* Do **not** use `--allow-dirty` for the main crate. Only the Android component requires it and a dirty workspace elsewhere is an error.
4542
6. Follow the remaining steps in [RELEASING] to create the appropiate version tag.
46-
7. If a new Android component release was made: Before publishing the GitHub release, run `./ci/archive_android_release.sh` to create a reproducible archive
47-
containing the Android Maven components that were just published to crates.io. After creating the archive, upload it as an additional release artifact on GitHub.
48-
Then, finish the release creation like normal.
4943

5044
[RELEASING]: https://github.com/rustls/rustls/blob/main/RELEASING.md

0 commit comments

Comments
 (0)