Skip to content

WIP: Automated android branch builds #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/android_builds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
on:
pull_request:
branches:
- main
# types:
# - closed
Comment on lines +5 to +6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tagging this as a place to fixup pre-merge. I see this types arg un-commented in the upstream docs.


name: Update Android Artifacts

permissions:
contents: write
Comment on lines +10 to +11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe prudent to leave a warning at the top of this YML that changes to this workflow should be carefully vetted since it can write to the repository.

I wonder if branch protection rules to require pull-requests into main would protect against this workflow pushing main or if that would be possible through malice or bugs in the script :-/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe branch protection rules do apply to Workflow Actions as well, although I'm unsure we can exactly test the existing main protection rule without risking a little bit.

Following this comment from you:

If there's a way to use branch protection rules (or some other mechanism) to further limit the workflow to only being able to operate with the Android maven branches we intend that would be a nice extra addition 🤞

I tried adding a custom ruleset (see here) to the repository that applies to every branch except main-with-maven. I then tried to push from the CI job to a testing branch. It failed to push with a permissions error like we'd want.

The downside of it seems to be that it treats our pushes to working branches in the repository as rule bypasses as well, which causes extra noise locally 😞

remote: Bypassed rule violations for refs/heads/automated-android-branch:
remote:
remote: - Cannot update this protected ref.
remote:
To github.com:rustls/rustls-platform-verifier.git
   036ac7e..e147cb2  automated-android-branch -> automated-android-branch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside of it seems to be that it treats our pushes to working branches in the repository as rule bypasses as well, which causes extra noise locally

Interesting! Thanks for testing.

I'm in the habit of pushing branches to a fork and opening PRs from there, so I don't think the noise would bother me but I can see how its annoying for others that don't want the overhead of managing branches on a fork.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, I am personally fine with the noise since I know what it means, and the gained benefit of locking this job down tightly feels very worth it. If I ever get tired of it I could also switch to using a fork. IMO given that we should keep the rule before merging this and live with the very minor downsides.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also consider adding a CODEOWNERS rule to this file to require another approval to prevent accidentally (though not malicious) updates to this workflow that are incorrect and have security consequences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also consider adding a CODEOWNERS rule to this file to require another approval

I wouldn't mind that extra caution if you wouldn't :-)


jobs:
update-android-branch:
# if: github.event.pull_request.merged == true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag for fixup.

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Java
uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "temurin"

- name: Validate Gradle Wrappers
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # 1.1.0

# Configure a committer identity and obtain information
# about the `main-with-maven` branch.
- name: Configure Git
run: |
git config --global user.name "Android Builder"
git config --global user.email "[email protected]"
git fetch --all

# Build up the same Maven local repository that would be used for
# a crates.io release and commit it to the dedicated Android branch.
- name: Package Android AAR
run: |
echo "Syncing main branch state to the Maven hosting branch"
git checkout -b main-with-maven
# git reset --hard origin/main
git reset --hard origin/automated-android-branch

echo "Building new Android release from main"
./ci/package_android_release.sh

git add --force android-release-support/*
git commit -am "[Automated] Bundle Android component artifacts"
# git push -f origin main-with-maven
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one more pre-merge fixup

git push -f origin HEAD:testing-branch-permissions
12 changes: 8 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ on:
branches:
- main
- "*_dev"
pull_request:
merge_group:
schedule:
- cron: '0 18 * * *'
#pull_request:
# merge_group:
#schedule:
# - cron: '0 18 * * *'
Comment on lines +6 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to disable these? Leftover from testing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a testing artifact, yes. I didn't want to waste CI resources when I was pushing to this branch a lot during development. I'll make sure to undo this before merging.


name: CI
permissions:
Expand Down Expand Up @@ -196,6 +196,10 @@ jobs:
- uses: actions/checkout@v3
with:
persist-credentials: false

# Ensure only legitimate Gradle wrapper binaries can be merged into `main`
- name: Validate Gradle Wrappers
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # 1.1.0

- name: Verify release artifact
run: ./ci/verify_android_release.sh
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
/.idea

/android/verification/

# Ignore all generated Maven local repository files and folders
/android-release-support/maven/pom.xml
/android-release-support/maven/rustls/rustls-platform-verifier/**/
/android-release-support/maven/rustls/rustls-platform-verifier/maven-metadata-local.xml
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 5 additions & 74 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,76 +1,7 @@
[package]
name = "rustls-platform-verifier"
version = "0.1.0"
authors = ["ComplexSpaces <[email protected]>", "1Password"]
description = "rustls-platform-verifier supports verifying TLS certificates in rustls with the operating system verifier"
keywords = ["tls", "certificate", "verification", "os", "native"]
repository = "https://github.com/1Password/rustls-platform-verifier"
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.64.0"

