diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d2e849..5633585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This changelog documents the changes between release versions. ## [Unreleased] Changes to be included in the next upcoming release +## [1.13.0] - 2025-03-27 +- Added native toolchain support for connector version upgrading and fixed Dockerized connector version upgrading ([#55](https://github.com/hasura/ndc-nodejs-lambda/pull/55)) + ## [1.12.0] - 2025-03-21 - Updated to use [TypeScript v5.8.2](https://devblogs.microsoft.com/typescript/announcing-typescript-5-8/) ([#53](https://github.com/hasura/ndc-nodejs-lambda/pull/53)) - Updated `cross-spawn` dependency to resolve [security vulnerability](https://www.cve.org/CVERecord?id=CVE-2024-21538) ([#53](https://github.com/hasura/ndc-nodejs-lambda/pull/53)) diff --git a/Dockerfile b/Dockerfile index 606238e..24213e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,10 @@ FROM node:20-alpine ARG CONNECTOR_VERSION RUN npm update -g npm -RUN apk add jq curl +RUN apk add bash jq curl COPY /docker /scripts +COPY /connector-definition/scripts/upgrade-connector.sh /scripts/upgrade-connector.sh RUN : "${CONNECTOR_VERSION:?Connector version must be set}" RUN echo ${CONNECTOR_VERSION} > /scripts/CONNECTOR_VERSION diff --git a/connector-definition/Makefile b/connector-definition/Makefile index 4a67b43..cab9233 100644 --- a/connector-definition/Makefile +++ b/connector-definition/Makefile @@ -18,6 +18,7 @@ dist dist/.hasura-connector: dist/.hasura-connector/connector-metadata.yaml: connector-metadata.yaml dist cp -f connector-metadata.yaml dist/.hasura-connector sed -i -E 's/\{\{VERSION\}\}/$(RELEASE_VERSION)/g' dist/.hasura-connector/connector-metadata.yaml + sed -i -E '/^# yaml-language-server:/d' dist/.hasura-connector/connector-metadata.yaml dist/.hasura-connector/Dockerfile: Dockerfile dist/.hasura-connector $(RELEASE_VERSION_DEP) cp -f Dockerfile dist/.hasura-connector/ diff --git a/connector-definition/connector-metadata.schema.json b/connector-definition/connector-metadata.schema.json new file mode 100644 index 0000000..1f78535 --- /dev/null +++ b/connector-definition/connector-metadata.schema.json @@ -0,0 +1,667 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "version": { + "const": "v1", + "type": "string" + }, + "packagingDefinition": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "PrebuiltDockerImage" + }, + "dockerImage": { + "type": "string" + } + }, + "required": [ + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ManagedDockerBuild" + } + }, + "required": [ + "type" + ] + } + ] + }, + "nativeToolchainDefinition": { + "type": "object", + "properties": { + "commands": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "update": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "watch": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "upgradeConfiguration": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "cliPluginEntrypoint": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "start" + ] + } + }, + "required": [ + "commands" + ] + }, + "supportedEnvironmentVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "defaultValue": { + "type": "string" + }, + "required": { + "type": "string" + } + }, + "required": [ + "description", + "name" + ] + } + }, + "commands": { + "type": "object", + "properties": { + "update": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "watch": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "printSchemaAndCapabilities": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + }, + "upgradeConfiguration": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Dockerized" + }, + "dockerImage": { + "type": "string" + }, + "commandArgs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "commandArgs", + "dockerImage", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "ShellScript" + }, + "bash": { + "type": "string" + }, + "powershell": { + "type": "string" + } + }, + "required": [ + "bash", + "powershell", + "type" + ] + }, + { + "type": "string" + } + ] + } + } + }, + "cliPlugin": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "Binary", + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "name", + "version" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "BinaryInline" + }, + "platforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "selector": { + "description": "The selector identifies the target platform for this configuration.\nIt follows the format: -\n\nPossible values:\n- darwin-arm64: macOS on ARM64 architecture (e.g., M1 Macs)\n- linux-arm64: Linux on ARM64 architecture\n- darwin-amd64: macOS on x86-64 architecture\n- windows-amd64: Windows on x86-64 architecture\n- linux-amd64: Linux on x86-64 architecture", + "enum": [ + "darwin-amd64", + "darwin-arm64", + "linux-amd64", + "linux-arm64", + "windows-amd64" + ], + "type": "string" + }, + "uri": { + "description": "The URI of the CLI plugin.\nThis CLI binary plugin should be a URL from where the binary can be downloaded,\nwithout any authentication.", + "type": "string" + }, + "sha256": { + "description": "The SHA256 hash of the binary file. This is used to verify the integrity of the downloaded binary.", + "type": "string" + }, + "bin": { + "description": "The name of the binary file. The binary file downloaded from the `uri` will be saved with this name.", + "type": "string" + } + }, + "required": [ + "bin", + "selector", + "sha256", + "uri" + ] + } + } + }, + "required": [ + "platforms", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Docker" + }, + "dockerImage": { + "type": "string" + } + }, + "required": [ + "dockerImage", + "type" + ] + } + ] + }, + "dockerComposeWatch": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "action": { + "enum": [ + "rebuild", + "sync", + "sync+restart" + ], + "type": "string" + }, + "target": { + "type": "string" + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "action", + "path" + ] + } + }, + "documentationPage": { + "type": "string" + } + }, + "required": [ + "commands", + "dockerComposeWatch", + "packagingDefinition", + "supportedEnvironmentVariables" + ] +} diff --git a/connector-definition/connector-metadata.yaml b/connector-definition/connector-metadata.yaml index edd0ff7..fdc569e 100644 --- a/connector-definition/connector-metadata.yaml +++ b/connector-definition/connector-metadata.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=connector-metadata.schema.json packagingDefinition: type: ManagedDockerBuild nativeToolchainDefinition: @@ -10,12 +11,18 @@ nativeToolchainDefinition: type: ShellScript bash: ./watch.sh powershell: ./watch.ps1 + upgradeConfiguration: + type: ShellScript + bash: | + ./upgrade-connector.sh "$HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH" "{{VERSION}}" + powershell: | + & ./upgrade-connector.ps1 "$env:HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH" "{{VERSION}}" supportedEnvironmentVariables: [] commands: upgradeConfiguration: type: Dockerized dockerImage: ghcr.io/hasura/ndc-nodejs-lambda:v{{VERSION}} - dockerCommand: ["/scripts/upgrade-connector.sh"] + commandArgs: ["/scripts/upgrade.sh"] dockerComposeWatch: # Rebuild the container if a new package restore is required because package[-lock].json changed - path: package.json diff --git a/connector-definition/scripts/upgrade-connector.ps1 b/connector-definition/scripts/upgrade-connector.ps1 new file mode 100644 index 0000000..56c9d2f --- /dev/null +++ b/connector-definition/scripts/upgrade-connector.ps1 @@ -0,0 +1,52 @@ +param( + [Parameter(Mandatory=$true)] + [string]$connector_path, + + [Parameter(Mandatory=$true)] + [string]$target_connector_version, + + [Parameter(Mandatory=$false)] + [string[]]$npm_flags = @() +) + +if (-not (Get-Command "npm" -ErrorAction SilentlyContinue)) { + Write-Host "npm could not be found. Is Node.js installed?" + exit 1 +} + +Push-Location $connector_path -ErrorAction Stop +try { + try { + $packageJson = Get-Content 'package.json' -Raw | ConvertFrom-Json + $existing_connector_version = $packageJson.dependencies.'@hasura/ndc-lambda-sdk' + } catch { + Write-Host "Unable to read the @hasura/ndc-lambda-sdk version from your package.json" + Write-Host "Please manually upgrade the @hasura/ndc-lambda-sdk package in your package.json to version $target_connector_version" + exit 1 + } + + if (-not $existing_connector_version) { + # This is very strange, their package.json must have the SDK installed but doesn't + # We'll roll with it and just install the package + Write-Host "Missing the @hasura/ndc-lambda-sdk package in your package.json. Installing version $target_connector_version" + } else { + Write-Host "Upgrading @hasura/ndc-lambda-sdk package from version $existing_connector_version to version $target_connector_version" + } + + try { + & npm install "@hasura/ndc-lambda-sdk@$target_connector_version" --save-exact --no-update-notifier @npm_flags + $exit_status = $LASTEXITCODE + } catch { + $exit_status = 1 + } + + if ($exit_status -ne 0) { + Write-Host "Failed to upgrade @hasura/ndc-lambda-sdk package to version $target_connector_version" + Write-Host "Please manually upgrade the @hasura/ndc-lambda-sdk package in your package.json to version $target_connector_version" + exit 1 + } + + Write-Host "Successfully upgraded @hasura/ndc-lambda-sdk package to version $target_connector_version" +} finally { + Pop-Location +} diff --git a/docker/upgrade-connector.sh b/connector-definition/scripts/upgrade-connector.sh similarity index 67% rename from docker/upgrade-connector.sh rename to connector-definition/scripts/upgrade-connector.sh index fd368f8..4b3f42d 100755 --- a/docker/upgrade-connector.sh +++ b/connector-definition/scripts/upgrade-connector.sh @@ -1,8 +1,31 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash set -eu -o pipefail -connector_path="${HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH:-/functions}" -target_connector_version="$(cat /scripts/CONNECTOR_VERSION)" +connector_path="${1:-}" +target_connector_version="${2:-}" +npm_flags="${3:-}" + +if [ -z "$connector_path" ]; then + echo "Error: connector path must be passed as the first argument" + exit 1 +fi + +if [ -z "$target_connector_version" ]; then + echo "Error: target connector version must be passed as the second argument" + exit 1 +fi + +if ! command -v npm &> /dev/null +then + echo "npm could not be found on the PATH. Is Node.js installed?" + exit 1 +fi + +if ! command -v jq &> /dev/null +then + echo "jq could not be found on the PATH. Is jq installed?" + exit 1 +fi cd "$connector_path" @@ -23,10 +46,7 @@ else echo "Upgrading @hasura/ndc-lambda-sdk package from version $existing_connector_version to version $target_connector_version" fi -# We do a --package-lock-only because we don't want to change the node_modules directory. -# This is because the existing node_modules directory may have been installed on a -# different platform since it is being volume mounted into a Linux container -npm install "@hasura/ndc-lambda-sdk@$target_connector_version" --save-exact --no-update-notifier --package-lock-only +npm install "@hasura/ndc-lambda-sdk@$target_connector_version" --save-exact --no-update-notifier $npm_flags exit_status=$? set -e @@ -37,4 +57,3 @@ if [ $exit_status -ne 0 ]; then fi echo "Successfully upgraded @hasura/ndc-lambda-sdk package to version $target_connector_version" -echo "You may need to run 'npm install' to install the new dependencies locally" diff --git a/docker/upgrade.sh b/docker/upgrade.sh new file mode 100755 index 0000000..c91d060 --- /dev/null +++ b/docker/upgrade.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -eu -o pipefail + +# We do a --package-lock-only because we don't want to change the node_modules directory. +# This is because the existing node_modules directory may have been installed on a +# different platform since it is being volume mounted into a Linux container +/scripts/upgrade-connector.sh "${HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH:-/functions}" "$(cat /scripts/CONNECTOR_VERSION)" "--package-lock-only" +echo "You may need to run 'npm install' to install the new dependencies locally" diff --git a/ndc-lambda-sdk/package-lock.json b/ndc-lambda-sdk/package-lock.json index 3870626..fd49c51 100644 --- a/ndc-lambda-sdk/package-lock.json +++ b/ndc-lambda-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hasura/ndc-lambda-sdk", - "version": "1.12.0", + "version": "1.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@hasura/ndc-lambda-sdk", - "version": "1.12.0", + "version": "1.13.0", "license": "Apache-2.0", "dependencies": { "@hasura/ndc-sdk-typescript": "^7.0.0", diff --git a/ndc-lambda-sdk/package.json b/ndc-lambda-sdk/package.json index a485065..5e1a40a 100644 --- a/ndc-lambda-sdk/package.json +++ b/ndc-lambda-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@hasura/ndc-lambda-sdk", - "version": "1.12.0", + "version": "1.13.0", "description": "SDK that can automatically expose TypeScript functions as Hasura NDC functions/procedures", "author": "Hasura", "license": "Apache-2.0",