nandi/jolt-nativepublic Fork 0
b6001e6
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Publish the shared objects instead of dropping them

The check job already built all five objects and then threw them away. It
now keeps them as job artifacts, and on main tars them by target and pushes
both tarballs to the generic package registry under the commit sha, so a
consumer flake can take one as a `flake = false` input and link against
lib/ without building this tree.

Publishing happens in the same job rather than a second one: a separate job
gets a cold nix store and would rebuild everything it was about to upload.

The Android output also picks up libc++_shared.so, which both device
objects NEEDED and nothing was shipping — only the NDK provides it, so an
APK packaging just the two objects fails to load them at run time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-07T18:21:07-07:00 Browse files
b6001e6 parent: 5416ab2
modified .gitlab-ci.yml +31 -0
@@ -4,6 +4,11 @@
44 # container image buck2's remote executor pulled; nothing about it ran the
55 # build. `nix flake check` runs fmt, clippy, the test suite and all three
66 # shared objects, and it pins what it runs with.
7+#
8+# The publish step is in the same job for the same reason: a second job would
9+# get a cold nix store and rebuild everything it was about to upload. The
10+# branch check is a shell `if` rather than a second job's `rules:` because
11+# there is nothing to gate but the last two commands.
712 stages: [check]
813
914 check:
@@ -17,6 +22,32 @@ check:
1722 - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf
1823 script:
1924 - nix flake check -L
25+ # The store paths are read-only symlink farms; the runner uploads plain
26+ # files, so dereference them into a tree it can zip.
27+ - nix build -L --no-link --print-out-paths .#libs .#android > /tmp/outs
28+ - mkdir -p artifacts && xargs -a /tmp/outs -I{} cp -rL {}/. artifacts/
29+ # Tarballs rooted at lib/ and include/, so a consumer's `flake = false`
30+ # input resolves to ${input}/lib/libjoltmoq.so with nothing in between.
31+ # One per target: the desktop objects carry a RUNPATH into the builder's
32+ # /nix/store and are only usable from nix, the Android ones link nothing
33+ # but the NDK sysroot and are what an APK actually packages.
34+ - |
35+ set -eu
36+ if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
37+ tar czf x86_64-linux.tar.gz -C artifacts include lib/libvidya.so lib/libjolttui.so lib/libjoltmoq.so
38+ tar czf android-arm64-v8a.tar.gz -C artifacts include lib/arm64-v8a
39+ base="$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/jolt-native/$CI_COMMIT_SHA"
40+ nix shell nixpkgs#curl -c sh -eu -c '
41+ for f in x86_64-linux.tar.gz android-arm64-v8a.tar.gz; do
42+ curl --fail-with-body --header "JOB-TOKEN: $CI_JOB_TOKEN" \
43+ --upload-file "$f" "$1/$f"
44+ done
45+ ' sh "$base"
46+ fi
47+ artifacts:
48+ name: "jolt-native-$CI_COMMIT_SHORT_SHA"
49+ paths: [artifacts/]
50+ expire_in: 1 week
2051 rules:
2152 - if: $CI_PIPELINE_SOURCE == "merge_request_event"
2253 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
@@ -4,6 +4,11 @@
4 # container image buck2's remote executor pulled; nothing about it ran the4 # container image buck2's remote executor pulled; nothing about it ran the
5 # build. `nix flake check` runs fmt, clippy, the test suite and all three5 # build. `nix flake check` runs fmt, clippy, the test suite and all three
6 # shared objects, and it pins what it runs with.6 # shared objects, and it pins what it runs with.
7+#
8+# The publish step is in the same job for the same reason: a second job would
9+# get a cold nix store and rebuild everything it was about to upload. The
10+# branch check is a shell `if` rather than a second job's `rules:` because
11+# there is nothing to gate but the last two commands.
7 stages: [check]12 stages: [check]
8 13
9 check:14 check:
@@ -17,6 +22,32 @@ check:
17 - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf22 - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf
18 script:23 script:
19 - nix flake check -L24 - nix flake check -L
25+ # The store paths are read-only symlink farms; the runner uploads plain
26+ # files, so dereference them into a tree it can zip.
27+ - nix build -L --no-link --print-out-paths .#libs .#android > /tmp/outs
28+ - mkdir -p artifacts && xargs -a /tmp/outs -I{} cp -rL {}/. artifacts/
29+ # Tarballs rooted at lib/ and include/, so a consumer's `flake = false`
30+ # input resolves to ${input}/lib/libjoltmoq.so with nothing in between.
31+ # One per target: the desktop objects carry a RUNPATH into the builder's
32+ # /nix/store and are only usable from nix, the Android ones link nothing
33+ # but the NDK sysroot and are what an APK actually packages.
34+ - |
35+ set -eu
36+ if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
37+ tar czf x86_64-linux.tar.gz -C artifacts include lib/libvidya.so lib/libjolttui.so lib/libjoltmoq.so
38+ tar czf android-arm64-v8a.tar.gz -C artifacts include lib/arm64-v8a
39+ base="$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/jolt-native/$CI_COMMIT_SHA"
40+ nix shell nixpkgs#curl -c sh -eu -c '
41+ for f in x86_64-linux.tar.gz android-arm64-v8a.tar.gz; do
42+ curl --fail-with-body --header "JOB-TOKEN: $CI_JOB_TOKEN" \
43+ --upload-file "$f" "$1/$f"
44+ done
45+ ' sh "$base"
46+ fi
47+ artifacts:
48+ name: "jolt-native-$CI_COMMIT_SHORT_SHA"
49+ paths: [artifacts/]
50+ expire_in: 1 week
20 rules:51 rules:
21 - if: $CI_PIPELINE_SOURCE == "merge_request_event"52 - if: $CI_PIPELINE_SOURCE == "merge_request_event"
22 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH53 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
modified flake.nix +25 -0
@@ -12,6 +12,25 @@
1212 # nix build .#android # the two device objects, cross-compiled
1313 # nix flake check # fmt, clippy, tests
1414 #
15+# Every push to main publishes the same two outputs to the GitLab package
16+# registry as tarballs rooted at lib/ and include/, so another flake can link
17+# against them without building this one:
18+#
19+# inputs.jolt-native-libs = {
20+# url = "https://gitlab.example/api/v4/projects/<id>/packages/generic/jolt-native/<sha>/android-arm64-v8a.tar.gz";
21+# flake = false; # a plain tarball, not a flake — nix unpacks it as-is
22+# };
23+# # then: ${jolt-native-libs}/lib/arm64-v8a/libjoltmoq.so
24+#
25+# The URL carries a commit sha and flake.lock pins the unpacked tree's narHash,
26+# so the input is immutable from both ends; moving it is an edit plus a lock
27+# update. There is deliberately no `latest` URL — a moving target under a
28+# pinned hash is a lockfile that lies. x86_64-linux.tar.gz is published the
29+# same way, but those objects carry a RUNPATH into the *builder's* /nix/store
30+# and only resolve on a machine that holds those paths; desktop consumers
31+# should take this repo as a flake input and build .#libs instead. The tarball
32+# is for the Android side, which links nothing but the NDK sysroot.
33+#
1534 # What went away with buck2: third-party/rust's reindeer-generated BUCK graph
1635 # (Cargo.lock is the one dependency graph now), the RBE container and its GitLab
1736 # job, and scripts/ entire — zcc, zxx, zig-include, the .dotslash manifests and
@@ -225,6 +244,12 @@
225244 mkdir -p $out/lib/arm64-v8a
226245 cp target/${androidTarget}/release/libvidya.so $out/lib/arm64-v8a/
227246 cp target/${androidTarget}/release/libjoltmoq.so $out/lib/arm64-v8a/
247+ # Both objects NEEDED it — aws-lc-sys and libspa-sys pull in the
248+ # C++ runtime — and the NDK ships it as a shared library that
249+ # nothing else provides on the device. It has to ride along in the
250+ # same directory the APK packages, or dlopen fails at run time.
251+ cp ${ndkBin}/../sysroot/usr/lib/${androidTarget}/libc++_shared.so \
252+ $out/lib/arm64-v8a/
228253 '';
229254 });
230255
@@ -12,6 +12,25 @@
12 # nix build .#android # the two device objects, cross-compiled12 # nix build .#android # the two device objects, cross-compiled
13 # nix flake check # fmt, clippy, tests13 # nix flake check # fmt, clippy, tests
14 #14 #
15+# Every push to main publishes the same two outputs to the GitLab package
16+# registry as tarballs rooted at lib/ and include/, so another flake can link
17+# against them without building this one:
18+#
19+# inputs.jolt-native-libs = {
20+# url = "https://gitlab.example/api/v4/projects/<id>/packages/generic/jolt-native/<sha>/android-arm64-v8a.tar.gz";
21+# flake = false; # a plain tarball, not a flake — nix unpacks it as-is
22+# };
23+# # then: ${jolt-native-libs}/lib/arm64-v8a/libjoltmoq.so
24+#
25+# The URL carries a commit sha and flake.lock pins the unpacked tree's narHash,
26+# so the input is immutable from both ends; moving it is an edit plus a lock
27+# update. There is deliberately no `latest` URL — a moving target under a
28+# pinned hash is a lockfile that lies. x86_64-linux.tar.gz is published the
29+# same way, but those objects carry a RUNPATH into the *builder's* /nix/store
30+# and only resolve on a machine that holds those paths; desktop consumers
31+# should take this repo as a flake input and build .#libs instead. The tarball
32+# is for the Android side, which links nothing but the NDK sysroot.
33+#
15 # What went away with buck2: third-party/rust's reindeer-generated BUCK graph34 # What went away with buck2: third-party/rust's reindeer-generated BUCK graph
16 # (Cargo.lock is the one dependency graph now), the RBE container and its GitLab35 # (Cargo.lock is the one dependency graph now), the RBE container and its GitLab
17 # job, and scripts/ entire — zcc, zxx, zig-include, the .dotslash manifests and36 # job, and scripts/ entire — zcc, zxx, zig-include, the .dotslash manifests and
@@ -225,6 +244,12 @@
225 mkdir -p $out/lib/arm64-v8a244 mkdir -p $out/lib/arm64-v8a
226 cp target/${androidTarget}/release/libvidya.so $out/lib/arm64-v8a/245 cp target/${androidTarget}/release/libvidya.so $out/lib/arm64-v8a/
227 cp target/${androidTarget}/release/libjoltmoq.so $out/lib/arm64-v8a/246 cp target/${androidTarget}/release/libjoltmoq.so $out/lib/arm64-v8a/
247+ # Both objects NEEDED it — aws-lc-sys and libspa-sys pull in the
248+ # C++ runtime — and the NDK ships it as a shared library that
249+ # nothing else provides on the device. It has to ride along in the
250+ # same directory the APK packages, or dlopen fails at run time.
251+ cp ${ndkBin}/../sysroot/usr/lib/${androidTarget}/libc++_shared.so \
252+ $out/lib/arm64-v8a/
228 '';253 '';
229 });254 });
230 255