From 9d24cfa7721c5bbede23e863f80ba329246da3fc Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Mon, 9 Mar 2026 09:52:44 +0100 Subject: [PATCH 1/5] chore: reformat codebase --- android/src/test/java/voltra/SmokeTest.kt | 8 ++-- .../ChannelUpdatesTestingScreen.tsx | 4 +- .../testing-grounds/TestingGroundsScreen.tsx | 3 +- .../GradientPlaygroundScreen.tsx | 4 +- generator/generate-types.ts | 44 +++++++++---------- ios/Tests/VoltraSharedTests/SmokeTests.swift | 2 +- ios/shared/VoltraEvent.swift | 2 +- ios/shared/VoltraInteractionIntent.swift | 4 +- ios/target/VoltraHomeWidget.swift | 12 +---- ios/tests/JSGradientParserTests.swift | 2 +- ios/ui/Layout/VoltraFlexStackLayout.swift | 29 +++++++++--- ios/ui/Protocols/VoltraView.swift | 4 +- ios/ui/Style/DecorationStyle.swift | 1 - ios/ui/Style/JSColorParser.swift | 18 ++++---- ios/ui/Style/JSStyleParser.swift | 8 ++-- ios/ui/Style/LayoutStyle.swift | 14 +++--- ios/ui/Views/VoltraGauge.swift | 1 - ios/ui/Views/VoltraGroupBox.swift | 1 - ios/ui/Views/VoltraLinearGradient.swift | 4 +- plugin/src/ios-widget/files/infoPlist.ts | 8 ++-- plugin/src/ios-widget/index.ts | 3 +- 21 files changed, 90 insertions(+), 86 deletions(-) diff --git a/android/src/test/java/voltra/SmokeTest.kt b/android/src/test/java/voltra/SmokeTest.kt index 55f6b501..fb06f383 100644 --- a/android/src/test/java/voltra/SmokeTest.kt +++ b/android/src/test/java/voltra/SmokeTest.kt @@ -4,8 +4,8 @@ import org.junit.Assert.assertEquals import org.junit.Test class SmokeTest { - @Test - fun oneEqualsOne() { - assertEquals(1, 1) - } + @Test + fun oneEqualsOne() { + assertEquals(1, 1) + } } diff --git a/example/screens/testing-grounds/ChannelUpdatesTestingScreen.tsx b/example/screens/testing-grounds/ChannelUpdatesTestingScreen.tsx index e5cece13..1588775c 100644 --- a/example/screens/testing-grounds/ChannelUpdatesTestingScreen.tsx +++ b/example/screens/testing-grounds/ChannelUpdatesTestingScreen.tsx @@ -58,9 +58,7 @@ export default function ChannelUpdatesTestingScreen() { Channel-Based Updates - - Start a minimal Live Activity subscribed to a specific broadcast channel. - + Start a minimal Live Activity subscribed to a specific broadcast channel. Live Activity Channel diff --git a/example/screens/testing-grounds/TestingGroundsScreen.tsx b/example/screens/testing-grounds/TestingGroundsScreen.tsx index 38183630..5f420bda 100644 --- a/example/screens/testing-grounds/TestingGroundsScreen.tsx +++ b/example/screens/testing-grounds/TestingGroundsScreen.tsx @@ -88,8 +88,7 @@ const TESTING_GROUNDS_SECTIONS = [ { id: 'channel-updates', title: 'Channel-Based Updates', - description: - 'Start a minimal Live Activity bound to a broadcast channel ID and test server-driven updates.', + description: 'Start a minimal Live Activity bound to a broadcast channel ID and test server-driven updates.', route: '/testing-grounds/channel-updates', }, ] diff --git a/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx b/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx index 7702c6f8..a5740b17 100644 --- a/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx +++ b/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx @@ -190,7 +190,9 @@ export default function GradientPlaygroundScreen() { {/* rgba inside gradient */} RGBA Inside Gradient - linear-gradient(to right, rgba(255,0,0,0.8) 0%, rgba(0,0,255,0.3) 100%) + + linear-gradient(to right, rgba(255,0,0,0.8) 0%, rgba(0,0,255,0.3) 100%) + ) => { } } +const runFormatScript = (scriptName: string, stepLabel: string) => { + console.log(`Step ${stepLabel}: Running npm run ${scriptName}...`) + try { + const result = execSync(`npm run ${scriptName}`, { encoding: 'utf-8', cwd: ROOT_DIR }) + if (result.trim()) { + console.log(result.trim()) + } + } catch (error: any) { + console.warn(` Warning: npm run ${scriptName} exited with code ${error.status ?? 'unknown'}`) + if (error.stdout) console.log(' stdout:', error.stdout.trim()) + if (error.stderr) console.log(' stderr:', error.stderr.trim()) + } + console.log() +} + const main = () => { console.log('🚀 Generating types from schemas...\n') @@ -67,29 +83,6 @@ const main = () => { writeFiles(TS_PROPS_OUTPUT_DIR, tsJsxResult.props) console.log() - // Step 4: Lint and fix generated TypeScript files - console.log('Step 4: Running eslint --fix on generated TypeScript files...') - const generatedTsFiles = Object.keys(tsJsxResult.props).map((filename) => path.join(TS_PROPS_OUTPUT_DIR, filename)) - if (generatedTsFiles.length > 0) { - const eslintCommand = `npx eslint --fix ${generatedTsFiles.join(' ')}` - try { - const result = require('child_process').execSync(eslintCommand, { encoding: 'utf-8', cwd: ROOT_DIR }) - if (result) { - console.log(' ESLint output:', result.trim()) - } - } catch (error: any) { - // ESLint might exit with code 1 if it fixed issues, which is normal - if (error.status !== 0 && error.status !== 1) { - console.warn(' Warning: ESLint exited with code', error.status) - if (error.stdout) console.log(' stdout:', error.stdout.trim()) - if (error.stderr) console.log(' stderr:', error.stderr.trim()) - } else if (error.stdout) { - console.log(' ESLint output:', error.stdout.trim()) - } - } - } - console.log() - // Step 5: Generate Swift parameter types console.log('Step 5: Generating Swift parameter types...') const swiftParameterFiles = generateSwiftParameters(componentsData) @@ -149,6 +142,11 @@ const main = () => { writeFiles(KOTLIN_GENERATED_DIR, kotlinShortNameFiles) console.log() + // Step 8: Format generated output + runFormatScript('format:js:fix', '8') + runFormatScript('format:kotlin:fix', '9') + runFormatScript('format:swift:fix', '10') + console.log('✅ Generation complete!\n') console.log('Generated files:') console.log( diff --git a/ios/Tests/VoltraSharedTests/SmokeTests.swift b/ios/Tests/VoltraSharedTests/SmokeTests.swift index d1e3a704..70de4920 100644 --- a/ios/Tests/VoltraSharedTests/SmokeTests.swift +++ b/ios/Tests/VoltraSharedTests/SmokeTests.swift @@ -1,5 +1,5 @@ -import XCTest @testable import VoltraSharedCore +import XCTest final class SmokeTests: XCTestCase { func testOneEqualsOne() { diff --git a/ios/shared/VoltraEvent.swift b/ios/shared/VoltraEvent.swift index 732ffe8f..0bb27104 100644 --- a/ios/shared/VoltraEvent.swift +++ b/ios/shared/VoltraEvent.swift @@ -4,7 +4,7 @@ import Foundation /// All Voltra event types with their associated data public enum VoltraEventType { - // Persistent events (widget → app, survives app death) + /// Persistent events (widget → app, survives app death) case interaction(source: String, identifier: String, payload: String?) // Transient events (main app only, in-memory) diff --git a/ios/shared/VoltraInteractionIntent.swift b/ios/shared/VoltraInteractionIntent.swift index 3dc57ce6..a1a85c5b 100644 --- a/ios/shared/VoltraInteractionIntent.swift +++ b/ios/shared/VoltraInteractionIntent.swift @@ -5,11 +5,11 @@ public struct VoltraInteractionIntent: LiveActivityIntent { public static var title: LocalizedStringResource = "Interact" public static var isDiscoverable: Bool = false - // The ID of the activity + /// The ID of the activity @Parameter(title: "Activity ID") var activityId: String - // The ID of the component in your JSON (e.g., "button_1", "pause_btn") + /// The ID of the component in your JSON (e.g., "button_1", "pause_btn") @Parameter(title: "Component ID") var componentId: String diff --git a/ios/target/VoltraHomeWidget.swift b/ios/target/VoltraHomeWidget.swift index 21db109c..3b8a48b1 100644 --- a/ios/target/VoltraHomeWidget.swift +++ b/ios/target/VoltraHomeWidget.swift @@ -104,7 +104,7 @@ public enum VoltraHomeWidgetStore { } } -// Timeline data structures (intermediate storage - still uses Data for flexibility) +/// Timeline data structures (intermediate storage - still uses Data for flexibility) public struct WidgetTimelineEntry { let date: Date let json: Data @@ -130,15 +130,6 @@ public struct VoltraHomeWidgetEntry: TimelineEntry, Equatable { self.widgetId = widgetId self.deepLinkUrl = deepLinkUrl } - - public static func == (lhs: VoltraHomeWidgetEntry, rhs: VoltraHomeWidgetEntry) -> Bool { - // VoltraNode is Hashable, so this comparison works correctly. - // If the parsed AST is different, entries are not equal → WidgetKit re-renders. - lhs.date == rhs.date && - lhs.rootNode == rhs.rootNode && - lhs.widgetId == rhs.widgetId && - lhs.deepLinkUrl == rhs.deepLinkUrl - } } public struct VoltraHomeWidgetProvider: TimelineProvider { @@ -240,7 +231,6 @@ public struct VoltraHomeWidgetView: View { .disableWidgetMarginsIfAvailable() } - @ViewBuilder private func placeholderView(widgetId _: String) -> some View { VStack(alignment: .leading, spacing: 8) { Text("Almost ready") diff --git a/ios/tests/JSGradientParserTests.swift b/ios/tests/JSGradientParserTests.swift index e78d78bc..3b8b3fd5 100644 --- a/ios/tests/JSGradientParserTests.swift +++ b/ios/tests/JSGradientParserTests.swift @@ -1,6 +1,6 @@ -import XCTest import SwiftUI @testable import VoltraStyleCore +import XCTest final class JSGradientParserTests: XCTestCase { private func assertLinearGradient( diff --git a/ios/ui/Layout/VoltraFlexStackLayout.swift b/ios/ui/Layout/VoltraFlexStackLayout.swift index 107afd49..e609f708 100644 --- a/ios/ui/Layout/VoltraFlexStackLayout.swift +++ b/ios/ui/Layout/VoltraFlexStackLayout.swift @@ -91,8 +91,14 @@ struct VoltraFlexStackLayout: Layout { let mainMarginTrailing: CGFloat let crossMarginLeading: CGFloat let crossMarginTrailing: CGFloat - var mainMargin: CGFloat { mainMarginLeading + mainMarginTrailing } - var crossMargin: CGFloat { crossMarginLeading + crossMarginTrailing } + var mainMargin: CGFloat { + mainMarginLeading + mainMarginTrailing + } + + var crossMargin: CGFloat { + crossMarginLeading + crossMarginTrailing + } + let alignSelf: FlexAlign? } @@ -325,10 +331,21 @@ struct VoltraFlexStackLayout: Layout { : containerPadding.leading + containerPadding.trailing } - private var leadingPad: CGFloat { containerPadding.leading } - private var trailingPad: CGFloat { containerPadding.trailing } - private var topPad: CGFloat { containerPadding.top } - private var bottomPad: CGFloat { containerPadding.bottom } + private var leadingPad: CGFloat { + containerPadding.leading + } + + private var trailingPad: CGFloat { + containerPadding.trailing + } + + private var topPad: CGFloat { + containerPadding.top + } + + private var bottomPad: CGFloat { + containerPadding.bottom + } private var crossPaddingLeading: CGFloat { axis == .horizontal ? containerPadding.top : containerPadding.leading diff --git a/ios/ui/Protocols/VoltraView.swift b/ios/ui/Protocols/VoltraView.swift index b1b68f43..2c8e0bc3 100644 --- a/ios/ui/Protocols/VoltraView.swift +++ b/ios/ui/Protocols/VoltraView.swift @@ -23,5 +23,7 @@ public struct EmptyParameters: ComponentParameters {} /// Convenience extension for views with no parameters public extension VoltraView where Parameters == EmptyParameters { - var params: EmptyParameters { EmptyParameters() } + var params: EmptyParameters { + EmptyParameters() + } } diff --git a/ios/ui/Style/DecorationStyle.swift b/ios/ui/Style/DecorationStyle.swift index c18156a8..e7e345dd 100644 --- a/ios/ui/Style/DecorationStyle.swift +++ b/ios/ui/Style/DecorationStyle.swift @@ -85,7 +85,6 @@ struct DecorationModifier: ViewModifier { } } - @ViewBuilder private func radialGradientBackground(_ spec: RadialGradientSpec) -> some View { GeometryReader { proxy in let size = proxy.size diff --git a/ios/ui/Style/JSColorParser.swift b/ios/ui/Style/JSColorParser.swift index d88f6310..7a62c439 100644 --- a/ios/ui/Style/JSColorParser.swift +++ b/ios/ui/Style/JSColorParser.swift @@ -107,7 +107,7 @@ enum JSColorParser { // MARK: - Hex Parser - // Supports #RGB, #RGBA, #RRGGBB, #RRGGBBAA + /// Supports #RGB, #RGBA, #RRGGBB, #RRGGBBAA private static func parseHex(_ hex: String) -> Color? { let hexSanitized = hex.replacingOccurrences(of: "#", with: "") @@ -147,9 +147,9 @@ enum JSColorParser { // MARK: - RGB Parser - // Supports: - // - rgb(255, 0, 0), rgba(255, 0, 0, 0.5) - // - rgb(255 0 0 / 80%), rgba(255 0 0 / 0.8) + /// Supports: + /// - rgb(255, 0, 0), rgba(255, 0, 0, 0.5) + /// - rgb(255 0 0 / 80%), rgba(255 0 0 / 0.8) private static func parseRGB(_ string: String) -> Color? { guard let function = parseFunctionCall(string, allowedNames: ["rgb", "rgba"]) else { return nil } @@ -167,9 +167,9 @@ enum JSColorParser { // MARK: - HSL Parser - // Supports: - // - hsl(120, 100%, 50%), hsla(120, 100%, 50%, 0.5) - // - hsl(120 100% 50% / 30%), hsla(120 100% 50% / 0.3) + /// Supports: + /// - hsl(120, 100%, 50%), hsla(120, 100%, 50%, 0.5) + /// - hsl(120 100% 50% / 30%), hsla(120 100% 50% / 0.3) private static func parseHSL(_ string: String) -> Color? { guard let function = parseFunctionCall(string, allowedNames: ["hsl", "hsla"]) else { return nil } @@ -208,7 +208,7 @@ enum JSColorParser { let argsEnd = trimmed.index(before: trimmed.endIndex) guard argsStart <= argsEnd else { return nil } - let arguments = String(trimmed[argsStart.. Axis (Horizontal) > Generic (All) + /// Helper to resolve CSS Edge Precedence: + /// Specific (Left) > Axis (Horizontal) > Generic (All) static func parseInsets(from dict: [String: Any], prefix: String) -> EdgeInsets { let all = number(dict[prefix]) ?? 0 let v = number(dict["\(prefix)Vertical"]) ?? all @@ -74,7 +74,7 @@ enum JSStyleParser { ) } - // Maps "bold", "600", "normal" -> Font.Weight + /// Maps "bold", "600", "normal" -> Font.Weight static func fontWeight(_ value: Any?) -> Font.Weight { guard let string = value as? String else { return .regular } @@ -89,7 +89,7 @@ enum JSStyleParser { } } - // Maps "center", "right", "justify" -> TextAlignment + /// Maps "center", "right", "justify" -> TextAlignment static func textAlignment(_ value: Any?) -> TextAlignment { guard let string = value as? String else { return .leading } diff --git a/ios/ui/Style/LayoutStyle.swift b/ios/ui/Style/LayoutStyle.swift index 1f2d1f33..7789c21f 100644 --- a/ios/ui/Style/LayoutStyle.swift +++ b/ios/ui/Style/LayoutStyle.swift @@ -77,10 +77,10 @@ struct LayoutStyle { var alignSelf: FlexAlign? // nil = auto (inherit from container) var gap: CGFloat? // Spacing between flex children - // 3. Priority (legacy only, derived from flex) + /// 3. Priority (legacy only, derived from flex) var layoutPriority: Double? - // 4. Aspect Ratio + /// 4. Aspect Ratio var aspectRatio: CGFloat? // 5. Spacing @@ -99,7 +99,7 @@ struct LayoutModifier: ViewModifier { let style: LayoutStyle var contentAlignment: Alignment = .center - // Extract CGFloat from SizeValue for frame constraints + /// Extract CGFloat from SizeValue for frame constraints private var widthValue: CGFloat? { guard case let .fixed(v) = style.width else { return nil } return v @@ -110,21 +110,21 @@ struct LayoutModifier: ViewModifier { return v } - // Derive effective ideal width: explicit width, then flexBasis, then nil + /// Derive effective ideal width: explicit width, then flexBasis, then nil private var effectiveIdealWidth: CGFloat? { if let w = widthValue { return w } if case let .fixed(v) = style.flexBasis { return v } return nil } - // Derive effective ideal height: explicit height, then flexBasis, then nil + /// Derive effective ideal height: explicit height, then flexBasis, then nil private var effectiveIdealHeight: CGFloat? { if let h = heightValue { return h } if case let .fixed(v) = style.flexBasis { return v } return nil } - // flexShrink > 0: allow shrinking by setting min to 0 (unless explicitly constrained) + /// flexShrink > 0: allow shrinking by setting min to 0 (unless explicitly constrained) private var effectiveMinWidth: CGFloat? { style.flexShrink > 0 ? (style.minWidth ?? 0) : style.minWidth } @@ -173,7 +173,7 @@ struct LayoutModifier: ViewModifier { } } - // Expand if flex > 0 OR flexGrow > 0 OR width/height is .fill + /// Expand if flex > 0 OR flexGrow > 0 OR width/height is .fill private var shouldExpandWidth: Bool { (style.flex ?? 0) > 0 || style.flexGrow > 0 || style.width == .fill } diff --git a/ios/ui/Views/VoltraGauge.swift b/ios/ui/Views/VoltraGauge.swift index 56ebc8ea..79af7b68 100644 --- a/ios/ui/Views/VoltraGauge.swift +++ b/ios/ui/Views/VoltraGauge.swift @@ -59,7 +59,6 @@ public struct VoltraGauge: VoltraView { } } - @ViewBuilder private func buildNestedView(_ nestedView: VoltraNode) -> some View { nestedView } diff --git a/ios/ui/Views/VoltraGroupBox.swift b/ios/ui/Views/VoltraGroupBox.swift index e5775839..a7ca1202 100644 --- a/ios/ui/Views/VoltraGroupBox.swift +++ b/ios/ui/Views/VoltraGroupBox.swift @@ -20,7 +20,6 @@ public struct VoltraGroupBox: VoltraView { .applyStyle(element.style) } - @ViewBuilder private func buildNestedView(_ nestedView: VoltraNode) -> some View { nestedView } diff --git a/ios/ui/Views/VoltraLinearGradient.swift b/ios/ui/Views/VoltraLinearGradient.swift index fdc4de4d..1ae8fd05 100644 --- a/ios/ui/Views/VoltraLinearGradient.swift +++ b/ios/ui/Views/VoltraLinearGradient.swift @@ -9,7 +9,7 @@ public struct VoltraLinearGradient: VoltraView { self.element = element } - // Map string to UnitPoint + /// Map string to UnitPoint private func parsePoint(_ s: String?) -> UnitPoint { guard let raw = s else { return .center } @@ -39,7 +39,7 @@ public struct VoltraLinearGradient: VoltraView { } } - // Build Gradient from parameters + /// Build Gradient from parameters private func buildGradient(params: LinearGradientParameters) -> Gradient { // Prefer explicit stops over color array if let stopsStr = params.stops { diff --git a/plugin/src/ios-widget/files/infoPlist.ts b/plugin/src/ios-widget/files/infoPlist.ts index 154db51b..578faf9f 100644 --- a/plugin/src/ios-widget/files/infoPlist.ts +++ b/plugin/src/ios-widget/files/infoPlist.ts @@ -10,7 +10,7 @@ import { logger } from '../../utils/logger' * Version keys are written directly to the plist following the Expo pattern. */ function generateInfoPlistContent(targetName: string, version: string, buildNumber: string): string { - return ` + return ` @@ -51,7 +51,7 @@ function generateInfoPlistContent(targetName: string, version: string, buildNumb * @param buildNumber - The build number (CFBundleVersion) */ export function generateInfoPlist(targetPath: string, targetName: string, version: string, buildNumber: string): void { - const infoPlistPath = path.join(targetPath, 'Info.plist') - fs.writeFileSync(infoPlistPath, generateInfoPlistContent(targetName, version, buildNumber)) - logger.info('Generated Info.plist') + const infoPlistPath = path.join(targetPath, 'Info.plist') + fs.writeFileSync(infoPlistPath, generateInfoPlistContent(targetName, version, buildNumber)) + logger.info('Generated Info.plist') } diff --git a/plugin/src/ios-widget/index.ts b/plugin/src/ios-widget/index.ts index 2ab953d3..ed9c1822 100644 --- a/plugin/src/ios-widget/index.ts +++ b/plugin/src/ios-widget/index.ts @@ -36,7 +36,8 @@ export interface WithIOSProps { * so fonts must be registered before configureXcodeProject. */ export const withIOS: ConfigPlugin = (config, props) => { - const { targetName, bundleIdentifier, deploymentTarget, widgets, groupIdentifier, fonts, version, buildNumber } = props + const { targetName, bundleIdentifier, deploymentTarget, widgets, groupIdentifier, fonts, version, buildNumber } = + props const plugins: [ConfigPlugin, any][] = [ // 1. Add custom fonts if provided From 4d47b545deb10397bf58df477e0f2ee6343d794f Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Mon, 9 Mar 2026 11:34:04 +0100 Subject: [PATCH 2/5] chore: update prettierignore --- .prettierignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 98e9dcef..95838720 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,10 @@ /build +/android/build /example/.expo /example/ios +/ios/.build /plugin/build /.eslintrc.js /.expo /website/doc_build -/website/docs \ No newline at end of file +/website/docs From 21788eed408d5c2635c65453b2cecde25a9202f9 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Mon, 9 Mar 2026 11:50:14 +0100 Subject: [PATCH 3/5] chore: bring back ktlint --- .github/workflows/ci.yml | 63 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf85867b..9e4937b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ concurrency: cancel-in-progress: true jobs: - lint: - name: Lint + js-lint: + name: JS Lint runs-on: ubuntu-latest steps: - name: Checkout code @@ -29,10 +29,10 @@ jobs: - name: Install dependencies run: npm ci - - name: Check formatting - run: npm run format:check + - name: Check JS formatting + run: npm run format:js:check - - name: Run linter + - name: Run JS linter run: npm run lint:libOnly typecheck: @@ -133,24 +133,35 @@ jobs: - name: Check Swift formatting run: npm run format:swift:check - # TODO: Add ktlint back at some point (it takes over 2 minutes to install) - # ktlint: - # name: ktlint Check - # runs-on: ubuntu-latest - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - - # - name: Set up Node.js - # uses: actions/setup-node@v4 - # with: - # node-version: '22' - # cache: 'npm' - - # - name: Install ktlint - # run: | - # eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - # brew install ktlint - - # - name: Check Kotlin formatting - # run: npm run format:kotlin:check + ktlint: + name: ktlint Check + runs-on: ubuntu-latest + env: + KTLINT_VERSION: 1.8.0 + KTLINT_SHA256: a3fd620207d5c40da6ca789b95e7f823c54e854b7fade7f613e91096a3706d75 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + # Downloading the pinned release binary is much faster in CI than installing ktlint via Homebrew. + - name: Install ktlint + run: | + mkdir -p "$RUNNER_TEMP/bin" + curl -fsSL \ + "https://github.com/pinterest/ktlint/releases/download/${KTLINT_VERSION}/ktlint" \ + -o "$RUNNER_TEMP/bin/ktlint" + echo "${KTLINT_SHA256} $RUNNER_TEMP/bin/ktlint" | sha256sum --check -- + chmod +x "$RUNNER_TEMP/bin/ktlint" + echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH" + + - name: Check ktlint version + run: ktlint --version + + - name: Check Kotlin formatting + run: npm run format:kotlin:check From 8246ec330a2a956ca1d9f714563ac99cbb6f2db2 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Mon, 9 Mar 2026 11:54:39 +0100 Subject: [PATCH 4/5] chore: rename --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e4937b8..7d1d9143 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: js-lint: - name: JS Lint + name: '[JS] Format & Lint' runs-on: ubuntu-latest steps: - name: Checkout code @@ -36,7 +36,7 @@ jobs: run: npm run lint:libOnly typecheck: - name: Type Check + name: '[JS] Types validation' runs-on: ubuntu-latest steps: - name: Checkout code @@ -55,7 +55,7 @@ jobs: run: npx tsc --noEmit test: - name: Test JavaScript + name: '[JS] Test' runs-on: ubuntu-latest steps: - name: Checkout code @@ -74,7 +74,7 @@ jobs: run: npm run test:js native-kotlin-test: - name: Test Kotlin + name: '[Kotlin] Test' runs-on: ubuntu-latest steps: - name: Checkout code @@ -99,7 +99,7 @@ jobs: run: npm run test:kotlin native-swift-test: - name: Test Swift + name: '[Swift] Test' runs-on: macos-latest steps: - name: Checkout code @@ -118,7 +118,7 @@ jobs: run: npm run test:swift swiftformat: - name: SwiftFormat Check + name: '[Swift] Format & Lint' runs-on: macos-latest steps: - name: Checkout code @@ -134,7 +134,7 @@ jobs: run: npm run format:swift:check ktlint: - name: ktlint Check + name: '[Kotlin] Format & Lint' runs-on: ubuntu-latest env: KTLINT_VERSION: 1.8.0 From f43b6e2dce31bbe7b62a5054d3264ed2b1c21e83 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Mon, 9 Mar 2026 11:54:46 +0100 Subject: [PATCH 5/5] chore: add caching for kotlin test --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d1d9143..cba4f2a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,6 +80,15 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '17' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Set up Node.js uses: actions/setup-node@v4 with: