cli-ext/README.md
Rama master · réplica de lectura del repositorio original.
## Building the binaries
The binaries are built using the [pkg](https://www.npmjs.com/package/pkg) package. Run the following commands:
```bash
npm install
npm run build
OR
make clean-build
```
The binaries will be placed in the `bin` directory at root. Copy the binary to your PATH as `cli-ext`.
Presently, binaries are built for following targets:
- node24.22.1-linux-x64
- node24.22.1-windows-x64
- node24.22.1-macos-x64
- node24.22.1-linux-arm64
- node24.22.1-macos-arm64
More information on `pkg` and `targets` is available on the [pkg page](https://www.npmjs.com/package/pkg).
If you want to build only a specific binary or one for a different target altogether, you can do so by following the commands below:
```bash
npm install -g pkg
pkg ./build/command.js --output <output-path> -t <node-range>-<platform>-<architecture>
```
A supported list and combinations of `node-range`, `platform` and `architecture` is provided [here](https://www.npmjs.com/package/pkg-fetch).
The hasura cli provides a `--cli-ext-path` flag to use a different cli-ext binary.
## Releasing cli-ext
cli-ext publication uses an already-built pull-request commit, separately from
an HGE/CLI release:
1. Open a PR with the cli-ext changes and apply `auto-approve-ci` and
`ci/build-cli-ext`. Do not apply `auto-update-auto-merge` before the source
build: that mode reuses cached binaries. CLI/console dev modes and server-test
mode can omit the job, and normal mono CI skip rules still apply.
2. Wait for a successful, actually executed source build in the mono pipeline's
`build-cli-ext` job. Record the exact source commit and producer job; a green
cached or skipped job is not sufficient.
3. Push a new `cli-ext/vX.Y.Z` tag on that exact successfully built commit. The
tagged commit must include the release workflow and producer-selection logic
described here. The `release cli-ext` GitHub workflow selects the newest
**mono** producer status on that SHA; a pending or failed mono build blocks
publication, even if an older mono build succeeded. Tag-triggered EE-lite
statuses are not release producers. No new source build is requested by the
tag itself.
4. Check the workflow's provenance/checksum verification, macOS arm64 ad-hoc
signing, and publication to
`gs://graphql-engine-cdn.hasura.io/cli-ext/releases/versioned/vX.Y.Z/`.
Missing, expired, cached, or mismatched artifacts fail closed; rebuild the
same source commit in mono and rerun the workflow instead of bypassing checks.
5. In the same PR, update the cached-download version in
`.buildkite/scripts/cli/build-cli-ext.sh` and the Go CLI fallback version in
`cli/internal/cliext/cli_ext.go`. Leave the release tag on the earlier built
commit; then get the pin update reviewed and merged.
6. Include that pin update in the next relevant HGE/CLI release branch and verify
the helper embedded by the released CLI. Publishing a helper does not upgrade
existing CLI executables, and `cli-ext/package.json` does not determine the
published CDN version.
The Buildkite webhook filters can still start a full EE-lite pipeline for a
`cli-ext/` tag. This procedure does not require its source build or its HGE
publication/deployment gates, and does not change those external pipeline filters.
Run the credential-free release-helper regressions with:
```bash
node --test .github/scripts/cli-ext-*.test.js
```
## API
```
scaffolder sdl to '{ "types": { "enums": [], "scalars": [], "input_objects": [ { "name": "UserInput", "fields": [ { "name": "username", "type": "String", "description": "lalz" }, { "name": "password", "type": "String!", "description": "pass" } ] } ], "objects": [ { "name": "UserInfo", "fields": [ { "name": "accessToken", "type": "String", "description": "lolz" } ] } ] } }'
```
```
scaffolder sdl from '{ "sdl": { "action": "type Mutation { actionName (arg1: SampleInput!): SampleOutput }", "types": "type SampleOutput { accessToken: String! } input SampleInput { username: String! password: String! }" }, "types": { "scalars": [], "enums": [], "input_objects": [], "objects": [] } }'
```
```
scaffolder scaffold '{ "action": { "action_name": "validatedUserInsert", "action_defn": { "kind": "synchronous", "webhook": "http://192.168.0.107:5000/actions", "arguments": [{ "name": "user", "type": "_user_insert_input!", "description": null }], "output_type": "UserInfo" } }, "types": { "enums": [{ "name": "_user_constraint", "values": [{ "value": "user_pkey", "description": null, "is_deprecated": null }], "description": null }, { "name": "_user_update_column", "values": [{ "value": "email", "description": null, "is_deprecated": null }, { "value": "id", "description": null, "is_deprecated": null }, { "value": "username", "description": null, "is_deprecated": null }], "description": null }, { "name": "_article_constraint", "values": [{ "value": "article_pkey", "description": null, "is_deprecated": null }], "description": null }, { "name": "_article_update_column", "values": [{ "value": "author_id", "description": null, "is_deprecated": null }, { "value": "content", "description": null, "is_deprecated": null }, { "value": "id", "description": null, "is_deprecated": null }, { "value": "title", "description": null, "is_deprecated": null }], "description": null }], "objects": [{ "name": "UserInfo", "fields": [{ "name": "userId", "type": "Int", "arguments": null, "description": null }, { "name": "accessToken", "type": "String", "arguments": null, "description": null }], "description": null, "relationships": [{ "name": "user", "remote_table": "user", "field_mapping": { "userId": "id" } }, { "name": "anotherrel", "remote_table": "article", "field_mapping": { "userId": "id" } }] }, { "name": "SMSInfo", "fields": [{ "name": "sms_id", "type": "String!", "arguments": null, "description": null }], "description": null, "relationships": null }], "scalars": [], "input_objects": [{ "name": "_user_insert_input", "fields": [{ "name": "articles", "type": "_article_arr_rel_insert_input", "description": null }, { "name": "email", "type": "String", "description": null }, { "name": "id", "type": "Int", "description": null }, { "name": "username", "type": "String", "description": null }], "description": null }, { "name": "_article_arr_rel_insert_input", "fields": [{ "name": "data", "type": "[_article_insert_input!]!", "description": null }, { "name": "on_conflict", "type": "_article_on_conflict", "description": null }], "description": null }, { "name": "_article_insert_input", "fields": [{ "name": "author_id", "type": "Int", "description": null }, { "name": "content", "type": "String", "description": null }, { "name": "id", "type": "Int", "description": null }, { "name": "title", "type": "String", "description": null }, { "name": "user", "type": "_user_obj_rel_insert_input", "description": null }], "description": null }, { "name": "_user_obj_rel_insert_input", "fields": [{ "name": "data", "type": "_user_insert_input!", "description": null }, { "name": "on_conflict", "type": "_user_on_conflict", "description": null }], "description": null }, { "name": "_user_on_conflict", "fields": [{ "name": "constraint", "type": "_user_constraint!", "description": null }, { "name": "update_columns", "type": "[_user_update_column!]!", "description": null }, { "name": "where", "type": "_user_bool_exp", "description": null }], "description": null }, { "name": "_user_bool_exp", "fields": [{ "name": "_and", "type": "[_user_bool_exp]", "description": null }, { "name": "_not", "type": "_user_bool_exp", "description": null }, { "name": "_or", "type": "[_user_bool_exp]", "description": null }, { "name": "articles", "type": "_article_bool_exp", "description": null }, { "name": "email", "type": "_String_comparison_exp", "description": null }, { "name": "id", "type": "_Int_comparison_exp", "description": null }, { "name": "username", "type": "_String_comparison_exp", "description": null }], "description": null }, { "name": "_article_bool_exp", "fields": [{ "name": "_and", "type": "[_article_bool_exp]", "description": null }, { "name": "_not", "type": "_article_bool_exp", "description": null }, { "name": "_or", "type": "[_article_bool_exp]", "description": null }, { "name": "author_id", "type": "_Int_comparison_exp", "description": null }, { "name": "content", "type": "_String_comparison_exp", "description": null }, { "name": "id", "type": "_Int_comparison_exp", "description": null }, { "name": "title", "type": "_String_comparison_exp", "description": null }, { "name": "user", "type": "_user_bool_exp", "description": null }], "description": null }, { "name": "_Int_comparison_exp", "fields": [{ "name": "_eq", "type": "Int", "description": null }, { "name": "_gt", "type": "Int", "description": null }, { "name": "_gte", "type": "Int", "description": null }, { "name": "_in", "type": "[Int!]", "description": null }, { "name": "_is_null", "type": "Boolean", "description": null }, { "name": "_lt", "type": "Int", "description": null }, { "name": "_lte", "type": "Int", "description": null }, { "name": "_neq", "type": "Int", "description": null }, { "name": "_nin", "type": "[Int!]", "description": null }], "description": null }, { "name": "_String_comparison_exp", "fields": [{ "name": "_eq", "type": "String", "description": null }, { "name": "_gt", "type": "String", "description": null }, { "name": "_gte", "type": "String", "description": null }, { "name": "_ilike", "type": "String", "description": null }, { "name": "_in", "type": "[String!]", "description": null }, { "name": "_is_null", "type": "Boolean", "description": null }, { "name": "_like", "type": "String", "description": null }, { "name": "_lt", "type": "String", "description": null }, { "name": "_lte", "type": "String", "description": null }, { "name": "_neq", "type": "String", "description": null }, { "name": "_nilike", "type": "String", "description": null }, { "name": "_nin", "type": "[String!]", "description": null }, { "name": "_nlike", "type": "String", "description": null }, { "name": "_nsimilar", "type": "String", "description": null }, { "name": "_similar", "type": "String", "description": null }], "description": null }, { "name": "_article_on_conflict", "fields": [{ "name": "constraint", "type": "_article_constraint!", "description": null }, { "name": "update_columns", "type": "[_article_update_column!]!", "description": null }, { "name": "where", "type": "_article_bool_exp", "description": null }], "description": null }, { "name": "SMSInput", "fields": [{ "name": "sms", "type": "String", "description": null }, { "name": "is_international", "type": "Boolean", "description": null }, { "name": "price", "type": "[Price]", "description": null }], "description": null }, { "name": "Price", "fields": [{ "name": "value", "type": "String", "description": null }], "description": null }] }, "framework": "typescript-express" }'
```
## Templaters
The templaters are present in the `src/templaters` directory. You can modify the templaters and build.
Each templater function must return an array of file objects. Something like:
```
[
{
"name": "filename1.js",
"content": "filename1 content"
},
{
"name": "filename2.js",
"content": "filename2 content"
}
]
```
These files will be generated in the PWD of execution.
## Packaged runtime provenance and release gate
The locked packager is `@yao-pkg/pkg` 6.24.0, which requires `@yao-pkg/pkg-fetch` 3.6.7. The published pkg-fetch 3.6.7 patch manifest maps the pinned Node 24.22.1 targets to its Node 24.22.1 packaging patch. An updated build-host Node does not update the embedded runtime by itself.
Node 24.22.1's official release records the Brotli backport `49dff6f57a` of upstream `0ed4268a4401abcb4472096340477cf4af7d1a2b`. Its embedded Brotli version string remains 1.2.0; that string alone is not a security acceptance check. pkg-fetch's expected SHA-256 for its Linux x64 supplier binary is `c0a4715edd09277f0e66c780dc16eb40be7c14e823e074e0218dd6c27fdac4c3`.
For release validation, use a clean `PKG_CACHE_PATH`, disallow `PKG_NODE_PATH` overrides and locally built/fallback supplier entries, and verify the fetched supplier hash against the locked package. Validate each target independently. Retain the source commit, lockfile, supplier hash and final cli-ext hash, then run benign SDL-to/from and controlled-local-template scaffolding checks on the resulting executable in the final consumer images. A successful roundtrip or exported decoder constant is not a malformed-input regression.
The runtime update does not restrict action-codegen's template execution: its URL and local template paths still execute template JavaScript. Runtime dependency remediation must not be treated as remediation of that separate behavior or as evidence about a reported environment-extraction incident.