Skip to content

Commit 8861871

Browse files
committed
Merge remote-tracking branch 'origin/main' into kyo-net-coherent-closed-exceptions
2 parents 15af8fa + f31d879 commit 8861871

563 files changed

Lines changed: 88465 additions & 962 deletions

File tree

Some content is hidden

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

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<!--
22
PRs require an approval from any of the core contributors, other than the PR author.
3+
CI on pull requests may require maintainer approval before it runs. To run CI yourself in the
4+
meantime, see "Running CI in Your Fork" in CONTRIBUTING.md.
35
46
Include this header if applicable:
57
Fixes #issue1, #issue2, ...
@@ -21,4 +23,5 @@ Add any important additional information as bullet points, such as:
2123
- Implementation details reviewers should know about
2224
- Open questions and concerns
2325
- Limitations
26+
- A link to a passing CI run in your fork, if you ran one (see "Running CI in Your Fork" in CONTRIBUTING.md)
2427
-->

.github/actions/setup/action.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@ runs:
8989
shell: bash
9090
run: bash scripts/apt-install.sh liburing-dev
9191

92+
# liburing is a build-time-only dependency of the kyo-net io_uring shim (kyo_uring.c), statically
93+
# linked so the shipped artifact has no runtime liburing dependency. The JVM target compiles the shim
94+
# via the kyo-ffi plugin's cc; the Native target links it through the Scala Native linker.
95+
- name: Install liburing (Linux)
96+
if: ${{ contains(inputs.os, 'linux') && (inputs.target == 'JVM' || inputs.target == 'Native') }}
97+
uses: nick-fields/retry@v4
98+
with:
99+
max_attempts: 3
100+
timeout_minutes: 5
101+
retry_wait_seconds: 15
102+
command: |
103+
sudo apt-get update
104+
sudo apt-get install -y -o Acquire::Retries=3 liburing-dev
105+
92106
- name: Install podman (Linux)
93107
if: ${{ contains(inputs.os, 'linux') }}
94108
shell: bash

.github/workflows/build.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ jobs:
5353
needs: prep
5454
# JVM full runs peak at ~95-100 min on Linux (browser/UI/caliban suites serialize under a
5555
# shared Chrome instance that cannot take concurrent CDP connections) and run slower on the
56-
# Windows pole, where runner-slot contention has pushed a full JVM run past 180 min. 240 min
57-
# bounds a genuine hang while giving the slowest pole real headroom.
58-
timeout-minutes: 240
56+
# Windows pole, where runner-slot contention has pushed a full JVM run past 180 min. The binding
57+
# pole is the Native aggregate build+test on the slower linux-arm64 runner: linux-x64 Native
58+
# completes in ~200 min, so arm64 needs headroom well past 240 min and hit the old cap. 360 min
59+
# (the GitHub-hosted job ceiling) bounds a genuine hang while giving the slowest pole real headroom.
60+
timeout-minutes: 360
5961
strategy:
6062
fail-fast: false
6163
max-parallel: 4
@@ -97,8 +99,7 @@ jobs:
9799
- uses: actions/checkout@v7.0.1
98100
with:
99101
# github.sha, not github.ref: a queued job must validate the commit that triggered the
100-
# run, not whatever the branch tip is when the job starts. The concurrency group in
101-
# ci.yml keys main pushes on the sha for the same reason.
102+
# run, not whatever the branch tip is when the job starts.
102103
ref: ${{ inputs.ref || github.sha }}
103104
fetch-depth: 0
104105

.github/workflows/ci.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ on:
5858
required: false
5959
default: ''
6060

61-
# A pull request keys its group on the PR number, so a new commit supersedes the in-flight run.
62-
# A push (to main) keys on the commit sha, so every commit gets its own group and is validated on
63-
# its own; cancel-in-progress only acts within a group, so a later push never cancels an earlier
64-
# main run.
61+
# Both pull requests and main pushes dedup their in-flight runs. A pull request keys on the PR
62+
# number, so a new commit supersedes the run still validating the previous one. A push to main
63+
# keys on the branch ref (github.ref is a constant refs/heads/main), so a newer push cancels an
64+
# earlier main run that is still pending or in progress; only the latest commit is validated.
6565
concurrency:
6666
# Custom-mode groups key on the run id (no dedup): the free-form command must never reach
6767
# the group name, where a multi-line value invalidates the workflow.
68-
group: ci-${{ github.event.pull_request.number || github.sha }}-${{ inputs.mode }}-${{ inputs.targets }}-${{ inputs.oses }}-${{ inputs.mode == 'custom' && github.run_id || 0 }}-${{ inputs.custom-runner }}
68+
group: ci-${{ github.event.pull_request.number || github.ref }}-${{ inputs.mode }}-${{ inputs.targets }}-${{ inputs.oses }}-${{ inputs.mode == 'custom' && github.run_id || 0 }}-${{ inputs.custom-runner }}
6969
cancel-in-progress: true
7070

