Skip to content

Commit 1c8ffda

Browse files
committed
Merge remote-tracking branch 'origin/main' into miro/sleep
2 parents 9b62189 + da93546 commit 1c8ffda

63 files changed

Lines changed: 1994 additions & 253 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.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: Breaking changes
2+
3+
permissions:
4+
contents: read
5+
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
8+
cancel-in-progress: true
9+
10+
on:
11+
pull_request:
12+
branches: [main]
13+
# Re-run when override labels are added or removed
14+
types: [opened, synchronize, reopened, labeled, unlabeled]
15+
16+
jobs:
17+
proto-breaking:
18+
runs-on: ubuntu-latest
19+
env:
20+
SKIP: ${{ contains(github.event.pull_request.labels.*.name, 'breaking-change:proto') || contains(github.event.pull_request.labels.*.name, 'breaking-change:approved') }}
21+
steps:
22+
- name: Skipped — intentional breaking change (label override)
23+
if: env.SKIP == 'true'
24+
run: |
25+
echo "::notice title=Proto breaking check skipped::This PR has label breaking-change:proto or breaking-change:approved. A maintainer acknowledged an intentional proto break. Remove the label to re-enable buf breaking."
26+
27+
- name: Checkout code
28+
if: env.SKIP != 'true'
29+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
30+
with:
31+
fetch-depth: 0
32+
submodules: recursive
33+
token: ${{ secrets.GITHUB_TOKEN }}
34+
35+
- name: Setup Bun
36+
if: env.SKIP != 'true'
37+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
38+
with:
39+
bun-version: 1.3.12
40+
41+
- name: Install dependencies
42+
if: env.SKIP != 'true'
43+
run: bun install --frozen-lockfile
44+
45+
# cre-sdk's buf.yaml points at ../../submodules/chainlink-protos/cre, which buf rejects when
46+
# --against uses subdir=packages/cre-sdk (module path escapes the context). Run breaking on
47+
# the chainlink-protos workspace instead, comparing HEAD to the submodule commit pinned on main.
48+
- name: Buf breaking (proto)
49+
if: env.SKIP != 'true'
50+
run: |
51+
set -euo pipefail
52+
REPO="${{ github.workspace }}"
53+
SUBMODULE="${REPO}/submodules/chainlink-protos"
54+
BASE_COMMIT=$(git -C "$REPO" rev-parse origin/main:submodules/chainlink-protos)
55+
BASE_DIR="${RUNNER_TEMP}/chainlink-protos-baseline"
56+
if ! git -C "$SUBMODULE" cat-file -e "${BASE_COMMIT}^{commit}" 2>/dev/null; then
57+
git -C "$SUBMODULE" fetch --no-tags origin "${BASE_COMMIT}"
58+
fi
59+
git -C "$SUBMODULE" worktree add "${BASE_DIR}" "${BASE_COMMIT}" --detach
60+
cleanup() {
61+
git -C "$SUBMODULE" worktree remove "${BASE_DIR}" --force || true
62+
}
63+
trap cleanup EXIT
64+
(cd "$SUBMODULE" && bun x @bufbuild/buf breaking cre --against "${BASE_DIR}/cre" --error-format github-actions)
65+
66+
ts-api-surface:
67+
runs-on: ubuntu-latest
68+
env:
69+
SKIP: ${{ contains(github.event.pull_request.labels.*.name, 'breaking-change:typescript-api') || contains(github.event.pull_request.labels.*.name, 'breaking-change:approved') }}
70+
steps:
71+
- name: Skipped — intentional breaking change (label override)
72+
if: env.SKIP == 'true'
73+
run: |
74+
echo "::notice title=TypeScript API check skipped::This PR has label breaking-change:typescript-api or breaking-change:approved. Commit an updated api-baseline.d.ts when appropriate; this label only bypasses CI."
75+
76+
- name: Checkout code
77+
if: env.SKIP != 'true'
78+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
79+
with:
80+
submodules: recursive
81+
token: ${{ secrets.GITHUB_TOKEN }}
82+
83+
- name: Setup Bun
84+
if: env.SKIP != 'true'
85+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
86+
with:
87+
bun-version: 1.3.12
88+
89+
- name: Install dependencies
90+
if: env.SKIP != 'true'
91+
run: bun install --frozen-lockfile
92+
93+
- name: Diff TypeScript public API vs baseline
94+
if: env.SKIP != 'true'
95+
working-directory: packages/cre-sdk
96+
run: |
97+
bun run update-api-baseline
98+
99+
if ! git diff --exit-code api-baseline.d.ts; then
100+
echo "::error::TypeScript public API surface changed. Run 'bun run update-api-baseline' locally and commit the updated api-baseline.d.ts."
101+
exit 1
102+
fi
103+
104+
host-bindings:
105+
runs-on: ubuntu-latest
106+
env:
107+
SKIP: ${{ contains(github.event.pull_request.labels.*.name, 'breaking-change:host-bindings') || contains(github.event.pull_request.labels.*.name, 'breaking-change:approved') }}
108+
steps:
109+
- name: Skipped — intentional breaking change (label override)
110+
if: env.SKIP == 'true'
111+
run: |
112+
echo "::notice title=Host bindings check skipped::This PR has label breaking-change:host-bindings or breaking-change:approved. Update snapshots and host-imports-baseline.txt when appropriate; this label only bypasses CI."
113+
114+
- name: Checkout code
115+
if: env.SKIP != 'true'
116+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
117+
with:
118+
submodules: recursive
119+
token: ${{ secrets.GITHUB_TOKEN }}
120+
121+
- name: Setup Bun
122+
if: env.SKIP != 'true'
123+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
124+
with:
125+
bun-version: 1.3.12
126+
127+
- name: Install dependencies
128+
if: env.SKIP != 'true'
129+
run: bun install --frozen-lockfile
130+
131+
- name: JS host bindings snapshot test
132+
if: env.SKIP != 'true'
133+
working-directory: packages/cre-sdk
134+
run: bun test src/sdk/wasm/host-bindings-contract.test.ts
135+
136+
- name: Diff Rust host imports vs baseline
137+
if: env.SKIP != 'true'
138+
run: |
139+
grep -E '^\s+fn [a-z_]+\(' \
140+
packages/cre-sdk-javy-plugin/src/javy_chainlink_sdk/src/lib.rs \
141+
| grep -v '{$' > /tmp/current-imports.txt
142+
143+
if ! diff packages/cre-sdk-javy-plugin/src/javy_chainlink_sdk/host-imports-baseline.txt \
144+
/tmp/current-imports.txt; then
145+
echo "::error::Rust host import signatures changed. Update host-imports-baseline.txt if intentional."
146+
exit 1
147+
fi

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ jobs:
110110
echo ""
111111
echo "=========================================="
112112
echo "Uncommitted file changes detected after full-checks!"
113+
echo "Changed files:"
114+
git diff --name-only
113115
echo "Run 'bun full-checks' locally and commit the resulting changes."
114116
echo "=========================================="
115117
exit 1

.github/workflows/template-compatibility-comment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ jobs:
4040
with:
4141
script: |
4242
const path = require('path');
43-
const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js'));
43+
const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.cjs'));
4444
await run({ github, context });

PUBLISHING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ There are 2 possible scenarios that you might encounter:
2323

2424
Below ale the steps for two scenarios.
2525

26+
> **Breaking changes:** If the release includes any intentional breaking changes to the
27+
> TypeScript API surface, JS host bindings, or Rust host imports, the corresponding baseline
28+
> files (`api-baseline.d.ts`, `__snapshots__/host-bindings-contract.test.ts.snap`,
29+
> `host-imports-baseline.txt`) must already be committed on the release branch before
30+
> tagging. The `breaking-changes` CI job must be green before publishing.
31+
2632
### 1. Both packages need an update
2733

2834
1. Create a new branch from `main` with the name `release-candidate-vx.y.z` (for example `release-candidate-v1.0.8`).

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,32 @@ bun generate:chain-selectors # Update chain selector types
266266
bun generate:sdk # Generate all SDK types and code
267267
```
268268

269+
### Breaking Changes
270+
271+
The [`breaking-changes`](./.github/workflows/breaking-changes.yml) workflow blocks PRs that alter any of the three protected contracts. If your change is intentional, update the relevant baseline before pushing:
272+
273+
| Contract | What triggers the failure | How to update |
274+
|---|---|---|
275+
| Proto fields | Field deleted, renamed, renumbered, or type changed | No baseline file — CI runs `buf breaking` on `submodules/chainlink-protos` (`cre` module) against the submodule commit pinned on `main` |
276+
| TypeScript public API | An exported type/interface was removed or changed | Run `bun run update-api-baseline` inside `packages/cre-sdk` and commit `api-baseline.d.ts` |
277+
| JS host binding names | A binding was added, removed, or renamed in `host-bindings.ts` | Run `bun test --update-snapshots` inside `packages/cre-sdk` and commit the updated `__snapshots__` file |
278+
| Rust host imports | An `extern "C"` import was added or removed in `lib.rs` | Re-run the sed extraction (see `breaking-changes.yml`) and commit `host-imports-baseline.txt` |
279+
280+
#### CI override labels
281+
282+
When a change is **intentionally** breaking and cannot be fixed by updating a baseline (e.g. a coordinated proto break before `main` catches up), a **maintainer** adds the matching label on the PR (this re-runs the workflow):
283+
284+
| Label | Skips |
285+
|-------|--------|
286+
| `breaking-change:proto` | `proto-breaking` (`buf breaking`) |
287+
| `breaking-change:typescript-api` | `ts-api-surface` |
288+
| `breaking-change:host-bindings` | `host-bindings` |
289+
| `breaking-change:approved` | All three jobs |
290+
291+
Labels are an audit trail in the PR timeline, not a substitute for review. Prefer updating baselines (`api-baseline.d.ts`, snapshots, `host-imports-baseline.txt`) when the new contract is the new source of truth. For proto breaks, coordinate the `chainlink-protos` submodule bump and document migration notes in the PR.
292+
293+
Restrict who can add these labels in the repo’s **Labels** settings (e.g. maintainers only).
294+
269295
For detailed development setup, see individual package READMEs:
270296

271297
- [CRE SDK Development](./packages/cre-sdk/README.md#building-from-source)

packages/cre-rust-inject-alpha/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use javy_plugin_api::javy::quickjs::prelude::*;
33
use javy_plugin_api::javy::quickjs::{Ctx, Object};
44

55
pub fn register(ctx: &Ctx<'_>) {
6-
let obj = Object::new(ctx.clone()).unwrap();
6+
let obj = Object::new(ctx.clone()).expect("failed to create rustAlpha export object");
77
obj.set(
88
"greet",
99
Func::from(|| -> String { "Hello from alpha".to_string() }),
1010
)
11-
.unwrap();
11+
.expect("failed to set rustAlpha.greet export");
1212
extend_wasm_exports(ctx, "rustAlpha", obj);
1313
}

packages/cre-rust-inject-beta/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use javy_plugin_api::javy::quickjs::prelude::*;
33
use javy_plugin_api::javy::quickjs::{Ctx, Object};
44

55
pub fn register(ctx: &Ctx<'_>) {
6-
let obj = Object::new(ctx.clone()).unwrap();
6+
let obj = Object::new(ctx.clone()).expect("failed to create rustBeta export object");
77
obj.set(
88
"greet",
99
Func::from(|| -> String { "Hello from beta".to_string() }),
1010
)
11-
.unwrap();
11+
.expect("failed to set rustBeta.greet export");
1212
extend_wasm_exports(ctx, "rustBeta", obj);
1313
}

packages/cre-sdk-examples/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ node_modules
22
dist
33
.turbo
44
tmp.js
5-
tmp.wasm
5+
tmp.wasm
6+
.cre_build_tmp.js
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"schedule": "0 */1 * * * *",
3+
"url": "https://api.mathjs.org/v4?expr=randomInt(1,101)"
4+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
ConfidentialHTTPClient,
3+
CronCapability,
4+
handler,
5+
httpRequest,
6+
json,
7+
ok,
8+
Runner,
9+
type Runtime,
10+
text,
11+
} from '@chainlink/cre-sdk'
12+
import { z } from 'zod'
13+
14+
const configSchema = z.object({
15+
schedule: z.string(),
16+
url: z.string(),
17+
})
18+
type Config = z.infer<typeof configSchema>
19+
20+
// Workflow demonstrate a usage of `httpRequest` helper
21+
// to build type-safe payloads for `ConfidentialHTTPClient`.
22+
const onCronTrigger = (runtime: Runtime<Config>) => {
23+
const client = new ConfidentialHTTPClient()
24+
25+
// Example 1: request config as separate variable
26+
const separateRequestConfig = {
27+
request: httpRequest({
28+
url: runtime.config.url,
29+
method: 'POST',
30+
bodyString: '{ hello: "world" }',
31+
multiHeaders: {
32+
'content-type': { values: ['application/json'] },
33+
},
34+
}),
35+
}
36+
37+
client.sendRequest(runtime, separateRequestConfig).result()
38+
39+
// Example 2: using helper inline
40+
client
41+
.sendRequest(runtime, {
42+
request: httpRequest({
43+
url: runtime.config.url,
44+
method: 'POST',
45+
body: { hello: 'world' },
46+
multiHeaders: {
47+
'content-type': { values: ['application/json'] },
48+
},
49+
}),
50+
})
51+
.result()
52+
53+
// Example 3: not using helper at all
54+
client
55+
.sendRequest(runtime, {
56+
request: {
57+
url: runtime.config.url,
58+
method: 'POST',
59+
// no helper -> must use bodyString/bodyBytes (proto oneof keys)
60+
bodyString: JSON.stringify({ hello: 'world' }),
61+
multiHeaders: {
62+
'content-type': { values: ['application/json'] },
63+
},
64+
},
65+
})
66+
.result()
67+
68+
return { success: true }
69+
}
70+
71+
const initWorkflow = (config: Config) => {
72+
const cron = new CronCapability()
73+
return [handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)]
74+
}
75+
76+
export async function main() {
77+
const runner = await Runner.newRunner<Config>({ configSchema })
78+
await runner.run(initWorkflow)
79+
}

0 commit comments

Comments
 (0)