Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/actions/rust-prelude/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Rust test prelude
description: >-
Python (interpreter only, for libpython linking) + Rust toolchain and cache.
Shared setup for every Rust test job. The caller must check out the repo first
— a local action can't be resolved until the workspace exists.

runs:
using: composite
steps:
# Rust tests link the PyO3 extension against libpython; the interpreter is
# needed at runtime but the dev deps are not, so skip the uv sync.
- name: Set up Python (libpython only)
uses: ./.github/actions/setup-python
with:
sync: "false"

- name: Set up Rust
uses: ./.github/actions/setup-rust
102 changes: 42 additions & 60 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ jobs:
name: Detect changed paths
runs-on: ubuntu-latest
outputs:
rust: ${{ steps.filter.outputs.rust }}
python: ${{ steps.filter.outputs.python }}
node: ${{ steps.filter.outputs.node }}
# Run policy folded in: true on push/dispatch (run everything) or when a
# suite's paths changed on a PR. Jobs gate on these directly.
rust: ${{ steps.decide.outputs.rust }}
python: ${{ steps.decide.outputs.python }}
node: ${{ steps.decide.outputs.node }}
steps:
- name: Check out repository
uses: actions/checkout@v7
Expand Down Expand Up @@ -71,6 +73,27 @@ jobs:
- '.github/actions/**'
- '.github/workflows/ci.yml'

- name: Decide which suites run
id: decide
# FORCE is true on push and workflow_dispatch — run every suite there.
# On PRs honor the path filter.
env:
FORCE: ${{ github.event_name != 'pull_request' }}
RUST: ${{ steps.filter.outputs.rust }}
PYTHON: ${{ steps.filter.outputs.python }}
NODE: ${{ steps.filter.outputs.node }}
run: |
decide() {
if [ "$FORCE" = "true" ] || [ "$2" = "true" ]; then
echo "$1=true" >> "$GITHUB_OUTPUT"
else
echo "$1=false" >> "$GITHUB_OUTPUT"
fi
}
decide rust "$RUST"
decide python "$PYTHON"
decide node "$NODE"

lint:
name: Lint & Static Analysis
runs-on: ubuntu-latest
Expand Down Expand Up @@ -129,24 +152,19 @@ jobs:
rust-test:
name: Rust Tests (SQLite)
needs: changes
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python (libpython for the linked extension)
uses: ./.github/actions/setup-python
with:
sync: "false"

- name: Set up Rust
uses: ./.github/actions/setup-rust
- uses: actions/checkout@v7
- uses: ./.github/actions/rust-prelude

# $pythonLocation is exported to the job env by setup-python at runtime.
- name: Run Rust test suite
# $pythonLocation is exported to the job env by setup-python at runtime.
run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace

- name: Run mesh crate tests
run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test -p taskito-mesh

- name: Check build with native-async features
run: cargo check --workspace --features native-async

Expand All @@ -156,7 +174,7 @@ jobs:
rust-test-postgres:
name: Rust Tests (PostgreSQL)
needs: changes
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
services:
postgres:
Expand All @@ -173,16 +191,8 @@ jobs:
--health-timeout 5s
--health-retries 10
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python (libpython for the linked extension)
uses: ./.github/actions/setup-python
with:
sync: "false"

- name: Set up Rust
uses: ./.github/actions/setup-rust
- uses: actions/checkout@v7
- uses: ./.github/actions/rust-prelude

- name: Run Rust test suite (PostgreSQL backend)
run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace --features postgres,workflows
Expand All @@ -192,7 +202,7 @@ jobs:
rust-test-redis:
name: Rust Tests (Redis)
needs: changes
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
services:
redis:
Expand All @@ -205,46 +215,18 @@ jobs:
--health-timeout 3s
--health-retries 10
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python (libpython for the linked extension)
uses: ./.github/actions/setup-python
with:
sync: "false"

- name: Set up Rust
uses: ./.github/actions/setup-rust
- uses: actions/checkout@v7
- uses: ./.github/actions/rust-prelude

- name: Run Rust test suite (Redis backend)
run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace --features redis,workflows
env:
TASKITO_REDIS_TEST_URL: redis://localhost:6379/15

rust-test-mesh:
name: Rust Tests (Mesh)
needs: changes
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python (libpython for the linked extension)
uses: ./.github/actions/setup-python
with:
sync: "false"

- name: Set up Rust
uses: ./.github/actions/setup-rust

- name: Run mesh crate tests
run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test -p taskito-mesh

node-test:
name: Node SDK Tests
needs: changes
if: needs.changes.outputs.node == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
if: needs.changes.outputs.node == 'true'
runs-on: ubuntu-latest
steps:
- name: Check out repository
Expand Down Expand Up @@ -283,7 +265,7 @@ jobs:
test:
name: Python Tests (${{ matrix.os }} / Python ${{ matrix.python-version }})
needs: [lint, changes]
if: needs.changes.outputs.python == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
if: needs.changes.outputs.python == 'true'
runs-on: ${{ matrix.os }}
env:
# setup-python on macOS upgrades certifi via pip; pip's HTTP cache
Expand Down Expand Up @@ -349,7 +331,7 @@ jobs:
ci-status:
name: CI status
if: always()
needs: [changes, lint, rust-test, rust-test-postgres, rust-test-redis, rust-test-mesh, node-test, test]
needs: [changes, lint, rust-test, rust-test-postgres, rust-test-redis, node-test, test]
runs-on: ubuntu-latest
steps:
- name: Check that no required job failed
Expand Down
44 changes: 34 additions & 10 deletions .github/workflows/publish-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ name: Publish to npm

# Builds the napi addon as prebuilt per-platform `.node` binaries on native
# runners (no cross-compilation — the same approach the Python publish.yml uses
# for wheels), then publishes the per-platform `taskito-<triple>` packages plus
# the main `taskito` package to npm via OIDC trusted publishing + provenance.
# for wheels), then publishes the per-platform `@byteveda/taskito-<triple>`
# packages plus the main `@byteveda/taskito` package to npm via OIDC trusted
# publishing + provenance. The npm scope sidesteps the spam-detection 403 that
# unscoped per-platform names trip on first publish.

on:
push:
Expand Down Expand Up @@ -272,27 +274,35 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Publish per-platform packages
id: platforms
if: ${{ !inputs.dry-run }}
working-directory: sdks/node
# Skip any package already live at this version so a rerun after a
# partial publish can finish the rest instead of aborting on the first.
# partial publish can finish the rest. A single platform failing (e.g.
# npm spam-detection 403) must NOT abort the run before the main package
# publishes — the SDK still installs on every platform that did land.
# Record failures and surface them at the end of the job instead.
run: |
failed=""
for dir in npm/*/; do
pkg=$(node -p "require('./${dir}package.json').name")
if npm view "${pkg}@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::${pkg}@${VERSION} already published — skipping"
elif npm publish "$dir" --provenance --access public; then
echo "Published ${pkg}@${VERSION}"
else
echo "Publishing ${pkg}@${VERSION}"
npm publish "$dir" --provenance --access public
echo "::warning::Failed to publish ${pkg}@${VERSION}"
failed="${failed} ${pkg}"
fi
done
echo "failed=${failed# }" >> "$GITHUB_OUTPUT"

- name: Publish main package
if: ${{ !inputs.dry-run }}
working-directory: sdks/node
run: |
if npm view "taskito@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::taskito@${VERSION} already published — skipping"
if npm view "@byteveda/taskito@${VERSION}" version >/dev/null 2>&1; then
echo "::notice::@byteveda/taskito@${VERSION} already published — skipping"
else
npm publish --provenance --access public
fi
Expand All @@ -306,13 +316,13 @@ jobs:
if: ${{ !inputs.dry-run }}
run: |
for _attempt in 1 2 3 4 5; do
if npm view "taskito@${VERSION}" version >/dev/null 2>&1; then
echo "taskito@${VERSION} live on npm"
if npm view "@byteveda/taskito@${VERSION}" version >/dev/null 2>&1; then
echo "@byteveda/taskito@${VERSION} live on npm"
exit 0
fi
sleep 15
done
echo "::error::taskito@${VERSION} not found on npm"
echo "::error::@byteveda/taskito@${VERSION} not found on npm"
exit 1

- name: Create git tag
Expand All @@ -336,3 +346,17 @@ jobs:
--title "taskito (Node) v${VERSION}" \
--generate-notes
fi

# The main package + tag + release are done; only now flag any platform
# binary that didn't publish, so a missing target turns the job red for a
# maintainer to chase without having blocked the otherwise-usable release.
- name: Fail if any platform package was skipped
if: ${{ always() && !inputs.dry-run && steps.platforms.outputs.failed != '' }}
# Pass the list via env, not inline ${{ }} expansion, so a package name
# can't break out of the shell script (template-injection defense).
env:
FAILED_PLATFORMS: ${{ steps.platforms.outputs.failed }}
run: |
echo "::error::Platform packages that did not publish: ${FAILED_PLATFORMS}"
echo "::error::Install on those platforms will fail until they are republished."
exit 1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
2 changes: 1 addition & 1 deletion docs/app/components/landing/sections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ export function CTA() {
</p>
<div className="install-row">
<InstallPill cmd="pip install taskito" />
<InstallPill cmd="pnpm add taskito" />
<InstallPill cmd="pnpm add @byteveda/taskito" />
</div>
<div className="btns">
<Link className="btn pri" to="/python/getting-started/quickstart">
Expand Down
4 changes: 2 additions & 2 deletions docs/content/docs/node/api-reference/context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: "currentJob() — the running task's job context."
---

```ts
import { currentJob } from "taskito";
import { currentJob } from "@byteveda/taskito";

const job = currentJob(); // JobContext | undefined
```
Expand Down Expand Up @@ -36,7 +36,7 @@ cancellation flow.
## `useResource`

```ts
import { useResource } from "taskito";
import { useResource } from "@byteveda/taskito";

const db = await useResource<Pool>("db"); // inside a running task
```
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/api-reference/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Every error the SDK throws extends `TaskitoError`, so a single `catch` can scope
all of them; the specific subclasses let you branch on what went wrong.

```ts
import { TaskitoError, ResourceNotFoundError } from "taskito";
import { TaskitoError, ResourceNotFoundError } from "@byteveda/taskito";

try {
await runJob();
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/api-reference/queue.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: "Construct a queue and the full producer/admin API."
---

```ts
import { Queue } from "taskito";
import { Queue } from "@byteveda/taskito";

const queue = new Queue(options);
```
Expand Down
4 changes: 2 additions & 2 deletions docs/content/docs/node/api-reference/serializers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ description: "JsonSerializer, MsgpackSerializer, and the Serializer interface."
---

```ts
import { Queue, JsonSerializer, MsgpackSerializer } from "taskito";
import type { Serializer } from "taskito";
import { Queue, JsonSerializer, MsgpackSerializer } from "@byteveda/taskito";
import type { Serializer } from "@byteveda/taskito";
```

## Built-ins
Expand Down
4 changes: 2 additions & 2 deletions docs/content/docs/node/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The Node.js SDK is a thin [napi-rs](https://napi.rs) shell over the Taskito Rust
core — peer to the Python SDK, with **no Python dependency**.

```bash
pnpm add taskito
pnpm add @byteveda/taskito
```

Requires Node.js >= 18. Ships as dual **ESM + CommonJS** with bundled type
Expand Down Expand Up @@ -35,7 +35,7 @@ the `postgres,redis,workflows,mesh` cargo features enabled.
## Pick a backend

```ts
import { Queue } from "taskito";
import { Queue } from "@byteveda/taskito";

new Queue({ dbPath: "taskito.db" }); // SQLite (default)
new Queue({ backend: "postgres", dsn: process.env.PG_URL, schema: "taskito" });
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/getting-started/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: "Define a task, enqueue a job, run a worker, await the result — i
Four steps: construct a queue, register a task, enqueue a job, run a worker.

```ts
import { Queue } from "taskito";
import { Queue } from "@byteveda/taskito";

const queue = new Queue({ dbPath: "taskito.db" });

Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/guides/core/cancellation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Cancellation is **cooperative**. A running task reads its context via
`currentJob()` and watches the `AbortSignal`:

```ts
import { currentJob } from "taskito";
import { currentJob } from "@byteveda/taskito";

queue.task("download", async (url: string) => {
const { signal } = currentJob() ?? {};
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/guides/core/predicates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ queue.gate("charge", ({ args }) => args[0] <= 10_000);
Combine predicates with `allOf`, `anyOf`, and `not`:

```ts
import { allOf, anyOf, not } from "taskito";
import { allOf, anyOf, not } from "@byteveda/taskito";

const businessHours = () => {
const h = new Date().getHours();
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/node/guides/core/streaming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Call `currentJob().publish(value)` from inside the task. The value must be
JSON-serializable.

```ts
import { currentJob } from "taskito";
import { currentJob } from "@byteveda/taskito";

queue.task("train", async (epochs: number) => {
const job = currentJob();
Expand Down
Loading