diff --git a/src/std/templates/microservice-aws-github/flake.nix b/src/std/templates/microservice-aws-github/flake.nix new file mode 100644 index 00000000..2bba75e7 --- /dev/null +++ b/src/std/templates/microservice-aws-github/flake.nix @@ -0,0 +1,32 @@ +{ + outputs = {std, ...} @ inputs: + std.growOn { + inherit inputs; + cellsFrom = ./ops; + cellBlocks = with std.blockTypes; [ + # Software Delivery Lifecycle (Packaging Layers) + # For deeper context, please consult: + # https://std.divnix.com/patterns/four-packaging-layers.html + (installables "packages" {ci.build = true;}) + (runnables "operables") + (containers "oci-images" {ci.publish = true;}) + (kubectl "deployments" {ci.apply = true;}) + # For rendering the Github Action CI/CD + (nixago "action") + ]; + }; + + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + inputs = { + std.url = "github:divnix/std"; + std.inputs.nixpkgs.follows = "nixpkgs"; + std.inputs.n2c.follows = "n2c"; + std.inputs.nixago.follows = "nixago"; + n2c.url = "github:nlewo/nix2container"; + n2c.inputs.nixpkgs.follows = "nixpkgs"; + nixago.url = "github:nix-community/nixago"; + nixago.inputs.nixpkgs.follows = "nixpkgs"; + nixago.inputs.nixago-exts.follows = ""; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/backend/deployments.nix b/src/std/templates/microservice-aws-github/ops/backend/deployments.nix new file mode 100644 index 00000000..20b7f8ae --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/deployments.nix @@ -0,0 +1,24 @@ +let + inherit (inputs.std) dmerge; + inherit (inputs.std.inputs) haumea; + inherit (inputs.std.lib.ops) readYAML; + + loadYaml = _: _: readYAML; + baseline = with haumea.lib; + load { + src = ./deployments; + loader = [(matchers.regex ''^.+\.(yaml|yml)'' loadYaml)]; + }; +in { + my-srv-a = dmerge baseline.my-srv-a { + meta.description = "Development Environment for my-srv-a"; + deployment = { + spec.template.spec.containers = dmerge.updateOn "name" [ + { + name = "my-srv-a"; + image = cell.oci-images.my-srv-a.image.name; + } + ]; + }; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/deployment.yaml b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/deployment.yaml new file mode 100644 index 00000000..49d9df01 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/deployment.yaml @@ -0,0 +1,2 @@ +apiVersion: apps/v1 +kind: Deployment diff --git a/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/ingress.yaml b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/ingress.yaml new file mode 100644 index 00000000..787ad0ae --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/ingress.yaml @@ -0,0 +1,2 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress diff --git a/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/service.yaml b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/service.yaml new file mode 100644 index 00000000..99e25efc --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/deployments/my-srv-a/service.yaml @@ -0,0 +1,2 @@ +apiVersion: v1 +kind: Service diff --git a/src/std/templates/microservice-aws-github/ops/backend/oci-images.nix b/src/std/templates/microservice-aws-github/ops/backend/oci-images.nix new file mode 100644 index 00000000..777c84f6 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/oci-images.nix @@ -0,0 +1,9 @@ +let + inherit (inputs) std; +in { + my-srv-a = std.lib.ops.mkStandardOCI { + name = "ghcr.io/myorg/myrepo/my-srv-a"; + operable = cell.operables.my-srv-a; + meta.description = "Minimal OCI Image for my-srv-a"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/backend/operables.nix b/src/std/templates/microservice-aws-github/ops/backend/operables.nix new file mode 100644 index 00000000..e5eafd4f --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/operables.nix @@ -0,0 +1,10 @@ +let + inherit (inputs) std; + inherit (inputs.nixpkgs) lib; +in { + my-srv-a = std.lib.ops.mkOperable { + package = cell.packages.my-srv-a; + runtimeScript = lib.getExe cell.packages.my-srv-a; + meta.description = "A thin warpper around my-srv-a binary executable"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/backend/packages.nix b/src/std/templates/microservice-aws-github/ops/backend/packages.nix new file mode 100644 index 00000000..c9d9cb52 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/backend/packages.nix @@ -0,0 +1,12 @@ +let + inherit (inputs) nixpkgs std self; + + src = std.incl self [ + "folder" + "my.file" + ]; +in { + my-srv-a = { + meta.description = "my-srv-a binary executable"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/frontend/deployments.nix b/src/std/templates/microservice-aws-github/ops/frontend/deployments.nix new file mode 100644 index 00000000..20b7f8ae --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/deployments.nix @@ -0,0 +1,24 @@ +let + inherit (inputs.std) dmerge; + inherit (inputs.std.inputs) haumea; + inherit (inputs.std.lib.ops) readYAML; + + loadYaml = _: _: readYAML; + baseline = with haumea.lib; + load { + src = ./deployments; + loader = [(matchers.regex ''^.+\.(yaml|yml)'' loadYaml)]; + }; +in { + my-srv-a = dmerge baseline.my-srv-a { + meta.description = "Development Environment for my-srv-a"; + deployment = { + spec.template.spec.containers = dmerge.updateOn "name" [ + { + name = "my-srv-a"; + image = cell.oci-images.my-srv-a.image.name; + } + ]; + }; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/deployment.yaml b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/deployment.yaml new file mode 100644 index 00000000..49d9df01 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/deployment.yaml @@ -0,0 +1,2 @@ +apiVersion: apps/v1 +kind: Deployment diff --git a/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/ingress.yaml b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/ingress.yaml new file mode 100644 index 00000000..787ad0ae --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/ingress.yaml @@ -0,0 +1,2 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress diff --git a/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/service.yaml b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/service.yaml new file mode 100644 index 00000000..99e25efc --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/deployments/my-srv-a/service.yaml @@ -0,0 +1,2 @@ +apiVersion: v1 +kind: Service diff --git a/src/std/templates/microservice-aws-github/ops/frontend/oci-images.nix b/src/std/templates/microservice-aws-github/ops/frontend/oci-images.nix new file mode 100644 index 00000000..777c84f6 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/oci-images.nix @@ -0,0 +1,9 @@ +let + inherit (inputs) std; +in { + my-srv-a = std.lib.ops.mkStandardOCI { + name = "ghcr.io/myorg/myrepo/my-srv-a"; + operable = cell.operables.my-srv-a; + meta.description = "Minimal OCI Image for my-srv-a"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/frontend/operables.nix b/src/std/templates/microservice-aws-github/ops/frontend/operables.nix new file mode 100644 index 00000000..e5eafd4f --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/operables.nix @@ -0,0 +1,10 @@ +let + inherit (inputs) std; + inherit (inputs.nixpkgs) lib; +in { + my-srv-a = std.lib.ops.mkOperable { + package = cell.packages.my-srv-a; + runtimeScript = lib.getExe cell.packages.my-srv-a; + meta.description = "A thin warpper around my-srv-a binary executable"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/frontend/packages.nix b/src/std/templates/microservice-aws-github/ops/frontend/packages.nix new file mode 100644 index 00000000..c9d9cb52 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/frontend/packages.nix @@ -0,0 +1,12 @@ +let + inherit (inputs) nixpkgs std self; + + src = std.incl self [ + "folder" + "my.file" + ]; +in { + my-srv-a = { + meta.description = "my-srv-a binary executable"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/github/action.nix b/src/std/templates/microservice-aws-github/ops/github/action.nix new file mode 100644 index 00000000..451aa0fe --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/github/action.nix @@ -0,0 +1,21 @@ +let + inherit (inputs.nixpkgs) lib; + inherit (inputs.std.lib) dev; + + template = (import ./action/template.nix) lib; +in { + inherit template; + ci = dev.mkNixago { + output = ".github/workflows/ci-cd.yaml"; + format = "yaml"; + hook.mode = "copy"; + data = template { + default_branch = "main"; + platform = "aws"; # gc, azure, digitalocean + # set up with nixbuild.net to speed up builds + withNixbuild = false; + # use with persistent discovery; needs to be setup separately + withPersistentDiscovery = false; + }; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/github/action/aws.nix b/src/std/templates/microservice-aws-github/ops/github/action/aws.nix new file mode 100644 index 00000000..08d7e0a5 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/github/action/aws.nix @@ -0,0 +1,14 @@ +{ + credentials = { + name = "Configure AWS Credentials"; + uses = "aws-actions/configure-aws-credentials@main"; + "with" = { + "role-to-assume" = "\${{ var.AWS_ROLE_ARN }}"; + "aws-region" = "\${{ var.AWS_REGION }}"; + }; + }; + ecr = { + name = "Login to Amazon ECR"; + uses = "aws-actions/amazon-ecr-login@v1"; + }; +} diff --git a/src/std/templates/microservice-aws-github/ops/github/action/template.nix b/src/std/templates/microservice-aws-github/ops/github/action/template.nix new file mode 100644 index 00000000..b5f93545 --- /dev/null +++ b/src/std/templates/microservice-aws-github/ops/github/action/template.nix @@ -0,0 +1,89 @@ +lib: { + default_branch, + platform, + withNixbuild, + withPersistentDiscovery, +}: let + aws = import ./aws.nix; + installNixAction = {uses = "blaggacao/nix-quick-install-action@detect-nix-flakes-config";}; + useNixbuildAction = { + uses = "nixbuild/nixbuild-action@v17"; + "with" = { + nixbuild_ssh_key = "\${{ secrets.SSH_KEY }}"; + generate_summary_for = "job"; + }; + }; + discoverAction = { + uses = "divnix/std-action/discover@main"; + id = "discovery"; + }; + runAction = {uses = "divnix/std-action/run@main";}; + # Jobs + discover = { + outputs.hits = "\${{ steps.discovery.outputs.hits }}"; + runs-on = "ubuntu-latest"; + steps = + [] + # account is part of ecr url, thus part of `hits` output and needs to pass so we can't mask it + ++ lib.optionals (platform == "aws") [(lib.recursiveUpdate aws.credentials {mask-aws-account-id = false;})] + ++ lib.optionals (platform == "aws") [aws.ecr] + ++ lib.optionals (!withPersistentDiscovery) [installNixAction] + ++ lib.optionals withNixbuild [useNixbuildAction] + ++ [discoverAction]; + }; + worker = { + block, + action, + needs ? [], + steps ? [], + }: { + needs = ["discover"] ++ needs; + name = "\${{ matrix.target.jobName }}"; + "if" = "fromJSON(needs.discover.outputs.hits).${block}.${action} != '{}'"; + strategy = { + fail-fast = false; + matrix.target = "\${{ fromJSON(needs.discover.outputs.hits).${block}.${action} }}"; + }; + steps = + [] + ++ [installNixAction] + ++ lib.optionals withNixbuild [useNixbuildAction] + ++ [runAction]; + }; +in { + name = "CI/CD"; + on = { + pull_request.branches = [default_branch]; + push.branches = [default_branch]; + }; + permissions = { + id-token = "write"; + contents = "read"; + }; + concurrency = { + group = ''std-''${{ github.workflow }}-''${{ runner.os }}-''${{ github.ref }}''; + "cancel-in-progress" = true; + }; + jobs = { + inherit discover; + build = worker { + block = "packages"; + action = "build"; + }; + images = worker { + block = "images"; + action = "publish"; + needs = ["build"]; + steps = + lib.optionals (platform == "aws") [aws.credentials] + lib.optionals (platform == "aws") [aws.ecr]; + }; + deploy = worker { + block = "deployments"; + action = "apply"; + needs = ["images"]; + steps = + lib.optionals (platform == "aws") [aws.credentials]; + }; + }; +}