From 254e4be9fedb22790756ad860f62c2a7d4ffe4bf Mon Sep 17 00:00:00 2001 From: madmecodes Date: Wed, 6 May 2026 14:20:06 +0530 Subject: [PATCH] Add JSON Schema generation tool for ECS application config --- docs/static/jsonschema/ecs.json | 1086 +++++++++++++++++++++++++++++++ go.mod | 5 + go.sum | 10 + tool/jsonschema-gen/main.go | 82 +++ 4 files changed, 1183 insertions(+) create mode 100644 docs/static/jsonschema/ecs.json create mode 100644 tool/jsonschema-gen/main.go diff --git a/docs/static/jsonschema/ecs.json b/docs/static/jsonschema/ecs.json new file mode 100644 index 0000000000..0525395286 --- /dev/null +++ b/docs/static/jsonschema/ecs.json @@ -0,0 +1,1086 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/pipe-cd/pipecd/pkg/config/ecs-application-spec", + "$ref": "#/$defs/ECSApplicationSpec", + "$defs": { + "AnalysisExpected": { + "properties": { + "min": { + "type": "number" + }, + "max": { + "type": "number" + } + }, + "additionalProperties": false, + "type": "object" + }, + "AnalysisHTTPHeader": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "AnalysisStageOptions": { + "properties": { + "duration": { + "type": "integer" + }, + "restartThreshold": { + "type": "integer" + }, + "metrics": { + "items": { + "$ref": "#/$defs/TemplatableAnalysisMetrics" + }, + "type": "array" + }, + "logs": { + "items": { + "$ref": "#/$defs/TemplatableAnalysisLog" + }, + "type": "array" + }, + "https": { + "items": { + "$ref": "#/$defs/TemplatableAnalysisHTTP" + }, + "type": "array" + }, + "skipOn": { + "$ref": "#/$defs/SkipOptions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "AnalysisTemplateRef": { + "properties": { + "name": { + "type": "string" + }, + "appArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "additionalProperties": false, + "type": "object" + }, + "Attachment": { + "properties": { + "sources": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "targets": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ChainApplicationMatcher": { + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "additionalProperties": false, + "type": "object" + }, + "CloudRunPromoteStageOptions": { + "properties": { + "percent": { + "$ref": "#/$defs/Percentage" + } + }, + "additionalProperties": false, + "type": "object" + }, + "CloudRunSyncStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "CustomSyncOptions": { + "properties": { + "timeout": { + "type": "integer" + }, + "envs": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "run": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DeploymentChain": { + "properties": { + "applications": { + "items": { + "$ref": "#/$defs/ChainApplicationMatcher" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DeploymentCommitMatcher": { + "properties": { + "quickSync": { + "type": "string" + }, + "pipeline": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DeploymentNotification": { + "properties": { + "mentions": { + "items": { + "$ref": "#/$defs/NotificationMention" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DeploymentPipeline": { + "properties": { + "stages": { + "items": { + "$ref": "#/$defs/PipelineStage" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DeploymentPlanner": { + "properties": { + "alwaysUsePipeline": { + "type": "boolean" + }, + "autoRollback": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "DriftDetection": { + "properties": { + "ignoreFields": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSApplicationSpec": { + "properties": { + "name": { + "type": "string" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "description": { + "type": "string" + }, + "planner": { + "$ref": "#/$defs/DeploymentPlanner" + }, + "commitMatcher": { + "$ref": "#/$defs/DeploymentCommitMatcher" + }, + "pipeline": { + "$ref": "#/$defs/DeploymentPipeline" + }, + "trigger": { + "$ref": "#/$defs/Trigger" + }, + "postSync": { + "$ref": "#/$defs/PostSync" + }, + "timeout": { + "type": "integer" + }, + "encryption": { + "$ref": "#/$defs/SecretEncryption" + }, + "attachment": { + "$ref": "#/$defs/Attachment" + }, + "notification": { + "$ref": "#/$defs/DeploymentNotification" + }, + "eventWatcher": { + "items": { + "$ref": "#/$defs/EventWatcherConfig" + }, + "type": "array" + }, + "driftDetection": { + "$ref": "#/$defs/DriftDetection" + }, + "plugins": true, + "input": { + "$ref": "#/$defs/ECSDeploymentInput" + }, + "quickSync": { + "$ref": "#/$defs/ECSSyncStageOptions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSCanaryCleanStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "ECSCanaryRolloutStageOptions": { + "properties": { + "scale": { + "$ref": "#/$defs/Percentage" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSDeploymentInput": { + "properties": { + "clusterArn": { + "type": "string" + }, + "launchType": { + "type": "string" + }, + "awsvpcConfiguration": { + "$ref": "#/$defs/ECSVpcConfiguration" + }, + "serviceDefinitionFile": { + "type": "string" + }, + "taskDefinitionFile": { + "type": "string" + }, + "targetGroups": { + "$ref": "#/$defs/ECSTargetGroups" + }, + "autoRollback": { + "type": "boolean" + }, + "runStandaloneTask": { + "type": "boolean" + }, + "accessType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSPrimaryRolloutStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "ECSSyncStageOptions": { + "properties": { + "recreate": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSTargetGroup": { + "properties": { + "targetGroupArn": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "containerPort": { + "type": "integer" + }, + "loadBalancerName": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSTargetGroups": { + "properties": { + "primary": { + "$ref": "#/$defs/ECSTargetGroup" + }, + "canary": { + "$ref": "#/$defs/ECSTargetGroup" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSTrafficRoutingStageOptions": { + "properties": { + "canary": { + "$ref": "#/$defs/Percentage" + }, + "primary": { + "$ref": "#/$defs/Percentage" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ECSVpcConfiguration": { + "properties": { + "subnets": { + "items": { + "type": "string" + }, + "type": "array" + }, + "assignPublicIp": { + "type": "string" + }, + "securityGroups": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EventWatcherConfig": { + "properties": { + "matcher": { + "$ref": "#/$defs/EventWatcherMatcher" + }, + "handler": { + "$ref": "#/$defs/EventWatcherHandler" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EventWatcherHandler": { + "properties": { + "type": { + "type": "string" + }, + "config": { + "$ref": "#/$defs/EventWatcherHandlerConfig" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EventWatcherHandlerConfig": { + "properties": { + "commitMessage": { + "type": "string" + }, + "makePullRequest": { + "type": "boolean" + }, + "replacements": { + "items": { + "$ref": "#/$defs/EventWatcherReplacement" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EventWatcherMatcher": { + "properties": { + "name": { + "type": "string" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EventWatcherReplacement": { + "properties": { + "file": { + "type": "string" + }, + "yamlField": { + "type": "string" + }, + "jsonField": { + "type": "string" + }, + "hCLField": { + "type": "string" + }, + "regex": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sBaselineCleanStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "K8sBaselineRolloutStageOptions": { + "properties": { + "replicas": { + "$ref": "#/$defs/Replicas" + }, + "suffix": { + "type": "string" + }, + "createService": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sCanaryCleanStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "K8sCanaryRolloutStageOptions": { + "properties": { + "replicas": { + "$ref": "#/$defs/Replicas" + }, + "suffix": { + "type": "string" + }, + "createService": { + "type": "boolean" + }, + "patches": { + "items": { + "$ref": "#/$defs/K8sResourcePatch" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sPrimaryRolloutStageOptions": { + "properties": { + "suffix": { + "type": "string" + }, + "createService": { + "type": "boolean" + }, + "addVariantLabelToSelector": { + "type": "boolean" + }, + "prune": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sResourcePatch": { + "properties": { + "target": { + "$ref": "#/$defs/K8sResourcePatchTarget" + }, + "ops": { + "items": { + "$ref": "#/$defs/K8sResourcePatchOp" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sResourcePatchOp": { + "properties": { + "op": { + "type": "string" + }, + "path": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sResourcePatchTarget": { + "properties": { + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "documentRoot": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object" + }, + "K8sTrafficRoutingStageOptions": { + "properties": { + "all": { + "type": "string" + }, + "primary": { + "$ref": "#/$defs/Percentage" + }, + "canary": { + "$ref": "#/$defs/Percentage" + }, + "baseline": { + "$ref": "#/$defs/Percentage" + } + }, + "additionalProperties": false, + "type": "object" + }, + "LambdaCanaryRolloutStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "LambdaPromoteStageOptions": { + "properties": { + "percent": { + "$ref": "#/$defs/Percentage" + } + }, + "additionalProperties": false, + "type": "object" + }, + "LambdaSyncStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "NotificationMention": { + "properties": { + "event": { + "type": "string" + }, + "slack": { + "items": { + "type": "string" + }, + "type": "array" + }, + "slackusers": { + "items": { + "type": "string" + }, + "type": "array" + }, + "slackgroups": { + "items": { + "type": "string" + }, + "type": "array" + }, + "email": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "OnChain": { + "properties": { + "disabled": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "OnCommand": { + "properties": { + "disabled": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "OnCommit": { + "properties": { + "disabled": { + "type": "boolean" + }, + "paths": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ignores": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "OnOutOfSync": { + "properties": { + "disabled": { + "type": "boolean" + }, + "minWindow": { + "type": "integer" + } + }, + "additionalProperties": false, + "type": "object" + }, + "Percentage": { + "properties": { + "number": { + "type": "integer" + }, + "hasSuffix": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "PipelineStage": { + "properties": { + "iD": { + "type": "string" + }, + "name": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "with": true, + "customSyncOptions": { + "$ref": "#/$defs/CustomSyncOptions" + }, + "waitStageOptions": { + "$ref": "#/$defs/WaitStageOptions" + }, + "waitApprovalStageOptions": { + "$ref": "#/$defs/WaitApprovalStageOptions" + }, + "analysisStageOptions": { + "$ref": "#/$defs/AnalysisStageOptions" + }, + "scriptRunStageOptions": { + "$ref": "#/$defs/ScriptRunStageOptions" + }, + "k8sPrimaryRolloutStageOptions": { + "$ref": "#/$defs/K8sPrimaryRolloutStageOptions" + }, + "k8sCanaryRolloutStageOptions": { + "$ref": "#/$defs/K8sCanaryRolloutStageOptions" + }, + "k8sCanaryCleanStageOptions": { + "$ref": "#/$defs/K8sCanaryCleanStageOptions" + }, + "k8sBaselineRolloutStageOptions": { + "$ref": "#/$defs/K8sBaselineRolloutStageOptions" + }, + "k8sBaselineCleanStageOptions": { + "$ref": "#/$defs/K8sBaselineCleanStageOptions" + }, + "k8sTrafficRoutingStageOptions": { + "$ref": "#/$defs/K8sTrafficRoutingStageOptions" + }, + "terraformSyncStageOptions": { + "$ref": "#/$defs/TerraformSyncStageOptions" + }, + "terraformPlanStageOptions": { + "$ref": "#/$defs/TerraformPlanStageOptions" + }, + "terraformApplyStageOptions": { + "$ref": "#/$defs/TerraformApplyStageOptions" + }, + "cloudRunSyncStageOptions": { + "$ref": "#/$defs/CloudRunSyncStageOptions" + }, + "cloudRunPromoteStageOptions": { + "$ref": "#/$defs/CloudRunPromoteStageOptions" + }, + "lambdaSyncStageOptions": { + "$ref": "#/$defs/LambdaSyncStageOptions" + }, + "lambdaCanaryRolloutStageOptions": { + "$ref": "#/$defs/LambdaCanaryRolloutStageOptions" + }, + "lambdaPromoteStageOptions": { + "$ref": "#/$defs/LambdaPromoteStageOptions" + }, + "eCSSyncStageOptions": { + "$ref": "#/$defs/ECSSyncStageOptions" + }, + "eCSCanaryRolloutStageOptions": { + "$ref": "#/$defs/ECSCanaryRolloutStageOptions" + }, + "eCSPrimaryRolloutStageOptions": { + "$ref": "#/$defs/ECSPrimaryRolloutStageOptions" + }, + "eCSCanaryCleanStageOptions": { + "$ref": "#/$defs/ECSCanaryCleanStageOptions" + }, + "eCSTrafficRoutingStageOptions": { + "$ref": "#/$defs/ECSTrafficRoutingStageOptions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "PostSync": { + "properties": { + "chain": { + "$ref": "#/$defs/DeploymentChain" + } + }, + "additionalProperties": false, + "type": "object" + }, + "Replicas": { + "properties": { + "number": { + "type": "integer" + }, + "isPercentage": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ScriptRunStageOptions": { + "properties": { + "env": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "run": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "onRollback": { + "type": "string" + }, + "skipOn": { + "$ref": "#/$defs/SkipOptions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "SecretEncryption": { + "properties": { + "encryptedSecrets": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "decryptionTargets": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "SkipOptions": { + "properties": { + "commitMessagePrefixes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "paths": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "TemplatableAnalysisHTTP": { + "properties": { + "url": { + "type": "string" + }, + "method": { + "type": "string" + }, + "headers": { + "items": { + "$ref": "#/$defs/AnalysisHTTPHeader" + }, + "type": "array" + }, + "expectedCode": { + "type": "integer" + }, + "expectedResponse": { + "type": "string" + }, + "interval": { + "type": "integer" + }, + "failureLimit": { + "type": "integer" + }, + "skipOnNoData": { + "type": "boolean" + }, + "timeout": { + "type": "integer" + }, + "template": { + "$ref": "#/$defs/AnalysisTemplateRef" + } + }, + "additionalProperties": false, + "type": "object" + }, + "TemplatableAnalysisLog": { + "properties": { + "query": { + "type": "string" + }, + "interval": { + "type": "integer" + }, + "failureLimit": { + "type": "integer" + }, + "skipOnNoData": { + "type": "boolean" + }, + "timeout": { + "type": "integer" + }, + "provider": { + "type": "string" + }, + "template": { + "$ref": "#/$defs/AnalysisTemplateRef" + } + }, + "additionalProperties": false, + "type": "object" + }, + "TemplatableAnalysisMetrics": { + "properties": { + "strategy": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "query": { + "type": "string" + }, + "expected": { + "$ref": "#/$defs/AnalysisExpected" + }, + "interval": { + "type": "integer" + }, + "failureLimit": { + "type": "integer" + }, + "skipOnNoData": { + "type": "boolean" + }, + "timeout": { + "type": "integer" + }, + "deviation": { + "type": "string" + }, + "canaryArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "baselineArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "primaryArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "template": { + "$ref": "#/$defs/AnalysisTemplateRef" + } + }, + "additionalProperties": false, + "type": "object" + }, + "TerraformApplyStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "TerraformPlanStageOptions": { + "properties": { + "exitOnNoChanges": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + }, + "TerraformSyncStageOptions": { + "properties": {}, + "additionalProperties": false, + "type": "object" + }, + "Trigger": { + "properties": { + "onCommit": { + "$ref": "#/$defs/OnCommit" + }, + "onCommand": { + "$ref": "#/$defs/OnCommand" + }, + "onOutOfSync": { + "$ref": "#/$defs/OnOutOfSync" + }, + "onChain": { + "$ref": "#/$defs/OnChain" + } + }, + "additionalProperties": false, + "type": "object" + }, + "WaitApprovalStageOptions": { + "properties": { + "timeout": { + "type": "integer" + }, + "approvers": { + "items": { + "type": "string" + }, + "type": "array" + }, + "minApproverNum": { + "type": "integer" + }, + "skipOn": { + "$ref": "#/$defs/SkipOptions" + } + }, + "additionalProperties": false, + "type": "object" + }, + "WaitStageOptions": { + "properties": { + "duration": { + "type": "integer" + }, + "skipOn": { + "$ref": "#/$defs/SkipOptions" + } + }, + "additionalProperties": false, + "type": "object" + } + } +} diff --git a/go.mod b/go.mod index e96d98542c..0821609add 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/hcl/v2 v2.0.0 + github.com/invopop/jsonschema v0.14.0 github.com/minio/minio-go/v7 v7.0.5 github.com/opencontainers/image-spec v1.1.1 github.com/ory/dockertest/v3 v3.9.1 @@ -107,7 +108,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 // indirect github.com/aws/smithy-go v1.24.2 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/buger/jsonparser v1.1.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect @@ -170,6 +173,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/runc v1.2.8 // indirect + github.com/pb33f/ordered-map/v2 v2.3.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -191,6 +195,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/multierr v1.6.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/term v0.38.0 // indirect golang.org/x/text v0.32.0 // indirect diff --git a/go.sum b/go.sum index f70bce91d7..4c5e07d51e 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 h1:O6tyji8mXmBGsHvTCB0VIhrDw19l github.com/aws/aws-sdk-go-v2/service/sts v1.31.2/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -175,6 +177,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= +github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -506,6 +510,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg= +github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -649,6 +655,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY= +github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= @@ -860,6 +868,8 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= +go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/tool/jsonschema-gen/main.go b/tool/jsonschema-gen/main.go new file mode 100644 index 0000000000..b1ad5b3bc6 --- /dev/null +++ b/tool/jsonschema-gen/main.go @@ -0,0 +1,82 @@ +// Copyright 2024 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// jsonschema-gen generates JSON Schema files from PipeCD application +// configuration structs. The generated schemas can be used by editors +// and language servers to provide validation and autocompletion for +// PipeCD YAML configuration files. +// +// Usage: +// +// go run ./tool/jsonschema-gen -out ./docs/static/jsonschema +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/invopop/jsonschema" + + "github.com/pipe-cd/pipecd/pkg/config" +) + +var outDir = flag.String("out", "docs/static/jsonschema", "output directory for generated schema files") + +type schemaTarget struct { + name string + instance any +} + +func main() { + flag.Parse() + + targets := []schemaTarget{ + {"ecs", &config.ECSApplicationSpec{}}, + } + + if err := os.MkdirAll(*outDir, 0o755); err != nil { + fmt.Fprintf(os.Stderr, "error creating output directory: %v\n", err) + os.Exit(1) + } + + r := &jsonschema.Reflector{ + KeyNamer: func(s string) string { + if len(s) == 0 { + return s + } + return strings.ToLower(s[:1]) + s[1:] + }, + RequiredFromJSONSchemaTags: true, + } + + for _, t := range targets { + schema := r.Reflect(t.instance) + data, err := json.MarshalIndent(schema, "", " ") + if err != nil { + fmt.Fprintf(os.Stderr, "error marshaling schema for %s: %v\n", t.name, err) + os.Exit(1) + } + + path := filepath.Join(*outDir, t.name+".json") + if err := os.WriteFile(path, append(data, '\n'), 0o644); err != nil { + fmt.Fprintf(os.Stderr, "error writing %s: %v\n", path, err) + os.Exit(1) + } + fmt.Printf("generated %s\n", path) + } +}