7171
jobs:
@@ -92,7 +92,11 @@ jobs:
9292
oses="$INPUT_OSES"
9393
targets="$INPUT_TARGETS"
9494
else
95-
oses='linux-x64 linux-arm64 windows-x64'
95+
# windows-x64 is temporarily excluded from push and pull_request runs while
96+
# windows-specific failures are worked (container-backed kyo-pod tests do not run on
97+
# the windows runner). It stays available on demand via workflow_dispatch with
98+
# oses=windows-x64, which is how windows fixes are validated in the meantime.
99+
oses='linux-x64 linux-arm64'
96100
targets='JVM JS Native Wasm'
97101
fi
98102

.github/workflows/deploy-site.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ concurrency:
2626

2727
jobs:
2828
build:
29+
if: github.repository == 'getkyo/kyo'
2930
runs-on: ubuntu-latest
3031
env:
3132
JAVA_OPTS: -Xms3G -Xmx4G -Xss10M -XX:MaxMetaspaceSize=512M
@@ -39,6 +40,23 @@ jobs:
3940
with:
4041
jvm: corretto:25
4142
apps: sbt
43+
# The site reaches kyo-net through kyo-http, so rendering it compiles kyo-net's C shims. This
44+
# workflow does not use .github/actions/setup, so the native build deps are installed here, as
45+
# readme.yml does for the same reason.
46+
#
47+
# liburing (build-time only): the io_uring shim (kyo_uring.c) statically links -luring.
48+
- name: Install liburing (Linux)
49+
run: bash scripts/apt-install.sh liburing-dev
50+
# BoringSSL has to be staged, not stubbed. Without the staged tree build.sbt compiles the
51+
# c-boringssl-stub shim, and kyo-net's `Compile / packageBin` runs kyoNetNativeClassifierGuard
52+
# (build.sbt), which fails on a stub. The site render needs kyo-net's packageBin, so it trips
53+
# the guard even though it publishes nothing.
54+
- name: Prepare BoringSSL
55+
shell: bash
56+
run: |
57+
command -v cmake >/dev/null 2>&1 || bash scripts/apt-install.sh cmake
58+
command -v go >/dev/null 2>&1 || bash scripts/apt-install.sh golang-go
59+
bash kyo-net/build/boringssl/build-boringssl.sh linux-x86_64
4260
- name: Link the browser bundle (fullLinkJS)
4361
run: sbt 'kyo-website-bundleJS/Compile/fullLinkJS'
4462
- name: Render and assemble the site from the current repo
@@ -48,6 +66,7 @@ jobs:
4866
path: site
4967
deploy:
5068
needs: build
69+
if: github.repository == 'getkyo/kyo'
5170
runs-on: ubuntu-latest
5271
environment:
5372
name: github-pages

.github/workflows/release-probe.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ name: release-probe
1818
on:
1919
pull_request:
2020
branches: [main]
21+
push:
22+
branches: [main]
2123

2224
permissions:
2325
contents: read
@@ -30,6 +32,11 @@ concurrency:
3032

3133
jobs:
3234
probe:
35+
# release.yml already builds the release artifacts on an upstream main push, so there the probe
36+
# only needs to run for pull requests. In a fork release.yml is guarded off, so the probe also
37+
# runs on pushes to the fork's main to keep publishability checked. On an upstream main push the
38+
# guard skips the job, so no runner is used and the run entry is inert.
39+
if: github.event_name == 'pull_request' || github.repository != 'getkyo/kyo'
3340
runs-on: ubuntu-latest
3441
timeout-minutes: 120
3542
env:

