[PM-18404] Add SDK version to About screen version copy-paste (#2250)

This commit is contained in:
Matt Czech 2026-01-15 10:22:50 -06:00 committed by GitHub
parent 04c9952ce7
commit a65b92732b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 194 additions and 4 deletions

3
.gitignore vendored
View File

@ -87,3 +87,6 @@ Authenticator/Application/Support/Settings.bundle/Acknowledgements.latest_result
# AI
.claude/settings.local.json
review-*.md
# Generated files
BitwardenKit/Core/Platform/Utilities/SDKVersionInfo.swift

View File

@ -8,6 +8,9 @@ import UIKit
public protocol AppAdditionalInfo {
/// CI Build information.
var ciBuildInfo: KeyValuePairs<String, String> { get }
/// SDK version information.
var sdkVersion: String { get }
}
// MARK: - DefaultAppAdditionalInfo
@ -19,6 +22,10 @@ public struct DefaultAppAdditionalInfo: AppAdditionalInfo {
CIBuildInfo.info
}
public var sdkVersion: String {
SDKVersionInfo.version
}
public init() {}
}
@ -102,11 +109,11 @@ public extension DefaultAppInfoService {
"",
appNameAndVersionString,
bundleString,
// TODO: PM-18404 - Include server and SDK version
// sdkString,
// TODO: PM-18404 - Include server version
// serverString,
deviceString,
systemOSString,
sdkString,
additionalInfoString,
]
.compactMap(\.self)
@ -118,11 +125,11 @@ public extension DefaultAppInfoService {
[
appNameAndVersionString,
bundleString,
// TODO: PM-18404 - Include server and SDK version
// sdkString,
// TODO: PM-18404 - Include server version
// serverString,
deviceString,
systemOSString,
sdkString,
additionalInfoString,
]
.compactMap(\.self)
@ -167,6 +174,13 @@ public extension DefaultAppInfoService {
"📱 Device: \(systemDevice.modelIdentifier)"
}
/// A string containing the SDK version info.
private var sdkString: String? {
let version = appAdditionalInfo.sdkVersion
guard version != "Unknown", !version.isEmpty else { return nil }
return "🦀 SDK: \(version)"
}
/// A string containing the OS info.
private var systemOSString: String {
"🍏 System: \(systemDevice.systemName) \(systemDevice.systemVersion)"

View File

@ -124,6 +124,67 @@ class AppInfoServiceTests: BitwardenTestCase {
)
}
/// `appInfoString` includes SDK version when available.
func test_appInfoString_withSDKVersion() {
appAdditionalInfo.sdkVersion = "1.0.0-1234-abc1234"
XCTAssertEqual(
subject.appInfoString,
"""
© Bitwarden Inc. 20152025
📝 Bitwarden 1.0 (1)
📦 Bundle: com.8bit.bitwarden
📱 Device: iPhone14,2
🍏 System: iOS 16.4
🦀 SDK: 1.0.0-1234-abc1234
""",
)
}
/// `appInfoString` excludes SDK version when unknown.
func test_appInfoString_withUnknownSDKVersion() {
appAdditionalInfo.sdkVersion = "Unknown"
XCTAssertEqual(
subject.appInfoString,
"""
© Bitwarden Inc. 20152025
📝 Bitwarden 1.0 (1)
📦 Bundle: com.8bit.bitwarden
📱 Device: iPhone14,2
🍏 System: iOS 16.4
""",
)
}
/// `appInfoString` includes both SDK version and CI build info when both available.
func test_appInfoString_withSDKVersionAndCIBuildInfo() {
appAdditionalInfo.sdkVersion = "1.0.0-1234-abc1234"
appAdditionalInfo.ciBuildInfo = [
"🧱 Commit": "bitwarden/ios/main@abc123",
"💻 Build Source": "bitwarden/ios/actions/runs/123/attempts/123",
"🛠️ Compiler Flags": "DEBUG_MENU",
]
XCTAssertEqual(
subject.appInfoString,
"""
© Bitwarden Inc. 20152025
📝 Bitwarden 1.0 (1)
📦 Bundle: com.8bit.bitwarden
📱 Device: iPhone14,2
🍏 System: iOS 16.4
🦀 SDK: 1.0.0-1234-abc1234
🧱 Commit: bitwarden/ios/main@abc123
💻 Build Source: bitwarden/ios/actions/runs/123/attempts/123
🛠 Compiler Flags: DEBUG_MENU
""",
)
}
/// `debugAppInfoString` returns the app info string without copyright info.
func test_appInfoWithoutCopyrightString() {
appAdditionalInfo.ciBuildInfo = [
@ -169,8 +230,17 @@ class AppInfoServiceTests: BitwardenTestCase {
func test_appAdditionalInfo_ciBuildInfo() {
XCTAssertTrue(DefaultAppAdditionalInfo().ciBuildInfo.isEmpty)
}
/// `sdkVersion` returns the SDK version from SDKVersionInfo.
func test_appAdditionalInfo_sdkVersion() {
let info = DefaultAppAdditionalInfo()
XCTAssertNotNil(info.sdkVersion)
XCTAssertFalse(info.sdkVersion.isEmpty)
XCTAssertEqual(info.sdkVersion, SDKVersionInfo.version)
}
}
class MockAppAdditionalInfo: AppAdditionalInfo {
var ciBuildInfo: KeyValuePairs<String, String> = [:]
var sdkVersion: String = "Unknown"
}

View File

@ -0,0 +1,45 @@
import BitwardenKit
import Foundation
import Testing
/// Tests for `SDKVersionInfo`.
struct SDKVersionInfoTests {
/// `version` is not "Unknown" since the build script should have generated it.
@Test
func versionIsNotUnknown() {
#expect(
SDKVersionInfo.version != "Unknown",
"SDK version should be generated by build script, not 'Unknown'",
)
}
/// `version` is not empty.
@Test
func versionIsNotEmpty() {
#expect(!SDKVersionInfo.version.isEmpty)
}
/// `version` matches the expected semantic version format from project-common.yml.
/// Expected format: "MAJOR.MINOR.PATCH-BUILD_NUMBER-COMMIT_HASH"
/// Example: "2.0.0-3659-948b207"
@Test
func versionMatchesSemanticVersionFormat() {
let version = SDKVersionInfo.version
// Regex pattern for semantic version with build number and commit hash
// Format: X.Y.Z-BUILD-HASH where:
// - X.Y.Z is the semantic version (e.g., 2.0.0)
// - BUILD is the build number (e.g., 3659)
// - HASH is the short git commit hash (e.g., 948b207)
let pattern = #"^\d+\.\d+\.\d+-\d+-[a-f0-9]+$"#
let regex = try? NSRegularExpression(pattern: pattern, options: [])
let range = NSRange(version.startIndex..., in: version)
let matches = regex?.firstMatch(in: version, options: [], range: range)
#expect(
matches != nil,
"SDK version '\(version)' should match format 'X.Y.Z-BUILD-HASH' (e.g., '2.0.0-3659-948b207')",
)
}
}

View File

@ -0,0 +1,49 @@
#!/bin/bash
#
# SDK Version Info Generator
#
# Extracts SDK version from project-common.yml and generates SDKVersionInfo.swift
# Works in ALL builds (local + CI), not just CI builds
#
# Prerequisites:
# - project-common.yml exists with BitwardenSdk revision comment
# - Write access to BitwardenKit/Core/Platform/Utilities/
set -euo pipefail
PROJECT_FILE="${SRCROOT:-$(pwd)}/project-common.yml"
OUTPUT_FILE="${SRCROOT:-$(pwd)}/BitwardenKit/Core/Platform/Utilities/SDKVersionInfo.swift"
# Extract SDK version from YAML comment
# Format: revision: <hash> # <version>
SDK_VERSION=$(grep -A5 "BitwardenSdk:" "$PROJECT_FILE" | \
grep "revision:" | \
sed 's/.*# //' | \
tr -d '\n' || echo "Unknown")
# If extraction failed or returned the full line (no # comment), provide fallback
if [ -z "$SDK_VERSION" ] || [ "$SDK_VERSION" = "Unknown" ] || [[ "$SDK_VERSION" == *"revision:"* ]]; then
echo "❌ ERROR: Could not extract SDK version from $PROJECT_FILE"
echo "Expected format: 'revision: <hash> # <version>'"
echo "Example: 'revision: 70f9530... # 2.0.0-3659-948b207'"
echo "Tests will fail with 'Unknown' version"
SDK_VERSION="Unknown"
fi
echo "🔧 Generating SDK Version Info..."
echo "🔧 SDK Version: $SDK_VERSION"
# Generate Swift file
cat << EOF > "$OUTPUT_FILE"
/// SDK version information extracted from project-common.yml at build time.
/// WARNING: This file is automatically generated by Scripts/generate-sdk-version-info.sh
/// and should not be modified manually.
///
public enum SDKVersionInfo {
/// The BitwardenSDK version string (e.g., "2.0.0-3659-948b207").
/// Extracted from the revision comment in project-common.yml.
public static let version: String = "$SDK_VERSION"
}
EOF
echo "✅ SDK Version Info generated successfully."

View File

@ -122,6 +122,15 @@ targets:
- SourceryTarget
templateAttributes:
sourcesPath: BitwardenKit
sources:
- path: BitwardenKit/Core/Platform/Utilities/SDKVersionInfo.swift
optional: true
preBuildScripts:
- name: Generate SDK Version Info
path: Scripts/generate-sdk-version-info.sh
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/BitwardenKit/Core/Platform/Utilities/SDKVersionInfo.swift
dependencies:
- package: BitwardenSdk
- package: SwiftUIIntrospect