Skip to content

Commit d346ffa

Browse files
authored
Release CI and README updates (#16)
1 parent 468f35d commit d346ffa

9 files changed

Lines changed: 411 additions & 27 deletions

File tree

.github/workflows/package.yml

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
name: Build Package
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- "releases/*"
7+
8+
jobs:
9+
build-bridge-libraries:
10+
strategy:
11+
fail-fast: true
12+
matrix:
13+
os: [ubuntu-latest, ubuntu-arm, macos-latest, macos-arm, windows-latest]
14+
include:
15+
- os: ubuntu-latest
16+
out-file: libtemporal_sdk_bridge.so
17+
out-prefix: linux-x64
18+
- os: ubuntu-arm
19+
out-file: libtemporal_sdk_bridge.so
20+
out-prefix: linux-arm64
21+
runsOn: buildjet-4vcpu-ubuntu-2204-arm
22+
- os: macos-latest
23+
out-file: libtemporal_sdk_bridge.dylib
24+
out-prefix: osx-x64
25+
- os: macos-arm
26+
out-file: libtemporal_sdk_bridge.dylib
27+
out-prefix: osx-arm64
28+
alternative-target: aarch64-apple-darwin
29+
runsOn: macos-latest
30+
- os: windows-latest
31+
out-file: temporal_sdk_bridge.dll
32+
out-prefix: win-x64
33+
runs-on: ${{ matrix.runsOn || matrix.os }}
34+
steps:
35+
- name: Print build information
36+
run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}, os: ${{ matrix.os }}"
37+
38+
- name: Checkout repository
39+
uses: actions/checkout@v2
40+
with:
41+
submodules: recursive
42+
43+
- name: Install Rust
44+
uses: actions-rs/toolchain@v1
45+
with:
46+
toolchain: stable
47+
48+
- name: Setup Rust cache
49+
uses: Swatinem/rust-cache@v2
50+
with:
51+
workspaces: src/Temporalio/Bridge
52+
key: ${{ matrix.os }}
53+
54+
- name: Add alternative Rust target
55+
if: ${{ matrix.alternative-target }}
56+
run: rustup target add ${{ matrix.alternative-target }}
57+
58+
- name: Install protoc (x64)
59+
# Does not work on arm
60+
if: ${{ matrix.os != 'ubuntu-arm' }}
61+
uses: arduino/setup-protoc@v1
62+
with:
63+
# TODO(cretz): Upgrade when https://github.com/arduino/setup-protoc/issues/33 fixed
64+
version: '3.x'
65+
repo-token: ${{ secrets.GITHUB_TOKEN }}
66+
67+
- name: Install protoc (Linux ARM)
68+
if: ${{ matrix.os == 'ubuntu-arm' }}
69+
run: |
70+
sudo apt update -y
71+
sudo apt install -y protobuf-compiler
72+
73+
- name: Build
74+
if: ${{ !matrix.alternative-target }}
75+
run: cargo build --manifest-path src/Temporalio/Bridge/Cargo.toml --release
76+
77+
- name: Build alternative target
78+
if: ${{ matrix.alternative-target != '' }}
79+
run: cargo build --manifest-path src/Temporalio/Bridge/Cargo.toml --release --target ${{ matrix.alternative-target }}
80+
81+
- name: Upload bridge library
82+
if: ${{ !matrix.alternative-target }}
83+
uses: actions/upload-artifact@v3
84+
with:
85+
name: ${{ matrix.out-prefix }}-bridge
86+
path: src/Temporalio/Bridge/target/release/${{ matrix.out-file }}
87+
88+
- name: Upload bridge library alternative target
89+
if: ${{ matrix.alternative-target != '' }}
90+
uses: actions/upload-artifact@v3
91+
with:
92+
name: ${{ matrix.out-prefix }}-bridge
93+
path: src/Temporalio/Bridge/target/${{ matrix.alternative-target }}/release/${{ matrix.out-file }}
94+
95+
build-nuget-package:
96+
needs:
97+
- build-bridge-libraries
98+
runs-on: windows-latest
99+
steps:
100+
- name: Print build information
101+
run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}, os: ${{ matrix.os }}"
102+
103+
- name: Checkout repository
104+
uses: actions/checkout@v2
105+
with:
106+
submodules: recursive
107+
108+
- name: Download bridge libraries
109+
uses: actions/download-artifact@v3
110+
with:
111+
path: bridge-libraries
112+
113+
- name: Setup .NET
114+
uses: actions/setup-dotnet@v3
115+
116+
- name: Build package
117+
run: dotnet pack -c Release /p:BridgeLibraryRoot=${{ github.workspace }}/bridge-libraries
118+
119+
- name: Upload NuGet artifact
120+
uses: actions/upload-artifact@v3
121+
with:
122+
name: nuget-package
123+
path: |
124+
src/Temporalio/bin/Release/*.nupkg
125+
src/Temporalio/bin/Release/*.snupkg
126+
127+
run-smoke-test:
128+
needs:
129+
- build-nuget-package
130+
strategy:
131+
fail-fast: true
132+
matrix:
133+
os: [ubuntu-latest, ubuntu-arm, macos-latest, windows-latest]
134+
include:
135+
- os: ubuntu-arm
136+
runsOn: buildjet-4vcpu-ubuntu-2204-arm
137+
runs-on: ${{ matrix.runsOn || matrix.os }}
138+
steps:
139+
- name: Print build information
140+
run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}, os: ${{ matrix.os }}"
141+
142+
- name: Checkout repository
143+
uses: actions/checkout@v2
144+
with:
145+
submodules: recursive
146+
147+
- name: Download NuGet artifact
148+
uses: actions/download-artifact@v3
149+
with:
150+
name: nuget-package
151+
path: nuget-package
152+
153+
- name: Setup .NET
154+
uses: actions/setup-dotnet@v3
155+
156+
- name: Add dependency on local package
157+
run: dotnet add tests/Temporalio.SmokeTest package Temporalio -s "${{ github.workspace }}/nuget-package;https://api.nuget.org/v3/index.json" --prerelease
158+
159+
- name: Run smoke test
160+
run: dotnet run --project tests/Temporalio.SmokeTest

Directory.Build.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
<Project>
22
<PropertyGroup>
3+
<Authors>Temporal</Authors>
4+
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
35
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
46
<GenerateDocumentationFile>true</GenerateDocumentationFile>
57
<Nullable>enable</Nullable>
8+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
9+
<RepositoryUrl>https://github.com/temporalio/sdk-dotnet</RepositoryUrl>
610
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
11+
<Version>0.1.0-alpha1</Version>
712
</PropertyGroup>
813

914
<!-- Run Analyzers only on latest .NET version -->

README.md

Lines changed: 180 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,198 @@
11
# Temporal .NET SDK
22

3-
See:
3+
[![NuGet](https://img.shields.io/nuget/vpre/temporalio.svg?style=for-the-badge)](https://www.nuget.org/packages/Temporalio)
4+
[![MIT](https://img.shields.io/github/license/temporalio/sdk-dotnet.svg?style=for-the-badge)](LICENSE)
45

5-
* NuGet Package (TODO)
6-
* Application Development Guide (TODO)
6+
[Temporal](https://temporal.io/) is a distributed, scalable, durable, and highly available orchestration engine used to
7+
execute asynchronous, long-running business logic in a scalable and resilient way.
8+
9+
"Temporal .NET SDK" is the framework for authoring workflows and activities using .NET programming languages.
10+
11+
Also see:
12+
13+
* [NuGet Package](https://www.nuget.org/packages/Temporalio)
14+
* [Application Development Guide](https://docs.temporal.io/application-development) (TODO: .NET docs)
715
* [API Documentation](https://dotnet.temporal.io/api)
816
* Samples (TODO)
917

1018
⚠️ UNDER ACTIVE DEVELOPMENT
1119

20+
This SDK is under active development and has not released a stable version yet. APIs may change in incompatible ways
21+
until the SDK is marked stable.
22+
23+
Notably missing from this SDK:
24+
25+
* Activity workers
26+
* Workflow workers
27+
1228
(for the previous .NET SDK repo, see https://github.com/temporalio/experiment-dotnet)
1329

1430
## Quick Start
1531

16-
TODO
32+
## Installation
33+
34+
Add the `Temporalio` package from [NuGet](https://www.nuget.org/packages/Temporalio). For example, using the `dotnet`
35+
CLI:
36+
37+
dotnet add package Temporalio --prerelease
38+
39+
**NOTE: This README is for the current branch and not necessarily what's released on NuGet.**
1740

1841
## Usage
1942

20-
TODO
43+
### Workflow Definitions
44+
45+
The current SDK does not yet support workflows written in .NET, but workflows from other languages can still be defined
46+
in .NET to be properly typed.
47+
48+
### Client
49+
50+
A client can be created an used to start a workflow. For example:
51+
52+
```csharp
53+
using Temporalio.Client;
54+
55+
// Create client connected to server at the given address and namespace
56+
var client = await TemporalClient.ConnectAsync(new()
57+
{
58+
TargetHost = "localhost:7233",
59+
Namespace = "my-namespace",
60+
});
61+
62+
// Start a workflow
63+
var handle = await client.StartWorkflowAsync(
64+
IMyWorkflow.RunAsync,
65+
"some workflow argument",
66+
new() { ID = "my-workflow-id", TaskQueue = "my-workflow-queue" });
67+
68+
// Wait for a result
69+
var result = await handle.GetResultAsync();
70+
Console.WriteLine("Result: {0}", result);
71+
```
72+
73+
Notes about the above code:
74+
75+
* Temporal clients are not explicitly disposable.
76+
* To enable TLS, the `Tls` option can be set to a non-null `TlsOptions` instance.
77+
* Instead of `StartWorkflowAsync` + `GetResultAsync` above, there is an `ExecuteWorkflowAsync` extension method that is
78+
clearer if the handle is not needed.
79+
* Non-typesafe forms of `StartWorkflowAsync` and `ExecuteWorkflowAsync` exist when there is no workflow definition or
80+
the workflow may take more than one argument or some other dynamic need. These simply take string workflow type names
81+
and an object array for arguments.
82+
* The `handle` above represents a `WorkflowHandle` which has specific workflow operations on it. For existing workflows,
83+
handles can be obtained via `client.GetWorkflowHandle`.
84+
85+
### Data Conversion
86+
87+
Data converters are using to convert raw Temporal payloads to/from actual .NET types. A custom data converter can be set
88+
via the `DataConverter` option when creating a client. Data converters are a combination of payload converters, payload
89+
codecs, and failure converters. Payload converters convert .NET values to/from serialized bytes. Payload codecs convert
90+
bytes to bytes (e.g. for compression or encryption). Failure converters convert exceptions to/from serialized failures.
91+
92+
Data converters are in the `Temporalio.Converters` namespace. The default data converter uses a default payload
93+
converter, which supports the following types:
94+
95+
* `null`
96+
* `byte[]`
97+
* `Google.Protobuf.IMessage` instances
98+
* Anything that `System.Text.Json` supports
99+
100+
Custom converters can be created for all uses. Due to potential sandboxing use, payload converters must be specified as
101+
types not instances. For example, to create client with a data converter that converts all C# property names to camel
102+
case, you would:
103+
104+
```csharp
105+
using System.Text.Json;
106+
using Temporalio.Client;
107+
using Temporalio.Converters;
108+
109+
public class CamelCasePayloadConverter : DefaultPayloadConverter
110+
{
111+
public CamelCasePayloadConverter()
112+
: base(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
113+
{
114+
}
115+
}
116+
117+
var client = await TemporalClient.ConnectAsync(new()
118+
{
119+
TargetHost = "localhost:7233",
120+
Namespace = "my-namespace",
121+
DataConverter = DataConverter.Default with { PayloadConverterType = typeof(CamelCasePayloadConverter) },
122+
});
123+
```
124+
125+
### Workflows
126+
127+
Workflows cannot yet be written in .NET but they can be defined.
128+
129+
#### Definition
130+
131+
Workflows are defined as classes or interfaces with a `[Workflow]` attribute. The entry point method for a workflow has
132+
the `[WorkflowRun]` attribute. Methods for signals and queries have the `[WorkflowSignal]` and `[WorkflowQuery]`
133+
attributes respectively. Here is an example of a workflow definition:
134+
135+
```csharp
136+
using System.Threading.Tasks;
137+
using Temporalio.Workflow;
138+
139+
public record GreetingInfo(string Salutation = "Hello", string Name = "<unknown>");
140+
141+
[Workflow]
142+
public interface IGreetingWorkflow
143+
{
144+
static readonly IGreetingWorkflow Ref = Refs<IGreetingWorkflow>.Instance;
145+
146+
[WorkflowRun]
147+
public Task<string> RunAsync(GreetingInfo initialInfo);
148+
149+
[WorkflowSignal]
150+
public Task UpdateSalutation(string salutation);
151+
152+
[WorkflowSignal]
153+
public Task CompleteWithGreeting();
154+
155+
[WorkflowQuery]
156+
public string CurrentGreeting();
157+
}
158+
```
159+
160+
Notes about the above code:
161+
162+
* The workflow client needs the ability to reference these instance methods, but C# doesn't allow referencing instance
163+
methods without an instance. Therefore we add a readonly `Ref` instance which is a proxy instance just for method
164+
references.
165+
* This is backed by a dynamic proxy generator but method invocations should never be made on it. It is only a for
166+
referencing methods.
167+
* This is technically not needed. Any way that the method can be referenced for a client is acceptable.
168+
* Source generators will provide an additional, alternative way to use workflows in a typed way in the future.
169+
* `[Workflow]` attribute must be present on the workflow type.
170+
* The attribute can have a string argument for the workflow type name. Otherwise the name is defaulted to the
171+
unqualified type name (with the `I` prefix removed if on an interface and has a capital letter following).
172+
* `[WorkflowRun]` attribute must be present on one and only one public method.
173+
* The workflow run method must return a `Task`.
174+
* The workflow run method _should_ accept a single parameter and return a single type that are often srecord. Records
175+
are encouraged because optional fields can be added without affecting backwards compatibility of the workflow
176+
definition.
177+
* The parameters of this method and its return type are considered the parameters and return type of the workflow
178+
itself.
179+
* This attribute is not inherited and this method must be explicitly defined on the declared workflow type. Even if
180+
the method functionality should be inherited, for clarity reasons it must still be explicitly defined even if it
181+
just invokes the base class method.
182+
* `[WorkflowSignal]` attribute may be present on any public method that handles signals.
183+
* Signal methods must return a `Task`.
184+
* The attribute can have a string argument for the signal name. Otherwise the name is defaulted to the unqualified
185+
method name with `Async` trimmed off the end if it is present.
186+
* This attribute is not inherited and therefore must be explicitly set on any override.
187+
* `[WorkflowQuery]` attribute may be present on any public method that handles queries.
188+
* Query methods must be non-`void` but cannot return a `Task` (i.e. they cannot be async).
189+
* The attribute can have a string argument for the query name. Otherwise the name is defaulted to the unqualified
190+
method name.
191+
* This attribute is not inherited and therefore must be explicitly set on any override.
192+
193+
### Activities
194+
195+
Activities are not implemented yet.
21196

22197
## Development
23198

@@ -101,16 +276,3 @@ Then:
101276
Install [docfx](https://dotnet.github.io/docfx/), then run:
102277

103278
docfx src/Temporalio.ApiDoc/docfx.json
104-
105-
TODO:
106-
107-
* Fix generated api doc
108-
* Specifically make `Temporalio.Api` have children collapsed by default
109-
* Switch/update template to full width
110-
* Formatting/style guide:
111-
* Line len 100 max on everything where reasonable
112-
* Rules for options classes
113-
* Shallow-copyable via virtual clone
114-
* Empty constructor and constructor with required params
115-
* TODO(cretz): Validation? Probably don't want attributes?
116-
* Source generator for workflows

0 commit comments

Comments
 (0)