Skip to content

Commit 1ba65f0

Browse files
authored
Implement Livebook Desktop with Tauri (#3112)
1 parent 6408432 commit 1ba65f0

File tree

98 files changed

+13878
-10
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+13878
-10
lines changed

.github/workflows/release.yml

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Release
2+
23
on:
34
push:
45
tags:
@@ -8,6 +9,10 @@ on:
89
- cron: "0 0 * * *"
910
# Workflow dispatch always builds as nightly
1011
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
1116
jobs:
1217
create_release:
1318
if: github.repository == 'livebook-dev/livebook'
@@ -30,14 +35,20 @@ jobs:
3035
${{ github.ref_name }}
3136
else
3237
ref_name="nightly"
38+
notes="Automated nightly build for ${GITHUB_SHA}."
3339
3440
if ! gh release view $ref_name; then
3541
gh release create \
3642
--repo ${{ github.repository }} \
3743
--title $ref_name \
38-
--notes "Automated nightly release." \
44+
--notes "${notes}" \
3945
--latest=false \
4046
$ref_name
47+
else
48+
gh release edit \
49+
--repo ${{ github.repository }} \
50+
$ref_name \
51+
--notes "${notes}"
4152
fi
4253
4354
git tag $ref_name --force
@@ -55,13 +66,158 @@ jobs:
5566
run: |
5667
if [[ "${{ github.ref_type }}" == "tag" ]]; then
5768
gh workflow run -R livebook-dev/livebook_cd build.yml -f ref=${{ github.ref_name }} -f release_name=${{ github.ref_name }}
69+
fi
70+
71+
app_next:
72+
if: github.repository == 'livebook-dev/livebook'
73+
name: "Desktop (${{ matrix.gui_target }})"
74+
needs: [create_release]
75+
permissions:
76+
contents: write # Required for uploading release assets
77+
strategy:
78+
fail-fast: false
79+
matrix:
80+
include:
81+
- platform: macos-15
82+
gui_target: "aarch64-apple-darwin"
83+
- platform: macos-15
84+
gui_target: "x86_64-apple-darwin"
85+
- platform: windows-2022
86+
gui_target: "x86_64-pc-windows-msvc"
87+
- platform: ubuntu-22.04
88+
gui_target: "x86_64-unknown-linux-gnu"
89+
runs-on: ${{ matrix.platform }}
90+
steps:
91+
- name: Checkout repository
92+
uses: actions/checkout@v4
93+
94+
- name: Read versions
95+
shell: bash
96+
run: |
97+
. versions
98+
echo "elixir=$elixir" >> $GITHUB_ENV
99+
echo "otp=$otp" >> $GITHUB_ENV
100+
101+
- uses: erlef/setup-beam@v1
102+
with:
103+
otp-version: ${{ env.otp }}
104+
elixir-version: ${{ env.elixir }}
105+
106+
- name: Setup Rust
107+
uses: dtolnay/rust-toolchain@stable
108+
with:
109+
targets: ${{ matrix.gui_target }}
110+
111+
- name: Rust cache
112+
uses: Swatinem/rust-cache@v2
113+
with:
114+
workspaces: rel/app_next/src-tauri
115+
cache-directories: |
116+
~/.cargo/bin
117+
key: ${{ matrix.gui_target }}
118+
119+
- name: Install dependencies (Linux)
120+
if: runner.os == 'Linux'
121+
shell: bash
122+
run: |
123+
sudo apt-get update
124+
sudo apt-get install -y \
125+
libwebkit2gtk-4.1-dev \
126+
libgtk-3-dev \
127+
libayatana-appindicator3-dev \
128+
librsvg2-dev \
129+
patchelf \
130+
libwxgtk3.0-gtk3-dev \
131+
xdg-utils
132+
133+
- name: Install Tauri CLI
134+
shell: bash
135+
run: |
136+
# Only install if not already cached
137+
if ! command -v cargo-tauri &> /dev/null; then
138+
cargo install tauri-cli --version "=2.8.0" --locked
58139
else
59-
gh workflow run -R livebook-dev/livebook_cd build.yml -f ref=${{ github.sha }} -f release_name=nightly
140+
echo "cargo-tauri already installed: $(cargo-tauri --version)"
60141
fi
61142
143+
- name: Install trusted-signing-cli (Windows)
144+
if: runner.os == 'Windows'
145+
run: |
146+
# TODO: use git dependency until trusted-signing-cli next release (v0.8.1 or v0.9)
147+
cargo install --git https://github.com/Levminer/trusted-signing-cli.git --rev 5415376 trusted-signing-cli
148+
149+
- name: Install Apple certificate (macOS)
150+
if: runner.os == 'macOS'
151+
env:
152+
P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
153+
P12_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }}
154+
KEYCHAIN_PASSWORD: secret
155+
run: |
156+
# Only run if certificate is provided
157+
if [ -n "$P12_BASE64" ]; then
158+
# Create variables
159+
CERTIFICATE_PATH=$RUNNER_TEMP/apple_certificate.p12
160+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
161+
162+
echo -n "$P12_BASE64" | base64 --decode -o $CERTIFICATE_PATH
163+
164+
# Create temporary keychain
165+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
166+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
167+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
168+
169+
# Import certificate to keychain
170+
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
171+
security list-keychain -d user -s $KEYCHAIN_PATH
172+
fi
173+
174+
- name: Build Tauri app
175+
uses: tauri-apps/[email protected]
176+
env:
177+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
178+
MIX_ENV: prod
179+
MIX_TARGET: app_next
180+
# macOS codesigning/notarization
181+
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
182+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }}
183+
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
184+
APPLE_ID: ${{ secrets.APPLE_ID }}
185+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
186+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
187+
# Windows codesigning
188+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
189+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
190+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
191+
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
192+
AZURE_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }}
193+
# Tauri updater
194+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
195+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
196+
with:
197+
projectPath: rel/app_next
198+
tauriScript: ./tauri.sh
199+
args: --target ${{ matrix.gui_target }}
200+
tagName: ${{ github.ref_type == 'tag' && github.ref_name || 'nightly' }}
201+
releaseName: ${{ github.ref_type == 'tag' && github.ref_name || 'nightly' }}
202+
releaseDraft: true
203+
assetNamePattern: "Livebook-[platform]-[arch][ext]"
204+
205+
- name: Verify app notarization (macOS)
206+
if: runner.os == 'macOS'
207+
shell: bash
208+
run: |
209+
app_path="rel/app_next/src-tauri/target/${{ matrix.gui_target }}/release/bundle/macos/Livebook.app"
210+
echo "Verifying $app_path"
211+
spctl -a -t exec -vvv "$app_path"
212+
62213
docker:
63-
if: github.repository == 'livebook-dev/livebook'
214+
# TODO: bring back
215+
# if: github.repository == 'livebook-dev/livebook'
216+
if: false
64217
name: Docker (${{ matrix.name }})
218+
permissions:
219+
contents: read
220+
packages: write # Required for pushing to ghcr.io
65221
runs-on: ubuntu-latest
66222
strategy:
67223
fail-fast: false
@@ -105,7 +261,7 @@ jobs:
105261
type=semver,pattern={{version}}
106262
type=raw,value=nightly,enable=${{ github.ref_type != 'tag' }}
107263
- name: Build and push
108-
uses: docker/build-push-action@v4
264+
uses: docker/build-push-action@v6
109265
with:
110266
context: .
111267
platforms: linux/amd64,linux/arm64

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ npm-debug.log
3333