exclude = [
"android/.run",
"android/gradle/**",
"android/gradle*",
"android/settings.gradle",
"android/src/androidTest",
[workspace]
members = [
"android-release-support",
"rustls-platform-verifier",
]

[lib]
name = "rustls_platform_verifier"
# Note: The `cdylib` specification is for testing only. The shared library
# is not expected to have a stable API.
crate-type = ["cdylib", "rlib"]

[features]
# Enables a C interface to use for testing where `cargo` can't be used.
# This feature is not stable, nor is the interface exported when it is enabled.
# Do not rely on this or use it in production.
ffi-testing = ["android_logger"]
# Enables APIs that expose lower-level verifier types for debugging purposes.
dbg = []
# Enables `log::debug` base64-encoded logging of all end-entity certificates processed
# by the platform's verifier.
cert-logging = ["base64"]
# Used for nicely documenting the Android-specific APIs. This feature is not stable.
docsrs = ["jni", "once_cell"]

[dependencies]
rustls = { version = "0.21", features = ["dangerous_configuration", "tls12", "logging"] }
log = { version = "0.4" }
base64 = { version = "0.21", optional = true } # Only used when the `cert-logging` feature is enabled.
jni = { version = "0.19", default-features = false, optional = true } # Only used during doc generation
once_cell = { version = "1.9", optional = true } # Only used during doc generation.

[target.'cfg(target_os = "linux")'.dependencies]
rustls-native-certs = "0.6"
once_cell = "1.9"
webpki = { package = "rustls-webpki", version = "0.101", features = ["alloc", "std"] }

[target.'cfg(target_os = "android")'.dependencies]
jni = { version = "0.19", default-features = false }
webpki = { package = "rustls-webpki", version = "0.101", features = ["alloc", "std"] }
once_cell = "1.9"
android_logger = { version = "0.13", optional = true } # Only used during testing.

[target.'cfg(target_arch = "wasm32")'.dependencies]
once_cell = "1.9"
webpki-roots = "0.25"

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
core-foundation = "0.9"
core-foundation-sys = "0.8"
security-framework = { version = "2.6", features = ["OSX_10_14"] }
security-framework-sys = { version = "2.4", features = ["OSX_10_14"] }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["wincrypt", "winerror"] }

[dev-dependencies]
tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] }
reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-manual-roots"] }

