diff --git a/.editorconfig b/.editorconfig index f8cf1cc..aa0ee78 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,7 +13,7 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true -[{*.go,*.cue,*.sh}] +[{*.go,*.cue,*.sh,go.mod}] indent_style = tab [*.bazel] diff --git a/MODULE.bazel b/MODULE.bazel index 4ead59e..990ab5b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,6 +5,7 @@ module( name = "cuelumi", version = "0.0.0", + repo_name = "com_gitlab_folliehiyuki_cuelumi", ) # TODO: enable when `rules_nixpkgs_go` is published and stablized (https://github.com/tweag/rules_nixpkgs/issues/181) @@ -24,11 +25,16 @@ bazel_dep(name = "rules_go", version = "0.43.0") go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") go_sdk.host() -bazel_dep(name = "gazelle", version = "0.34.0") +bazel_dep(name = "gazelle", version = "0.34.0", dev_dependency = True) go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") -use_repo(go_deps) +use_repo( + go_deps, + "com_github_goccy_go_json", + "com_github_goccy_go_yaml", + "com_github_pelletier_go_toml_v2", +) # Expose the generated CUE files as Bazel inputs, so other modules can use them bazel_dep(name = "rules_cue", version = "0.4.2") diff --git a/README.adoc b/README.adoc index a1c9455..4ed2c1c 100644 --- a/README.adoc +++ b/README.adoc @@ -11,7 +11,7 @@ See link:./examples[examples/] directory for some use cases. == Tools * **cue-pulumi-gen**: a program to generate CUE definitions for Pulumi providers' JSON schemas inside link:./schemata/providers[schemata/providers/] directory -* **pulumi-schema**: a simple command to conveniently print out `+schema.json+` file, given a Pulumi proivder's name +* **pulumi-schema**: a simple command to conveniently print out `+schema.json+` file, given a Pulumi provider's name == Schema packages diff --git a/examples/time/main.cue b/examples/time/main.cue index 9629b72..41c43be 100644 --- a/examples/time/main.cue +++ b/examples/time/main.cue @@ -8,8 +8,22 @@ import ( "gitlab.com/FollieHiyuki/cuelumi/schemata/pulumi" ) -name: pulumi.#Name & "cuelumi-examples" +pulumi.#PulumiYAML & { + name: "cuelumi-examples" + runtime: "yaml" + description: "Example Pulumi project using CUE" -runtime: pulumi.#Runtime & "yaml" + resources: { + time: { + type: "pulumi:providers:time" + options: version: "v0.0.16" + } + rotation: { + type: "time:Rotating" + properties: rotationDays: 30 + options: provider: "${time}" + } + } -description: pulumi.#Description & "Example Pulumi project using CUE" + outputs: date: "${rotation.hour}" +} diff --git a/flake.lock b/flake.lock index 40bc2b9..4489eb3 100644 --- a/flake.lock +++ b/flake.lock @@ -36,11 +36,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1701718080, - "narHash": "sha256-6ovz0pG76dE0P170pmmZex1wWcQoeiomUZGggfH9XPs=", + "lastModified": 1702151865, + "narHash": "sha256-9VAt19t6yQa7pHZLDbil/QctAgVsA66DLnzdRGqDisg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2c7f3c0fb7c08a0814627611d9d7d45ab6d75335", + "rev": "666fc80e7b2afb570462423cb0e1cf1a3a34fedd", "type": "github" }, "original": { @@ -80,11 +80,11 @@ ] }, "locked": { - "lastModified": 1701958734, - "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", + "lastModified": 1702281974, + "narHash": "sha256-OX6umqmLlRKKX0yEfQBmMx8pDNHtxp+sGTLyFh8kLG8=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "e8cea581dd2b7c9998c1e6662db2c1dc30e7fdb0", + "rev": "5ff2cdbe0db6a6f3445f7d878cb87d121d914d83", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index bdd29d4..3e67404 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,7 @@ apps = { # Wrap bazel with `nix run .#bazel`, for convenience - inherit (pkgs) bazel; + inherit (pkgs) bazel cue; # Run `go mod tidy` then update BUILD.bazel files gazelle = @@ -73,7 +73,16 @@ --style=c \ "$OUTFILE" ''; - }; + } // (lib.listToAttrs ( + map + (x: { + name = x; + value = with pkgs; writeShellScriptBin x '' + ${bazel-buildtools}/bin/${x} "$@" + ''; + }) + [ "buildifier" "buildozer" ] + )); in { apps = lib.mapAttrs diff --git a/go.mod b/go.mod index 67aafd7..60fcb01 100644 --- a/go.mod +++ b/go.mod @@ -5,3 +5,17 @@ module gitlab.com/FollieHiyuki/cuelumi go 1.21.5 + +require ( + github.com/goccy/go-json v0.10.2 + github.com/goccy/go-yaml v1.11.2 + github.com/pelletier/go-toml/v2 v2.1.0 +) + +require ( + github.com/fatih/color v1.10.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..be32d50 --- /dev/null +++ b/go.sum @@ -0,0 +1,46 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ= +github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.sum.license b/go.sum.license new file mode 100644 index 0000000..b157bf8 --- /dev/null +++ b/go.sum.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Hoang Nguyen + +SPDX-License-Identifier: CC0-1.0 diff --git a/internal/schema/BUILD.bazel b/internal/schema/BUILD.bazel index 3dfeb34..aa6a144 100644 --- a/internal/schema/BUILD.bazel +++ b/internal/schema/BUILD.bazel @@ -6,7 +6,15 @@ load("@rules_go//go:def.bzl", "go_library") go_library( name = "schema", - srcs = ["metadata.go"], + srcs = [ + "metadata.go", + "schema.go", + ], importpath = "gitlab.com/FollieHiyuki/cuelumi/internal/schema", visibility = ["//:__subpackages__"], + deps = [ + "@com_github_goccy_go_json//:go-json", + "@com_github_goccy_go_yaml//:go-yaml", + "@com_github_pelletier_go_toml_v2//:go-toml", + ], ) diff --git a/internal/schema/metadata.go b/internal/schema/metadata.go index b794261..5cf330f 100644 --- a/internal/schema/metadata.go +++ b/internal/schema/metadata.go @@ -5,3 +5,16 @@ */ package schema + +type PkgInfo struct { + Type string `toml:"type"` + Version string `toml:"version"` + RepoSlug string `toml:"repo_slug"` + SchemaFilePath string `toml:"schema_file_path"` + GitURL string `toml:"git_url,omitempty"` +} + +const ( + githubURL = "https://github.com" + gitlabURL = "https://gitlab.com" +) diff --git a/internal/schema/providers.toml b/internal/schema/providers.toml index 2df4663..79d8b17 100644 --- a/internal/schema/providers.toml +++ b/internal/schema/providers.toml @@ -2,22 +2,22 @@ # # SPDX-License-Identifier: Apache-2.0 -# For the list of existing Pulumi proivders, see https://www.pulumi.com/registry/ +# For the list of existing Pulumi providers, see https://www.pulumi.com/registry/ [aci] type = "github" version = "v0.0.6" -repository = "https://github.com/netascode/pulumi-aci" -schema_path = "provider/cmd/pulumi-resource-aci/schema.json" +repo_slug = "netascode/pulumi-aci" +schema_file_path = "provider/cmd/pulumi-resource-aci/schema.json" [acme] type = "github" version = "v0.0.1" -repository = "https://github.com/pulumiverse/pulumi-acme" -schema_path = "provider/cmd/pulumi-resource-acme/schema.json" +repo_slug = "pulumiverse/pulumi-acme" +schema_file_path = "provider/cmd/pulumi-resource-acme/schema.json" [aiven] type = "github" version = "v6.7.2" -repository = "https://github.com/pulumi/pulumi-aiven" -schema_path = "provider/cmd/pulumi-resource-aiven/schema.json" +repo_slug = "pulumi/pulumi-aiven" +schema_file_path = "provider/cmd/pulumi-resource-aiven/schema.json" diff --git a/internal/schema/schema.go b/internal/schema/schema.go new file mode 100644 index 0000000..43f113a --- /dev/null +++ b/internal/schema/schema.go @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 Hoang Nguyen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package schema + +import ( + "github.com/goccy/go-json" + "github.com/goccy/go-yaml" + "github.com/pelletier/go-toml/v2" +) diff --git a/schemata/pulumi/pulumi.cue b/schemata/pulumi/pulumi.cue index 36d93c8..39ce8a8 100644 --- a/schemata/pulumi/pulumi.cue +++ b/schemata/pulumi/pulumi.cue @@ -4,8 +4,168 @@ package pulumi -#Name: string +#PulumiYAML: { + // ============================== + // Base Pulumi.yaml configuration + // Ref: https://www.pulumi.com/docs/concepts/projects/project-file/ + // ============================== -#Runtime: string + name: string -#Description: string + // A naive file path regular expression + // NOTE: + // * don't allow whitespaces or weird characters + // * also no root directory / + _pathRegex: =~#"^([\w\.-]+)?(/[\w\.-]+)+/?$"# + + _#RuntimeOpts: + { + name: "yaml" + options: compiler: string + } | + { + name: "python" + options: virtualenv: _pathRegex + } | + { + name: "nodejs" + options: { + typescript?: bool + nodeargs: string + } + } | + { + name: "go" + options: buildTarget: _pathRegex + } | + { + name: "dotnet" | "java" | "go" + options: binary: _pathRegex + } + + runtime: _#RuntimeOpts | "go" | "python" | "nodejs" | "dotnet" | "java" | *"yaml" + + description?: string + + // Basic types that Pulumi accepts + _#Type: string | int | bool | [...] + + _#Value: { + value: _#Type | {...} + } + + // This is only valid for project property keys + _#Schema: { + type: "string" | "boolean" | "integer" | "array" + description?: string + secret?: bool + default?: _#Type + items?: type: "string" | "boolean" | "integer" + } + + config?: [string]: _#Type | _#Schema | _#Value + + main?: _pathRegex + + // Requires a relative path to Pulumi.yaml, so we can't reuse _pathRegex here + stackConfigDir?: =~#"^([\w\.-]+/)+$"# + + backend?: url: =~#"^(https?|s3|azblob|gs|file)://.+$"# + + options?: refresh: "always" + + template?: { + description?: string + config: { + description?: string + default?: _#Type + secret?: bool + } + } + + plugins?: ["providers" | "analyzers" | "languages"]: [{ + name: string + path?: _pathRegex + version?: string + }] + + // ============================== + // `pulumi-yaml` specific keys + // Ref: https://www.pulumi.com/docs/languages-sdks/yaml/yaml-language-reference/ + // ============================== + + _#Resource: { + type: string + defaultProvider?: bool + properties?: [string]: _#Expression + options?: { + additionalSecretOutputs?: [...string] + aliases?: [...string] + customTimeouts?: { + create?: string + delete?: string + update?: string + } + deleteBeforeReplace?: bool + dependsOn?: [..._#Interpolation] + ignoreChanges?: [...string] + import?: string + parent?: _#Interpolation + protect?: bool + provider?: _#Interpolation + providers?: [string]: _#Interpolation + version?: string + pluginDownloadURL?: =~#"^https?://.+$"# + replaceOnChanges?: [...string] + retainOnDelete?: bool + } + get?: { + id: string + state?: [string]: _#Expression + } + } + + resources?: [string]: _#Resource + + _#BuiltinFn: + {"fn::toBase64": string} | + {"fn::fromBase64": =~#"^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"#} | + {"fn::toJSON": _#NonFnMap} | + { + "fn::invoke": { + function: string + arguments: _#NonFnMap + options?: { + parent?: _#Interpolation + provider?: _#Interpolation + version?: string + pluginDownloadURL?: =~#"^https?://.+$"# + } + return?: string + } + } | + {"fn::join": [string, [...string]]} | + {"fn::split": [string, string]} | + {"fn::select": [int, [...]]} | + {"fn::assetArchive": [string]: _#AssetArchiveFn} | + _#AssetArchiveFn | + {"fn::readFile": =~#"^([\w\.\$\{\}-]+)?(/[\w\.\$\{\}-]+)*$"#} + + _#AssetArchiveFn: + {"fn::stringAsset": string} | + {["fn::fileAsset" | "fn::fileArchive"]: _pathRegex} | + {["fn::remoteAsset" | "fn::remoteArchive"]: =~#"^(https?|file)://.*$"#} + + // fn::secret is separated to avoid recursion + _#SecretFn: {"fn::secret": string | _#BuiltinFn} + + _#Interpolation: =~#"^\$\{[A-Za-z0-9]+\}$"# + + _#NonFnMap: [!~#"^fn::[a-zA-Z0-9]+$"#]: _#Type | {...} + + _#Expression: _#Type | _#NonFnMap | _#BuiltinFn | _#SecretFn + + variables?: [string]: _#Expression + + outputs?: [string]: _#Expression +} diff --git a/treefmt.nix b/treefmt.nix index 171c07d..3426356 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -5,7 +5,7 @@ _: { projectRootFile = "flake.lock"; - programs = builtins.listToAttrs ( + programs = (builtins.listToAttrs ( builtins.map (x: { name = x; @@ -18,5 +18,7 @@ _: { "nixpkgs-fmt" "statix" ] - ); + )) // { + gofumpt.extra = true; + }; }