3434
# The built Escript
3535
/livebook
36+
37+
/rel/app_next/resources

elixirkit_next/.formatter.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]

elixirkit_next/.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where third-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Temporary files, for example, from tests.
14+
/tmp/
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump
18+
19+
# Also ignore archive artifacts (built via "mix archive.build").
20+
*.ez
21+
22+
# Ignore package tarball (built via "mix hex.build").
23+
elixirkitnext-*.tar
24+
25+
/examples/cli_example/target
26+
/examples/tauri_example/target
27+
/elixirkit_rs/target

elixirkit_next/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# ElixirKit
2+
3+
ElixirKit helps running Elixir alongside native applications written in Rust.
4+
5+
🚧 ElixirKit is experimental and subject to change without notice.
6+
7+
See examples:
8+
- [`examples/cli_example`](examples/cli_example) - Minimal CLI that communicates with Elixir
9+
- [`examples/tauri_example`](examples/tauri_example) - Desktop app using Tauri
10+
11+
## Rust API
12+
13+
```rust
14+
let command = elixirkit::release(rel_dir, rel_name)
15+
16+
let exit_code = command.start(|(name, data)| {
17+
match name {
18+
"echo" => {
19+
println!("Received: {data}");
20+
}
21+
_ => {}
22+
}
23+
});
24+
```
25+
26+
### API Reference
27+
28+
- `elixirkit::release(rel_dir, rel_name)` - create command for Elixir release
29+
- `elixirkit::Command::new(program, args)` - create command for any executable
30+
- `command.current_dir(path)` - set working directory
31+
- `command.env(&[("KEY", "value")])` - set environment variables
32+
- `command.start(handler) -> exit_code` - start the command and run event loop
33+
- `command.send((name, data))` - send message to Elixir side
34+
35+
## Elixir API
36+
37+
- `ElixirKit.start_link()` - start listener, messages from Rust will be delivered as `{:event, name, data}` tuples
38+
- `ElixirKit.publish(name, data)` - send message to Rust side
39+
40+
# TODO
41+
- Receive `{:message, {name, data}}` messages to match Rust side
42+
- Change to ElixirKit.send({name, data}) to match Rust side

elixirkit_next/elixirkit_rs/Cargo.lock

Lines changed: 88 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "elixirkit"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
name = "elixirkit"
8+
path = "src/lib.rs"
9+
10+
[dependencies]
11+
tracing = "0.1"

0 commit comments

Comments
 (0)