Skip to content
Merged
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
85 changes: 62 additions & 23 deletions .github/workflows/ci-dotnet.yaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,74 @@
name: dotnet CI build
# Keep this filename: its existing run counter supplies all package versions.
name: Protocol packages

on: push
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
donet-ci-build:
version:
runs-on: ubuntu-latest
outputs:
package-version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout source
uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Test shared version rules
run: python3 -B -m unittest discover -s scripts -p 'test_*.py'
- name: Calculate the shared package version
id: version
run: |
VERSION=$(python3 scripts/package-version.py)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Setup .NET Core @ Latest
uses: actions/setup-dotnet@v4
nuget:
needs: version
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
9.0.x
dotnet-version: 9.0.x
source-url: https://nuget.pkg.github.com/BluEye-Robotics/index.json
env:
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

- name: Sanitize branch name
id: branches
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build the NuGet package
env:
PACKAGE_VERSION: ${{ needs.version.outputs.package-version }}
run: dotnet pack Blueye.Protocol.Protobuf.csproj -p:Version="$PACKAGE_VERSION" -c Release -o out
- name: Publish to GitHub Packages
if: github.event_name != 'pull_request' && github.repository == 'BluEye-Robotics/ProtocolDefinitions'
env:
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH="${GITHUB_REF_NAME}"
SANITIZED=$(echo "$BRANCH" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | tr '[:upper:]' '[:lower:]' | cut -c1-63)
echo "sanitized-branch-name=$SANITIZED" >> "$GITHUB_OUTPUT"
dotnet nuget push ./out/*.nupkg --skip-duplicate --no-symbols \
--source https://nuget.pkg.github.com/BluEye-Robotics/index.json --api-key "$NUGET_AUTH_TOKEN"

- name: Build project and generate NuGet package
run: |
dotnet pack Blueye.Protocol.Protobuf.csproj --version-suffix "${{ steps.branches.outputs.sanitized-branch-name }}.$GITHUB_RUN_NUMBER" -c Release -o out
kotlin:
needs: version
permissions:
contents: read
packages: write
uses: ./.github/workflows/ci-kotlin.yaml
with:
package-version: ${{ needs.version.outputs.package-version }}

- name: Push generated package to GitHub registry
env:
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: dotnet nuget push ./out/*.nupkg --skip-duplicate --no-symbols --api-key $NUGET_AUTH_TOKEN
swift:
needs: [version, nuget, kotlin]
Comment thread
follesoe marked this conversation as resolved.
Resolved
if: >-
github.event_name != 'pull_request' &&
github.repository == 'BluEye-Robotics/ProtocolDefinitions' &&
github.ref == 'refs/heads/master' &&
vars.SWIFT_PROTOCOL_REPOSITORY != ''
uses: ./.github/workflows/ci-swift.yaml
with:
package-version: ${{ needs.version.outputs.package-version }}
secrets:
CI_PAT: ${{ secrets.CI_PAT }}
55 changes: 55 additions & 0 deletions .github/workflows/ci-kotlin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Kotlin package

on:
workflow_call:
inputs:
package-version:
required: true
type: string

permissions:
contents: read

defaults:
run:
working-directory: kotlin

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- name: Build, test, and publish to a local test repository
env:
PACKAGE_VERSION: ${{ inputs.package-version }}
run: ./gradlew --no-daemon build publishProtocolPublicationToSmokeTestRepository -PpackageVersion="$PACKAGE_VERSION"
- name: Verify package from a separate consuming project
env:
PACKAGE_VERSION: ${{ inputs.package-version }}
run: ./gradlew --no-daemon -p smoke-test run -PpackageVersion="$PACKAGE_VERSION"

publish:
if: github.event_name != 'pull_request' && github.repository == 'BluEye-Robotics/ProtocolDefinitions'
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- name: Publish to GitHub Packages
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_VERSION: ${{ inputs.package-version }}
run: ./gradlew --no-daemon publishProtocolPublicationToGitHubPackagesRepository -PpackageVersion="$PACKAGE_VERSION"
29 changes: 29 additions & 0 deletions .github/workflows/ci-swift.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Release blueye.protocol.swift

on:
workflow_call:
inputs:
package-version:
required: true
type: string
secrets:
CI_PAT:
required: true

permissions:
contents: read

jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Dispatch the protocol revision and shared version
env:
GH_TOKEN: ${{ secrets.CI_PAT }}
SWIFT_PROTOCOL_REPOSITORY: ${{ vars.SWIFT_PROTOCOL_REPOSITORY }}
PACKAGE_VERSION: ${{ inputs.package-version }}
run: |
gh api "repos/$SWIFT_PROTOCOL_REPOSITORY/dispatches" \
-f event_type=protocol-update \
-f "client_payload[sha]=$GITHUB_SHA" \
-f "client_payload[version]=$PACKAGE_VERSION"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ node_modules

# Rider directories
.idea

# Gradle build state
.gradle
.kotlin
3 changes: 1 addition & 2 deletions Blueye.Protocol.Protobuf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>13.0</LangVersion>
<PackageId>Blueye.Protocol.Protobuf</PackageId>
<VersionPrefix>6.3.0</VersionPrefix>
<Company>Blueye Robotics AS</Company>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<PackageDescription>Library with dotnet representation of the ProtocolDefinition protobuf files.</PackageDescription>
Expand All @@ -25,7 +24,7 @@
</ItemGroup>

<ItemGroup>
<Protobuf Include="**/*.proto" ProtoRoot="protobuf_definitions" GrpcServices="None" />
<Protobuf Include="protobuf_definitions/*.proto" ProtoRoot="protobuf_definitions" GrpcServices="None" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<VersionPrefix>$([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)version.txt').Trim()).0</VersionPrefix>
</PropertyGroup>
</Project>
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,49 @@ Drones running a Blunux version \< 3.0 will need to use the legacy protocol. [bl

Version 3 of the Blueye communication protocol is based on [Protocol Buffers](https://developers.google.com/protocol-buffers).

The protobuf definitions are compiled to language specific libraries, and are available as a [NuGet package](https://github.com/BluEye-Robotics/ProtocolDefinitions/packages/1239508), [Python package](https://pypi.org/project/blueye.protocol/) and a [npm package](https://www.npmjs.com/package/@blueyerobotics/protocol-definitions); a Rust crate is available as a git dependency (see below).
The protobuf definitions are compiled to language specific libraries, and are available as a [NuGet package](https://github.com/BluEye-Robotics/ProtocolDefinitions/packages/1239508), [Python package](https://pypi.org/project/blueye.protocol/) and a [npm package](https://www.npmjs.com/package/@blueyerobotics/protocol-definitions). Swift sources are maintained in [blueye.protocol.swift](https://github.com/BluEye-Robotics/blueye.protocol.swift). This repository also builds a [Kotlin/JVM Maven package](kotlin/README.md) and a Rust crate (see below).

Automatically generated documentation for the protocol format can be found [here](https://blueyebuildserver.blob.core.windows.net/protocoldefinitions/docs/protocol.html).

### Swift and Kotlin

The Swift package exposes `BlueyeProtocol` through Swift Package Manager from
the separate [blueye.protocol.swift repository](https://github.com/BluEye-Robotics/blueye.protocol.swift).
Like Python, it pins this repository as a submodule and commits generated
sources in its own repository. Swift tags use the same version as NuGet and Maven.

The Kotlin/JVM library publishes to GitHub Packages as
`com.blueyerobotics:protocol-definitions`, with the full Protobuf runtime and
generated Java classes plus Kotlin DSLs. See [Kotlin setup and publishing](kotlin/README.md).

All three packages use `protobuf_definitions/` as their source of truth. No
generated Java/Kotlin sources are committed here. Package versions are separate
from the wire protocol version described above.

`version.txt` holds the Blueye App major/minor version, currently `6.5`. The
**Protocol packages** workflow (`ci-dotnet.yaml`) computes one version for every
package using its existing run counter:

- Master builds: stable `6.5.<run-number>` releases.
- Other branches and pull requests: `6.5.<run-number>-branch-<sanitized-branch>`.
- Local builds: `6.5.0-local` for Maven and `6.5.0` for .NET unless overridden.

Merging to master automatically creates the next stable package version. Update
`version.txt` only when the Blueye App major/minor version changes. NuGet and Maven
publish on upstream branch pushes; Swift receives master releases after both
publish successfully. Its receiver generates and validates the pinned schema,
then commits and tags it with the same version. Delayed older releases are
generated and tagged separately without moving the Swift default branch backward.
Python keeps its separate repository and release versioning. Do not rename or
recreate the coordinating workflow without preserving a monotonically increasing
build counter.

Once the Swift receiver is installed, set the Actions variable
`SWIFT_PROTOCOL_REPOSITORY` to `BluEye-Robotics/blueye.protocol.swift`. The existing
`CI_PAT` secret must have permission to dispatch events to that repository.
`ci-swift.yaml` receives the shared version and sends it with the exact protocol
SHA. Dispatch is skipped until the variable is configured.

### Rust

The `rust/` directory contains the `blueye-protocol` crate. Code is generated at build time from `protobuf_definitions/*.proto` using [prost](https://crates.io/crates/prost) and [protox](https://crates.io/crates/protox) — no `protoc` installation required.
Expand Down
99 changes: 99 additions & 0 deletions kotlin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Kotlin/JVM package

Maven coordinate: `com.blueyerobotics:protocol-definitions`.

The library generates Java message classes and Kotlin builder DSLs from the
shared schemas. It uses the **full** `protobuf-java` and `protobuf-kotlin` runtimes,
including descriptors, reflection, and `Any`. It targets Java 8 bytecode, uses
Kotlin 2.2.21, and builds with JDK 17 and the included Gradle 8.14.3 wrapper.
It is a JVM/Android library, not a Kotlin Multiplatform library, and contains no
network transport implementation.

## Consume

Add the repository to `dependencyResolutionManagement.repositories` in your
`settings.gradle.kts` (or your project's `repositories` block):

```kotlin
maven {
url = uri("https://maven.pkg.github.com/blueye-robotics/ProtocolDefinitions")
credentials {
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
}
content { includeGroup("com.blueyerobotics") }
}
mavenCentral()
```

Store `gpr.user` and `gpr.key` in your user-level `~/.gradle/gradle.properties`,
not in source control. Use a GitHub personal access token (classic) with
`read:packages` and access to the repository. In Actions, a workflow's
`GITHUB_TOKEN` can be used where it has the required package access.

After the first release is published, use its full version from the package workflow
(the run number below is an example):

```kotlin
dependencies {
implementation("com.blueyerobotics:protocol-definitions:6.5.815")
}
```

Both Protobuf runtimes are transitive API dependencies. Consumers do not need a
Protobuf Gradle plugin or `protoc` installation.

```kotlin
import blueye.protocol.Attitude
import blueye.protocol.attitude
import com.google.protobuf.Any as ProtoAny

val original = attitude { yaw = 90f }
val decoded = Attitude.parseFrom(original.toByteArray())
val packed = ProtoAny.pack(original)
val unpacked = packed.unpack(Attitude::class.java)
val descriptor = Attitude.getDescriptor()
```

Java classes live directly in `blueye.protocol` because the schemas enable
`java_multiple_files`. This does not change protobuf wire names or field numbers.
For Protobuf JSON, add `com.google.protobuf:protobuf-java-util` at the same runtime
version (currently `4.36.2`); tests exercise JSON with an `Any` type registry.

## Develop and test

From `kotlin/`:

```sh
./gradlew build
./gradlew publishProtocolPublicationToSmokeTestRepository -PpackageVersion=6.5.0-local
./gradlew -p smoke-test run -PpackageVersion=6.5.0-local
```

The Gradle build downloads the matching `protoc` artifact and runtimes using the
version in `../protobuf.version`. Java and Kotlin sources are generated only in
the build directory. The separate smoke-test project resolves the actual local
Maven publication to verify the JAR, Kotlin DSL, and transitive dependencies.
The publication includes a sources JAR, POM, and Gradle module metadata.

## Publishing

`ci-kotlin.yaml` is called by the **Protocol packages** workflow in
`ci-dotnet.yaml`. The caller supplies the same version to NuGet, Maven, and Swift;
this reusable workflow does not calculate its own build number. Builds and tests
run on pushes and pull requests. Upstream branch pushes publish to GitHub Packages
using `GITHUB_TOKEN` with `packages: write`; pull requests and forks do not publish.

The shared `version.txt` contains the Blueye App major/minor version, currently
`6.5`. Master publishes stable `6.5.<run-number>` versions automatically. Other
branches publish `6.5.<run-number>-branch-<sanitized-branch>`. Local builds default
to `6.5.0-local`. The run counter belongs to `ci-dotnet.yaml`, so merges to master
do not require a version-file edit. NuGet and Maven publish first, then the same
version and source SHA are dispatched to Swift for generation and tagging.

Published versions are immutable. A workflow rerun keeps its original version;
prefer rerunning failed jobs so successful publications are not repeated.

To publish manually after testing, set credentials as above and run
`./gradlew publishProtocolPublicationToGitHubPackagesRepository -PpackageVersion=<version>`.
The generic `publish` task also writes to the local smoke-test repository.
Loading
Loading