Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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
81 changes: 71 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ aliases:
- /^release-sync-ios-\d+\.\d+\.\d+-beta\d+$/
branches:
ignore: /.*/
- snapshot-android-tag-filter: &snapshot-android-tag-filter
filters:
tags:
only:
- /^snapshot-sync-android-\d+\.\d+\.\d+$/
branches:
ignore: /.*/

#============================================================================
# Executors
Expand All @@ -172,15 +179,10 @@ executors:
working_directory: *workspace
macos-kotlin-ios-executor-m1:
macos:
xcode: "15.1.0"
resource_class: macos.m1.medium.gen1
xcode: "16.4.0"
resource_class: m4pro.medium
working_directory: *workspace
environment:
# when bumping xcode version - update the IOS_SIMULATOR_DEVICE_ID
# See: https://circleci.com/docs/using-macos/
# (on m1 executor deviceId is different than specified in the doc above (the doc is for x86_64).
# Run `xcrun simctl list` on a circleci node to get full device ids list)
IOS_SIMULATOR_DEVICE_ID: A2CD73C7-D03C-47CA-858B-94C0D2A9475A
# IOS_SIMULATOR_DEVICE_ID is auto-detected by run-tests-kotlin-ios.rb

#============================================================================
# Commands: All
Expand Down Expand Up @@ -374,8 +376,8 @@ commands:
- run:
name: "Generate project settings: << parameters.gradle-project-dir >>"
command: |
echo "sonatype.username=$SONATYPE_USERNAME" >> gradle.properties
echo "sonatype.password=$SONATYPE_PASSWORD" >> gradle.properties
echo "sonatype.username=$MAVEN_CENTRAL_TOKEN_USERNAME" >> gradle.properties
echo "sonatype.password=$MAVEN_CENTRAL_TOKEN_PASSWORD" >> gradle.properties

echo "signing.keyId=$SONATYPE_SIGNING_KEY_ID" >> gradle.properties
echo "signing.password=$SONATYPE_SIGNING_PASSWORD" >> gradle.properties
Expand Down Expand Up @@ -1026,6 +1028,18 @@ jobs:
gradle-project-dir: *android_sync_root_project_dir
input-file: gradle-output.log

publish-snapshot-sdk-android:
executor: linux-android-executor
description: "Publish Snapshot"
steps:
- checkout
- attach_workspace:
at: *workspace
- restore_gradle_cache
- run_gradle_target:
gradle-project-dir: *android_sync_root_project_dir
gradle-target: publishToSonatype -Pdisable-metadata-subtasks

promote-to-release-sdk-android:
executor: linux-android-executor
description: "Promote to release"
Expand Down Expand Up @@ -1251,3 +1265,50 @@ workflows:
- rtd-ci-build-publish-snapshot
- rtd-sdk-slack-secrets

#=========================================================================
# Workflows: Publish Snapshot
#=========================================================================

publish-snapshot-android:
jobs:
- prepare-dependencies:
<<: *snapshot-android-tag-filter
context:
- rtd-ci-tokens
- rtd-ci-test-instance-config
- rtd-ci-build-publish-snapshot
- rtd-android-chat-demo-app-release
- build-twilsock-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- prepare-dependencies
- build-shared-internal-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- prepare-dependencies
- build-shared-public-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- prepare-dependencies
- build-sync-sdk-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- prepare-dependencies
- build-sync-docs-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- prepare-dependencies
- publish-snapshot-sdk-android:
<<: *snapshot-android-tag-filter
context: rtd-ci-build-publish-snapshot
requires:
- build-twilsock-android
- build-shared-internal-android
- build-shared-public-android
- build-sync-sdk-android
- build-sync-docs-android
6 changes: 1 addition & 5 deletions BuildScripts/run-sdk-android-tests-in-gcloud.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ $BUILDSCRIPTS_DIR/run-android-tests-in-gcloud.sh \
-"$RENAME_SUFFIX" \
"" \
$SHARDS_COUNT \
model=Nexus5X,version=24 \
model=Nexus5X,version=25 \
model=Nexus5X,version=26 \
model=HWMHA,version=24 \
model=cactus,version=27 \
model=blueline,version=28 \
model=redfin,version=30 \
model=oriole,version=31 \
model=oriole,version=32 \
Expand Down
34 changes: 32 additions & 2 deletions BuildScripts/run-tests-kotlin-ios.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,41 @@ def exec_command(message, command, fail_on_error: true)

TEST_BINARY = ARGV[0]
MAX_RETRIES = ARGV[1] ? ARGV[1].to_i : 2
IOS_SIMULATOR_DEVICE_ID = ENV['IOS_SIMULATOR_DEVICE_ID'] || (raise "IOS_SIMULATOR_DEVICE_ID env var is not set")

# Auto-detect an iPhone simulator if IOS_SIMULATOR_DEVICE_ID is not set
def detect_simulator_device_id
output = `xcrun simctl list devices available -j`
require 'json'
devices = JSON.parse(output)["devices"]

# Find an available iPhone simulator (prefer iPhone 15 Pro or any iPhone)
devices.each do |runtime, device_list|
next unless runtime.include?("iOS")
device_list.each do |device|
if device["name"].include?("iPhone 15 Pro")
return device["udid"]
end
end
end

# Fallback to any iPhone
devices.each do |runtime, device_list|
next unless runtime.include?("iOS")
device_list.each do |device|
if device["name"].include?("iPhone")
return device["udid"]
end
end
end

raise "No iPhone simulator found"
end

IOS_SIMULATOR_DEVICE_ID = ENV['IOS_SIMULATOR_DEVICE_ID'] || detect_simulator_device_id

puts "TEST_BINARY: #{TEST_BINARY}"
puts "MAX_RETRIES: #{MAX_RETRIES}"
puts "IOS_SIMULATOR_DEVICE_ID: #{ENV['IOS_SIMULATOR_DEVICE_ID']}"
puts "IOS_SIMULATOR_DEVICE_ID: #{IOS_SIMULATOR_DEVICE_ID}"

exec_command "Booting simulator:", "xcrun simctl boot #{IOS_SIMULATOR_DEVICE_ID}", fail_on_error: false

Expand Down
59 changes: 54 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,23 @@ if (project.hasProperty('disable-metadata-subtasks')) {
apply from: "./disable-metadata-subtasks.gradle"
}

// Determine if this is a snapshot build for nexusPublishing configuration
def gitTagForNexus = findProperty("gitTag") ?: ""
def isSnapshotForNexus = (gitTagForNexus =~ /^snapshot-sync-android-\d+\.\d+\.\d+$/).find()

nexusPublishing {
repositories {
sonatype {
nexusUrl = uri("https://ossrh-staging-api.central.sonatype.com/service/local/")
snapshotRepositoryUrl = uri("https://central.sonatype.com/repository/maven-snapshots/")

username = project.getProperty('sonatype.username')
password = project.getProperty('sonatype.password')
packageGroup = "com.twilio"

// useStaging = false for snapshots (publish directly to snapshot repo)
// useStaging = true for releases (publish to staging repo first)
useStaging = !isSnapshotForNexus
}
}
}
Expand Down Expand Up @@ -110,6 +120,18 @@ task packageDocs {
dependsOn 'packageDokka'
}

def gitHash = findProperty("gitHash") ?: "HEAD"
def gitTag = findProperty("gitTag") ?: ""

// For snapshot builds, only append -SNAPSHOT to twilsock version
// shared-internal and shared-public use their released versions
def isSnapshotBuild = (gitTag =~ /^snapshot-sync-android-\d+\.\d+\.\d+$/).find()
if (isSnapshotBuild) {
twilsockVersion = twilsockVersion + "-SNAPSHOT"
// Don't snapshot shared-internal/shared-public - use released versions
println "Snapshot build detected - twilsock version suffixed with -SNAPSHOT"
}

def needPublishTwilsock = needPublishTwilsock(twilsockVersion)
println "needPublishTwilsock: $needPublishTwilsock"

Expand All @@ -119,8 +141,6 @@ println "need to publish shared-internal: $needPublishSharedInternal"
def needPublishSharedPublic = needPublishSharedPublic(sharedPublicVersion)
println "need to publish shared-public: $needPublishSharedPublic"

def gitHash = findProperty("gitHash") ?: "HEAD"
def gitTag = findProperty("gitTag") ?: ""
def (isSyncReleaseCandidate, syncVersion) = generatePublishVersionNames(gitHash, gitTag)

println "publishing isSyncReleaseCandidate?: $isSyncReleaseCandidate"
Expand Down Expand Up @@ -235,7 +255,7 @@ if (needPublishSharedPublic) {
tasks['generateMetadataFileForSharedPublicReleasePublication'].mustRunAfter(':shared-public:reZipReleaseAar')
}