[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
features = ["dbg", "docsrs"]
resolver = "2"
79 changes: 39 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,58 +49,57 @@ component must be included in your app's build to support `rustls-platform-verif
`rustls-platform-verifier` bundles the required native components in the crate, but the project must be setup to locate them
automatically and correctly.

Firstly, create an [init script](https://docs.gradle.org/current/userguide/init_scripts.html) in your Android
Gradle project, with a filename of `init.gradle`. This is generally placed in your project's root. In your project's `settings.gradle`, add these lines:
Inside of your project's `build.gradle` file, add the following code and Maven repository definition. `$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
the location of your `build.gradle` file:

```groovy
apply from: file("./init.gradle");
// Cargo automatically handles finding the downloaded crate in the correct location
// for your project.
def veifierProjectPath = findRustlsPlatformVerifierProject()
includeBuild("${verifierProjectPath}/android/")
```

Next, the `rustls-platform-verifier` external dependency needs to be setup. Open the `init.gradle` file and add the following:
`$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 the location of your `init.gradle` file.

Alternatively, you can use `cmdProcessBuilder.directory(File("PATH_TO_ROOT"))` to change the working directory instead.

```groovy
ext.findRustlsPlatformVerifierProject = {
def cmdProcessBuilder = new ProcessBuilder(new String[] { "cargo", "metadata", "--format-version", "1", "--manifest-path", "$PATH_TO_DEPENDENT_CRATE" })
def dependencyInfoText = new StringBuffer()
import groovy.json.JsonSlurper
import groovy.transform.Memoized

// ...Your own script code could be here...

allprojects {
repositories {
// ... Your other repositories could be here...
maven {
url = findRustlsPlatformVerifierProject()
metadataSources.artifact()
}
}
}

def cmdProcess = cmdProcessBuilder.start()
cmdProcess.consumeProcessOutput(dependencyInfoText, null)
cmdProcess.waitFor()
@Memoized
String findRustlsPlatformVerifierProject() {
def dependencyText = providers.exec {
it.workingDir = new File("../")
commandLine("cargo", "metadata", "--format-version", "1", "--manifest-path", "$PATH_TO_DEPENDENT_CRATE/Cargo.toml")
}.standardOutput.asText.get()

def dependencyJson = new groovy.json.JsonSlurper().parseText(dependencyInfoText.toString())
def manifestPath = file(dependencyJson.packages.find { it.name == "rustls-platform-verifier" }.manifest_path)
return manifestPath.parent
def dependencyJson = new JsonSlurper().parseText(dependencyText)
def manifestPath = file(dependencyJson.packages.find { it.name == "rustls-platform-verifier-android" }.manifest_path)
return new File(manifestPath.parentFile, "maven").path
}
```

This script can be tweaked as best suits your project, but the `cargo metadata` invocation must be included so that the Android
implementation source can be located on disk.
Then, wherever you declare your dependencies, add the following:
```groovy
implementation "rustls:rustls-platform-verifier:latest.release"
```

If your project often updates its Android Gradle Plugin versions, you should additionally consider setting your app's project
up to override `rustls-platform-verifier`'s dependency versions. This allows your app to control what versions are used and avoid
conflicts. To do so, advertise a `versions.path` system property from your `settings.gradle`:
Cargo automatically handles finding the downloaded crate in the correct location for your project. It also handles updating the version when
new releases of `rustls-platform-verifier` are published. If you only use published releases, no extra maintainence should be required.

```groovy
ext.setVersionsPath = {
System.setProperty("versions.path", file("your/versions/path.toml").absolutePath)
}
These script snippets can be tweaked as best suits your project, but the `cargo metadata` invocation must be included so that the Android
implementation part can be located on disk.

setVersionsPath()
If your Android application makes use of Proguard for optimizations, its important to make sure that the Android verifier component isn't optimized
out because it looks like dead code. Proguard is unable to see any JNI usage, so your rules must manually opt into keeping it. THe following rule
can do this for you:
```text
-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; }
```

Finally, sync your gradle project changes. It should pick up on the `rustls-platform-verifier` Gradle project. It should finish
successfully, resulting in a `rustls` group appearing in Android Studio's project view.
After this, everything should be ready to use. Future updates of `rustls-platform-verifier` won't need any maintenance beyond the
expected `cargo update`.

#### Crate initialization

In order for the crate to call into the JVM, it needs handles from Android. These
Expand Down
18 changes: 18 additions & 0 deletions admin/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# How-to release `rustls-platform-verifier`

This document records the steps to publish new versions of the crate since it requires non-trivial preperation and ordering
that needs to be remembered due to the Android component's distribution.

## Steps

1. Update main crate'a version in `rustls-platform-verifier/Cargo.toml`, and in any additional places.
2. If any non-test changes have been made to the `android` directory since the last release:
1. Update Android artifact version in `android-release-support/Cargo.toml`
2. Bump dependency version of the Android support crate in `rustls-platform-verifier/Cargo.toml` to match the new one
3. Commit version increase changes on the release branch
4. Run `ci/package_android_release.sh` in a UNIX compatible shell
5. (Optional) `cargo publish -p rustls-platform-verifier-android --dry-run`
6. (Optional) Inspect extracted archive to ensure the local Maven repository artifacts are present
7. Publish the Android artifacts' new version: `cargo publish -p rustls-platform-verifier-android`
3. Commit main crate's version increase on the release branch
4. Publish the main crate's new version: `cargo publish -p rustls-platform-verifier`
18 changes: 18 additions & 0 deletions android-release-support/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "rustls-platform-verifier-android"
version = "0.1.0"
description = "The internal JVM support component of the rustls-platform-verifier crate. You shouldn't depend on this directly."
repository = "https://github.com/1Password/rustls-platform-verifier"
license = "MIT OR Apache-2.0"
edition = "2021"

# Explicitly include the Maven local repository for the Android component.
# While not checked into the repository, it is generated for releases and other contexts.
include = [
"src/*",
"maven/pom.xml",
"maven/rustls/rustls-platform-verifier/**/",
"maven/rustls/rustls-platform-verifier/maven-metadata-local.xml",
]

[dependencies]
Empty file.
10 changes: 10 additions & 0 deletions android-release-support/pom-template.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>rustls</groupId>
<artifactId>rustls-platform-verifier</artifactId>
<version>$VERSION</version>
<packaging>aar</packaging>
<description>The internal JVM support component of the rustls-platform-verifier Rust crate</description>
</project>
Loading