.github/workflows/release.yml

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ jobs:
2424
# clang emit x86_64 code.
2525
natives-darwin:
2626
if: |
27-
(github.event_name == 'push') ||
28-
(github.event_name == 'issue_comment' &&
29-
github.event.issue.pull_request &&
30-
github.event.comment.body == '/release')
27+
github.repository == 'getkyo/kyo' && (
28+
(github.event_name == 'push') ||
29+
(github.event_name == 'issue_comment' &&
30+
github.event.issue.pull_request &&
31+
github.event.comment.body == '/release'))
3132
runs-on: macos-14
3233
env:
3334
JAVA_OPTS: -Xms4G -Xmx4G -XX:ReservedCodeCacheSize=256M
@@ -71,7 +72,7 @@ jobs:
7172
test -s "kyonet-natives/$f" || { echo "producer natives-darwin is missing $f" >&2; exit 1; }
7273
done
7374
ls -l kyonet-natives
74-
- uses: actions/upload-artifact@v4
75+
- uses: actions/upload-artifact@v7
7576
with:
7677
name: natives-darwin
7778
path: kyonet-natives
@@ -83,10 +84,11 @@ jobs:
8384
# run on the glibc host so they use the runner's Node; only the build runs in the container.
8485
natives-linux-x86_64:
8586
if: |
86-
(github.event_name == 'push') ||
87-
(github.event_name == 'issue_comment' &&
88-
github.event.issue.pull_request &&
89-
github.event.comment.body == '/release')
87+
github.repository == 'getkyo/kyo' && (
88+
(github.event_name == 'push') ||
89+
(github.event_name == 'issue_comment' &&
90+
github.event.issue.pull_request &&
91+
github.event.comment.body == '/release'))
9092
runs-on: ubuntu-latest
9193
env:
9294
JAVA_OPTS: -Xms4G -Xmx4G -XX:ReservedCodeCacheSize=256M
@@ -138,7 +140,7 @@ jobs:
138140
test -s "kyonet-natives/$f" || { echo "producer natives-linux-x86_64 is missing $f" >&2; exit 1; }
139141
done
140142
ls -l kyonet-natives
141-
- uses: actions/upload-artifact@v4
143+
- uses: actions/upload-artifact@v7
142144
with:
143145
name: natives-linux-x86_64
144146
path: kyonet-natives
@@ -148,10 +150,11 @@ jobs:
148150
# the same Alpine step on the arm64 host (the temurin alpine image is multi-arch).
149151
natives-linux-aarch64:
150152
if: |
151-
(github.event_name == 'push') ||
152-
(github.event_name == 'issue_comment' &&
153-
github.event.issue.pull_request &&
154-
github.event.comment.body == '/release')
153+
github.repository == 'getkyo/kyo' && (
154+
(github.event_name == 'push') ||
155+
(github.event_name == 'issue_comment' &&
156+
github.event.issue.pull_request &&
157+
github.event.comment.body == '/release'))
155158
runs-on: ubuntu-24.04-arm
156159
env:
157160
JAVA_OPTS: -Xms4G -Xmx4G -XX:ReservedCodeCacheSize=256M
@@ -200,7 +203,7 @@ jobs:
200203
test -s "kyonet-natives/$f" || { echo "producer natives-linux-aarch64 is missing $f" >&2; exit 1; }
201204
done
202205
ls -l kyonet-natives
203-
- uses: actions/upload-artifact@v4
206+
- uses: actions/upload-artifact@v7
204207
with:
205208
name: natives-linux-aarch64
206209
path: kyonet-natives
@@ -209,10 +212,11 @@ jobs:
209212
publish:
210213
needs: [natives-darwin, natives-linux-x86_64, natives-linux-aarch64]
211214
if: |
212-
(github.event_name == 'push') ||
213-
(github.event_name == 'issue_comment' &&
214-
github.event.issue.pull_request &&
215-
github.event.comment.body == '/release')
215+
github.repository == 'getkyo/kyo' && (
216+
(github.event_name == 'push') ||
217+
(github.event_name == 'issue_comment' &&
218+
github.event.issue.pull_request &&
219+
github.event.comment.body == '/release'))
216220
runs-on: ubuntu-latest
217221
env:
218222
JAVA_OPTS: -Xms4G -Xmx4G -XX:ReservedCodeCacheSize=256M
@@ -230,7 +234,7 @@ jobs:
230234
# host's). merge-multiple flattens the per-producer artifacts into one directory; the filenames
231235
# (lib<id>-<os>-<arch>.<ext>) are unique across producers, so nothing collides.
232236
- name: Download producer natives into ffiPrebuiltDir
233-
uses: actions/download-artifact@v4
237+
uses: actions/download-artifact@v8
234238
with:
235239
pattern: natives-*
236240
path: kyonet-prebuilt

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
.metals
66
project/target
77
project/project
8+
project/.boot
9+
project/.ivy
10+
project/.sbtboot
811
.vscode
912
target
1013
*.log

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ To create or rewrite a module's README, invoke the `/readme <module-path>` skill
187187

188188
## Common Gotchas
189189

