fix: pass regional Flight gRPC URL; correct app-url to spice.ai/<org>/<app>#15
Merged
Conversation
…/<app> Two fixes from the latest failed deploy run. 1. SQL probe `14 UNAVAILABLE: No connection established` The @spiceai/spice SDK was initialized without `flightUrl`, so its gRPC client targeted the default localhost:50051. Per the Spice docs the regional flight endpoint is `<region>-prod-aws-flight.spiceai.io:443` (mirrors the data hostname with `-data` swapped for `-flight`). Derive that from the resolved app's cname / region and pass it through to `RuntimeClient` → `SpiceClient`. Add a `flight-url` input override that accepts `host:port` or `grpc+tls://host:port` form (scheme is stripped). 2. `app-url` was wrong Apps live at `https://spice.ai/<org>/<app-name>`, not `https://<app-name>.spice.ai`. New helper `buildAppUrl` constructs the right URL using the new `org` input, falling back to the owner part of `GITHUB_REPOSITORY` (which matches the Spice org slug for personal orgs and connected GitHub orgs), and finally to `https://spice.ai/apps` if neither is available. Tests: 101 passing (up from 93). New cases cover flight URL derivation from cname / region / explicit input / scheme stripping, and the buildAppUrl org-precedence logic.
There was a problem hiding this comment.
Pull request overview
Fixes Spice Cloud deploy action outputs and runtime probe connectivity by deriving correct portal URLs and configuring the Spice SDK with a regional Arrow Flight endpoint.
Changes:
- Construct
app-urloutput usinghttps://spice.ai/<org>/<app>via neworginput +GITHUB_REPOSITORYfallback. - Add
flight-urlinput and derive a default regional Flight gRPC endpoint to pass through toRuntimeClient/SpiceClient. - Add/extend tests for URL resolution behavior and update docs/changelog to reflect the fixes.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/main.ts |
Switches app-url output to buildAppUrl(...) and introduces the helper. |
src/inputs.ts |
Adds org and flightUrl inputs plus Flight URL derivation/helpers and parsing logic. |
src/deploy.ts |
Resolves and logs Flight URL, and passes it into the runtime client used by probes. |
action.yml |
Documents new org and flight-url inputs and clarifies runtime-url as HTTP. |
__tests__/main.test.ts |
Adds unit tests for buildAppUrl. |
__tests__/deploy.test.ts |
Adds unit tests for resolveFlightUrl. |
README.md |
Updates app-url output documentation to the canonical portal pattern. |
CHANGELOG.md |
Records the new inputs and the two fixes under Unreleased. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+322
to
+326
| it("respects explicit flight-url override (with scheme stripping)", () => { | ||
| expect( | ||
| resolveFlightUrl(sampleApp, { ...baseInputs, flightUrl: "custom-flight.example:443" }), | ||
| ).toBe("custom-flight.example:443"); | ||
| }); |
Comment on lines
+139
to
+152
| /** | ||
| * Resolve the regional Flight gRPC endpoint (`host:port`) for the SDK. Prefer | ||
| * an explicit `flight-url` input, then derive from the app's cname (replacing | ||
| * `-data` with `-flight`), then from the app's region, then from the input | ||
| * region. Returns `undefined` if no source is available — callers fall back to | ||
| * letting the SDK use HTTP. | ||
| */ | ||
| export function resolveFlightUrl(app: App, inputs: ActionInputs): string | undefined { | ||
| if (inputs.flightUrl) return inputs.flightUrl; | ||
| if (app.cname) return deriveFlightUrlFromCname(app.cname); | ||
| if (app.region) return deriveFlightUrl(app.region); | ||
| if (inputs.region) return deriveFlightUrl(inputs.region); | ||
| return undefined; | ||
| } |
Comment on lines
+200
to
+204
| // Flight URL is `host:port` (gRPC), not an HTTP URL — don't validate as URL. | ||
| // Strip an optional `grpc+tls://` / `grpc://` scheme so docs-style values work. | ||
| const flightUrlRaw = getOptional("flight-url"); | ||
| const flightUrl = flightUrlRaw?.replace(/^grpc(\+tls)?:\/\//, ""); | ||
|
|
| | `app-id` | Resolved numeric app ID. | | ||
| | `app-name` | Resolved app name. | | ||
| | `app-url` | `https://<app-name>.spice.ai`. | | ||
| | `app-url` | Portal URL of the deployed app: `https://spice.ai/<org>/<app-name>`. The `<org>` slug comes from the `org` input when set, otherwise the owner part of `GITHUB_REPOSITORY`. | |
Comment on lines
+9
to
19
| ### Added | ||
| - New `org` input. The action now constructs the `app-url` output as `https://spice.ai/<org>/<app-name>` (the canonical Spice Cloud portal URL pattern). When `org` is unset, it falls back to the owner part of `GITHUB_REPOSITORY`, which matches the Spice org slug for personal orgs and orgs created from a connected GitHub organization. | ||
| - New `flight-url` input. The action now passes a regional Apache Arrow Flight gRPC endpoint to the `@spiceai/spice` SDK so the SQL probe uses gRPC instead of falling through to localhost. When `flight-url` is unset, it's derived from the resolved app's region as `<region>-prod-aws-flight.spiceai.io:443` (mirrors the data hostname with `-data` swapped for `-flight`). A `grpc+tls://` / `grpc://` scheme prefix on the input is stripped automatically. | ||
|
|
||
| ### Fixed | ||
| - The `app-url` output was previously `https://<app-name>.spice.ai`, which doesn't resolve. It is now `https://spice.ai/<org>/<app-name>` (e.g. `https://spice.ai/lukekim/home`). | ||
| - SQL smoke test failed with `14 UNAVAILABLE: No connection established` because the SDK initialized a gRPC client with no `flightUrl` configured for Spice Cloud. The action now derives a regional flight URL by default; the SDK uses gRPC for SQL queries with HTTP fallback as it was designed to. | ||
|
|
||
| ### Added | ||
| - Auto-capture a `repository` tag from `GITHUB_REPOSITORY` when set, sanitized to fit the API's tag-value rule (`/` → `_`). Users can override by setting `repository:` explicitly in the `tags` input. | ||
| - New post-deploy dataset readiness check: poll `GET /v1/datasets?status=true` until every dataset reaches a terminal-ok state (`ready`, `disabled`, or `refreshing`); fail the job immediately on `error` or on timeout-while-pending — regardless of `fail-on-test-error`, which still only governs runtime-probe results. Statuses like `shuttingdown` and any unrecognized values are treated as still-pending so the loop never returns a false-positive "loaded". Configured via `dataset-ready-timeout-seconds` (default `300`, set `0` to skip). Dataset states are surfaced as a `datasets` action output and as a table in the GitHub job step summary. |
Comment on lines
+3
to
+11
| vi.mock("@actions/core", () => ({ | ||
| setFailed: vi.fn(), | ||
| setOutput: vi.fn(), | ||
| setSecret: vi.fn(), | ||
| info: vi.fn(), | ||
| summary: { addHeading: vi.fn(), addTable: vi.fn(), write: vi.fn() }, | ||
| })); | ||
|
|
||
| import { buildAppUrl } from "../src/main.js"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two fixes from the latest failed deploy on `lukekim/home`:
```
##[error]✗ sql (4769ms) — 14 UNAVAILABLE: No connection established. Last error: null. Resolution note:
…
App URL: https://home.spice.ai ← wrong; should be https://spice.ai/lukekim/home
```
1. SQL probe `14 UNAVAILABLE: No connection established`
The `@spiceai/spice` SDK was instantiated without a `flightUrl`, so its gRPC client targeted the default localhost:50051 and failed. Per the Spice Cloud Flight docs, the regional endpoint is `-prod-aws-flight.spiceai.io:443` (mirrors the data hostname with `-data` swapped for `-flight` — so `us-west-2-prod-aws-data.spiceai.io` → `us-west-2-prod-aws-flight.spiceai.io:443`). The action now derives that from the resolved app's cname / region and passes it through to `RuntimeClient` → `SpiceClient`. Per @lukekim's preference we keep gRPC as the primary transport (with the SDK's HTTP fallback) rather than forcing `httpOnly: true`.
New `flight-url` input override accepts `host:port` or `grpc+tls://host:port` form (scheme is stripped before passing to the SDK).
2. `app-url` was wrong
Apps live at `https://spice.ai//`, not `https://.spice.ai`. New `buildAppUrl` helper constructs the right URL using:
So a workflow on `lukekim/home` now outputs `https://spice.ai/lukekim/home\`.
Node 24 verification
Confirmed every Node-version pin is on 24 already (set in #13):
```
action.yml runs.using: node24
package.json scripts.build: esbuild --target=node24 …
package.json engines.node: ">=24"
.nvmrc: 24
.github/workflows/ci.yml: node-version: 24 (×2)
.github/workflows/release.yml: node-version: 24
```
The Node 20 deprecation warning seen in the latest workflow log is for `actions/checkout@…` (third-party, pinned by the user's workflow), not this action.
Test plan