if (isSyncReleaseCandidate) {
if (isSyncReleaseCandidate || isSnapshotBuild) {
// this line is parsed on circleci. don't touch it!
// see: $MONOREPO/BuildScripts/push-sonatype-git-tag.sh
println "Publishing sync v$syncVersion"
Expand Down Expand Up @@ -318,14 +338,20 @@ if (isSyncReleaseCandidate) {
}

tasks['publishToSonatype'].with {
dependsOn(validateIfSingleRcTagOnCommit)
mustRunAfter(validateIfSingleRcTagOnCommit)
dependsOn(validatePublishTag)
mustRunAfter(validatePublishTag)
}

tasks['releaseSonatypeStagingRepository'].dependsOn(checkReleaseVersionMatchesRC)

} // gradle.projectsEvaluated

task validatePublishTag {
doLast {
findPublishTag() // throws exception if no valid tag found
}
}

task validateIfSingleRcTagOnCommit {
doLast {
findRCTag() // throws exception if zero or more than one RC tag found
Expand All @@ -342,6 +368,29 @@ task checkReleaseVersionMatchesRC {
}
}

def findPublishTag() {
def isRcTag = { tag ->
(tag =~ /^release-sync-android-\d+\.\d+\.\d+-rc\d+$/).find()
}
def isSnapshotTag = { tag ->
(tag =~ /^snapshot-sync-android-\d+\.\d+\.\d+$/).find()
}

def tags = getGitTagList(gitHash)
.findAll { isRcTag(it) || isSnapshotTag(it) }

if (tags.size > 1) {
throw new GradleException("Attempt to build with multiple publish tags on same commit")
}

if (tags.size == 0) {
throw new GradleException("Cannot find RC or snapshot tag")
}

println "Found publish tag: ${tags[0]}"
return tags[0]
}

def findRCTag() {
def isRcTag = { tag ->
(tag =~ /^release-sync-android-\d+\.\d+\.\d+-rc\d+$/).find()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ class TestContinuationTokenStorage : ContinuationTokenStorage {

class TestConnectivityMonitor : ConnectivityMonitor {
override val isNetworkAvailable = true
override val defaultNetworkId: String? = null
override var onChanged: () -> Unit = {}
override var onDefaultNetworkChanged: (networkId: String?) -> Unit = {}
override fun start() = Unit
override fun stop() = Unit
}
Expand Down
3 changes: 3 additions & 0 deletions sdk/twilsock/api/twilsock.api
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,12 @@ public final class com/twilio/twilsock/commands/CommandsScheduler {
}

public abstract interface class com/twilio/twilsock/util/ConnectivityMonitor {
public abstract fun getDefaultNetworkId ()Ljava/lang/String;
public abstract fun getOnChanged ()Lkotlin/jvm/functions/Function0;
public abstract fun getOnDefaultNetworkChanged ()Lkotlin/jvm/functions/Function1;
public abstract fun isNetworkAvailable ()Z
public abstract fun setOnChanged (Lkotlin/jvm/functions/Function0;)V
public abstract fun setOnDefaultNetworkChanged (Lkotlin/jvm/functions/Function1;)V
public abstract fun start ()V
public abstract fun stop ()V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,30 @@ internal actual class ConnectivityMonitorImpl actual constructor(private val cor
}
}

override val defaultNetworkId: String?
get() = try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
connectivityManager?.activeNetwork?.toString()
} else {
null
}
} catch (e: Exception) {
logger.w("Cannot get defaultNetworkId", e)
null
}

override var onChanged: () -> Unit = {}
override var onDefaultNetworkChanged: (networkId: String?) -> Unit = {}

private val connectionStatusCallback by lazy { ConnectionStatusCallback() }
private val defaultNetworkCallback by lazy { DefaultNetworkCallback() }

override fun start() {
try {
connectivityManager?.registerNetworkCallback(NetworkRequest.Builder().build(), connectionStatusCallback)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager?.registerDefaultNetworkCallback(defaultNetworkCallback)
}
} catch (e: Exception) {
logger.w("Cannot registerNetworkCallback (probably app doesn't have ACCESS_NETWORK_STATE " +
"permission? Considering network as always available)", e)
Expand All @@ -52,6 +69,9 @@ internal actual class ConnectivityMonitorImpl actual constructor(private val cor
override fun stop() {
try {
connectivityManager?.unregisterNetworkCallback(connectionStatusCallback)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager?.unregisterNetworkCallback(defaultNetworkCallback)
}
} catch (e: Exception) {
logger.w("Cannot unregisterNetworkCallback (probably app doesn't have ACCESS_NETWORK_STATE " +
"permission?", e)
Expand Down Expand Up @@ -97,4 +117,16 @@ internal actual class ConnectivityMonitorImpl actual constructor(private val cor
isNetworkAvailable = activeNetworks.isNotEmpty()
}
}

private inner class DefaultNetworkCallback : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
logger.d { "Default network changed to: $network" }
coroutineScope.launch { onDefaultNetworkChanged(network.toString()) }
}

override fun onLost(network: Network) {
logger.d { "Default network lost: $network" }
coroutineScope.launch { onDefaultNetworkChanged(null) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ open class BaseTwilsockTest {
lateinit var twilsockObserver: TwilsockObserver

var onConnectivityChanged = {}
var onDefaultNetworkChanged: (String?) -> Unit = {}

@BeforeTest
open fun setUp() {
setupTestLogging()
MockKAnnotations.init(this@BaseTwilsockTest, relaxUnitFun = true)
every { connectivityMonitor.isNetworkAvailable } returns true
every { connectivityMonitor.defaultNetworkId } returns "defaultNetwork"
every { connectivityMonitor.onChanged = any() } propertyType onConnectivityChanged::class answers { onConnectivityChanged = value }
every { connectivityMonitor.onDefaultNetworkChanged = any() } propertyType onDefaultNetworkChanged::class answers { onDefaultNetworkChanged = value }
every { twilsockTransportFactory(any(), any(), any(), any()) } returns twilsockTransport
clearTwilsockObserverMock()

Expand Down
Loading