190-
1. **`kyo.System` shadows `java.lang.System`**: use fully qualified `java.lang.System` when needed
190+
1. **`kyo.System` shadows `java.lang.System`**, and **`kyo.SecureRandom` shadows `java.security.SecureRandom`**: use the fully qualified JDK name when both are in scope
191191
2. **Effect handlers are not inline**: `Abort.run`, `Var.run` are regular methods; only suspend/create methods are inline
192192
3. **`Frame` required on every effectful method**, but not on pure data accessors like `capacity` or `size`
193193
4. **Overloads delegate to canonical**: never duplicate logic across method variants

CONTRIBUTING.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Thank you for considering contributing to this project! We welcome all contribut
88
- [Getting Started](#getting-started)
99
- [Configuring Java Options](#configuring-java-options)
1010
- [How to Build Locally](#how-to-build-locally)
11+
- [Running CI in Your Fork](#running-ci-in-your-fork)
1112
- [Adding a New API](#adding-a-new-api)
1213
- [LLM Use Guide](#llm-use-guide)
1314
- [Core Principles](#core-principles)
@@ -129,6 +130,20 @@ Check formatting before submitting:
129130
sbt "scalafmtCheckAll"
130131
```
131132

133+
### Running CI in Your Fork
134+
135+
Pull requests to the main repository currently require maintainer approval before CI runs, so checks may not start right away. To get full CI signal on your own schedule, run the same workflows in your fork. They use only GitHub-hosted runners that are free for public repositories and read no repository secrets, so they run unmodified.
136+
137+
1. **Enable Actions on your fork.** GitHub disables a fork's workflows by default. Open your fork's **Actions** tab (`https://github.com/<your-user>/<your-fork>/actions`) and enable them when prompted.
138+
139+
2. **Run the `ci` workflow.** In the **Actions** tab, select **ci** and click **Run workflow**, then choose your branch. Set **oses** to `linux-x64 linux-arm64` to match what pull-request CI runs. Leave **mode** as `full` for a complete run, or set it to `diff` to test only the modules your branch changed. The Windows pole is not part of PR CI; add `windows-x64` to **oses** to include it.
140+
141+
3. **Or open a fork-internal pull request.** A PR from your working branch against your fork's own `main` triggers the same diff-mode run an upstream PR would, on your runners, with no approval needed. It also runs `release-probe`, a no-secrets publishability check, for free.
142+
143+
Diff mode compares against your fork's `main`, so sync your fork before a diff-mode run (the **Sync fork** button, or `git fetch upstream && git push origin main`) to match upstream. The first run is slower while the runner caches warm up.
144+
145+
When you open your pull request, include a link to the fork CI run if you have one, so reviewers can see the result.
146+
132147
### Adding a New API
133148

134149
If you want to contribute a new method or type, feel free to:
@@ -243,11 +258,16 @@ When a Kyo primitive exists for a concept, use it instead of the stdlib equivale
243258
| `Result[E, A]` | `Either`, `Try` | Three-way: `Success`/`Failure`/`Panic` — never raw `Either` or `Try` in effect signatures |
244259
| `Chunk[A]` | `Seq`, `List`, `Vector` | Use internally; accept generic collections in public APIs (see below) |
245260
| `Duration` | `java.time.Duration`, `scala.concurrent.duration.Duration` | Opaque `Long`-based, zero-allocation |
261+
| `ByteSize` | a bare `Long` or `Int` holding a byte count | Opaque `Long`-based, zero-allocation, saturating arithmetic, non-negative by construction |
246262
| `Instant` | `java.time.Instant` | Kyo's own wrapper with consistent API |
247263
| `Span[A]` | `IArray[A]`, `ArraySeq[A]` | Immutable array wrapper, avoids boxing, O(1) indexing |
248264
| `Schedule` | Custom retry/timing logic | Composable scheduling policies |
249265
| `TypeMap[A]` | Heterogeneous maps | Type-safe map keyed by type |
250266

267+
Prefer `ByteSize` over a bare numeric type wherever a value means "a quantity of bytes": storage and disk sizes, file sizes, buffer and byte-array capacities, read and write chunk sizes, network packet and frame sizes, transfer limits and quotas, memory footprints. It carries the unit in the type, so a call site cannot silently pass kibibytes where bytes were expected, and its arithmetic saturates instead of overflowing. A raw `Long` or `Int` stays correct for an index, an offset into a buffer, or a count of elements; those are positions, not sizes.
268+
269+
This is guidance for new and changed code. Existing APIs that thread byte counts as `Long` are not required to migrate, and a migration should be its own change rather than a drive-by edit inside an unrelated one.
270+
251271
**kyo-prelude** — effects:
252272

253273
| Kyo primitive | Purpose | Notes |

0 commit comments

Comments
 (0)