diff --git a/README.adoc b/README.adoc index f487132..703ba08 100644 --- a/README.adoc +++ b/README.adoc @@ -11,3 +11,5 @@ Monorepo managing all of my cloud and home lab resources. === Structure This repository is small, and each Pulumi project is a one-stack directory. Therefore, all Pulumi projects use the same project name, while their stack names are chosen to be the parent directory name. The `+Pulumi.yaml+` file at repository's root is reused for all Pulumi projects inside. + +Except the toplevel `+package.json+` file, all `+rescript.json+` and `+package.json+` files are generated using CUE. diff --git a/flake.nix b/flake.nix index f714820..8216ea9 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,7 @@ }; }; - outputs = { self, nixpkgs, flake-utils, treefmt-nix, ... }: + outputs = { nixpkgs, flake-utils, treefmt-nix, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages."${system}"; @@ -38,8 +38,14 @@ ]; in { + apps.gen-project-conf = flake-utils.lib.mkApp { + drv = pkgs.writeShellScriptBin "gen-project-conf" '' + cd ./tools/generate + ${pkgs.cue}/bin/cue cmd gen-project-conf + ''; + }; + checks = with pkgs; { - treefmt = treefmtEval.config.build.check self; ansible-lint = writeShellScriptBin "ansible-lint" '' ${ansible-lint}/bin/ansible-lint ./playbooks/ ''; diff --git a/nx.json b/nx.json index 6d442b4..fe8488c 100644 --- a/nx.json +++ b/nx.json @@ -24,10 +24,7 @@ "commands": ["rescript build"], "cwd": "{projectRoot}" }, - "inputs": [ - "{projectRoot}/src/**/*.res", - "{projectRoot}/src/**/*.resi" - ], + "inputs": ["{projectRoot}/src/**/*.res", "{projectRoot}/src/**/*.resi"], "outputs": [ "{projectRoot}/src/**/*.res.js", "{projectRoot}/src/**/*.gen.tsx" diff --git a/packages/bindings/package.json b/packages/bindings/package.json index d71c270..35bc7cf 100644 --- a/packages/bindings/package.json +++ b/packages/bindings/package.json @@ -1,16 +1,16 @@ { - "name": "bindings", - "type": "module", "devDependencies": { "@rescript/core": "^1.1.0", "rescript": "^11.0.1" }, + "name": "bindings", "nx": { "targets": { "res:build": {}, "res:clean": {}, - "res:format": {}, - "res:dev": {} + "res:dev": {}, + "res:format": {} } - } + }, + "type": "module" } diff --git a/packages/bindings/rescript.json b/packages/bindings/rescript.json index 073b141..7935918 100644 --- a/packages/bindings/rescript.json +++ b/packages/bindings/rescript.json @@ -1,13 +1,7 @@ { "name": "bindings", "namespace": true, - "sources": { - "dir": "src", - "subdirs": true - }, - "package-specs": { - "module": "es6-global", - "in-source": true - }, + "sources": { "dir": "src", "subdirs": true }, + "package-specs": { "module": "es6-global", "in-source": true }, "suffix": ".res.js" } diff --git a/stacks/cloudflare/package.json b/stacks/cloudflare/package.json index 471a45d..a38ced5 100644 --- a/stacks/cloudflare/package.json +++ b/stacks/cloudflare/package.json @@ -1,14 +1,14 @@ { - "name": "cloudflare", - "main": "src/Main.res.js", - "type": "module", - "private": true, "devDependencies": { "@rescript/core": "^1.1.0", "rescript": "^11.0.1" }, + "main": "src/Main.res.js", + "name": "cloudflare", "nx": { - "implicitDependencies": ["bindings"], + "implicitDependencies": [ + "bindings" + ], "targets": { "preview": { "options": { @@ -20,15 +20,17 @@ "stack": "cloudflare" } }, + "res:build": {}, + "res:clean": {}, + "res:dev": {}, + "res:format": {}, "up": { "options": { "stack": "cloudflare" } - }, - "res:build": {}, - "res:clean": {}, - "res:format": {}, - "res:dev": {} + } } - } + }, + "private": true, + "type": "module" } diff --git a/stacks/cloudflare/rescript.json b/stacks/cloudflare/rescript.json index 67ae408..340c54c 100644 --- a/stacks/cloudflare/rescript.json +++ b/stacks/cloudflare/rescript.json @@ -1,14 +1,8 @@ { "name": "cloudflare", - "namespace": true, - "sources": { - "dir": "src", - "subdirs": true - }, - "package-specs": { - "module": "es6-global", - "in-source": true - }, "bs-dependencies": ["bindings"], + "namespace": true, + "sources": { "dir": "src", "subdirs": true }, + "package-specs": { "module": "es6-global", "in-source": true }, "suffix": ".res.js" } diff --git a/tools/generate/base.cue b/tools/generate/base.cue new file mode 100644 index 0000000..c3efaf9 --- /dev/null +++ b/tools/generate/base.cue @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Hoang Nguyen +// +// SPDX-License-Identifier: Apache-2.0 + +package generate + +#Workspace: { + name: string + path: string + pulumi: bool | *false + + // Additional package.json and rescript.json config + package: #Package + rescript: #ReScript +} + +workspaces: [...#Workspace] & [ + { + name: "bindings" + path: "packages/bindings" + }, + { + name: "cloudflare" + path: "stacks/cloudflare" + pulumi: true + }, +] diff --git a/tools/generate/gen_tool.cue b/tools/generate/gen_tool.cue new file mode 100644 index 0000000..3f71a26 --- /dev/null +++ b/tools/generate/gen_tool.cue @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2024 Hoang Nguyen +// +// SPDX-License-Identifier: Apache-2.0 + +package generate + +import ( + "encoding/json" + "strings" + "tool/exec" + "tool/file" +) + +command: "gen-project-conf": { + gitRoot: exec.Run & { + cmd: ["git", "rev-parse", "--show-toplevel"] + stdout: string + path: strings.TrimSpace(stdout) + } + + for _, ws in workspaces { + "\(ws.name)": { + // Ensure the below files can be written + dir: file.MkdirAll & { + path: "\(gitRoot.path)/\(ws.path)" + } + + // Create package.json file + package: file.Create & { + filename: "\(dir.path)/package.json" + permissions: 0o644 + contents: json.Marshal(ws.package & { + name: ws.name + _pulumi: ws.pulumi + }) + } + + // Create rescript.json file + rescript: file.Create & { + filename: "\(dir.path)/rescript.json" + permissions: 0o644 + contents: json.Marshal(ws.rescript & { + name: ws.name + _pulumi: ws.pulumi + }) + } + + // Format the created files with `biome`. + // (biome >=1.6.0 is required to format package.json files) + // NOTE: we can also just plug json.Indent() into the above structs, but let's be consistent. + format: exec.Run & { + cmd: [ + "biome", + "format", + "--write", + "\(ws.path)/package.json", + "\(ws.path)/rescript.json", + ] + dir: gitRoot.path + } + } + } +} diff --git a/tools/generate/package.cue b/tools/generate/package.cue new file mode 100644 index 0000000..7e1dca0 --- /dev/null +++ b/tools/generate/package.cue @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2024 Hoang Nguyen +// +// SPDX-License-Identifier: Apache-2.0 + +package generate + +#Package: { + // Required inputs + _pulumi: bool + name: string + + // Enforce ES6 style + type: "module" + + // Use the same version of ReScript everywhere + devDependencies: { + "@rescript/core": "^1.1.0" + rescript: "^11.0.1" + } + + // Make ReScript tasks available everywhere + nx: targets: { + "res:build": {} + "res:clean": {} + "res:dev": {} + "res:format": {} + } + + // All Pulumi projects here should work the same + if _pulumi { + main: "src/Main.res.js" + private: true + + nx: { + implicitDependencies: ["bindings", ...] + targets: { + preview: options: stack: name + refresh: options: stack: name + up: options: stack: name + } + } + } + + // Allow further customizations + ... +} diff --git a/tools/generate/rescript.cue b/tools/generate/rescript.cue new file mode 100644 index 0000000..b5c5354 --- /dev/null +++ b/tools/generate/rescript.cue @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 Hoang Nguyen +// +// SPDX-License-Identifier: Apache-2.0 + +package generate + +#ReScript: { + // Required inputs + name: string + _pulumi: bool + + // Enforced configurations + namespace: true + sources: { + dir: "src" + subdirs: true + } + "package-specs": { + module: "es6-global" + "in-source": true + } + suffix: ".res.js" + + if _pulumi { + "bs-dependencies": ["bindings", ...] + } + + // Allow further customizations + ... +} diff --git a/treefmt.nix b/treefmt.nix index 75e160e..366db89 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -22,7 +22,7 @@ value.enable = true; }) [ - # "cue" + "cue" "biome" "nixpkgs-fmt" "shellcheck"