diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7c5f3f95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/result +/result-* diff --git a/README.md b/README.md index d7939ec7..28266134 100644 --- a/README.md +++ b/README.md @@ -16,58 +16,62 @@ For a version compatible with a previous version, check out [this commit](https: ## Quick start -In the `types` folder you'll find the types for the Kubernetes definitions. E.g. [here's][Deployment] the type for a Deployment. +In the `types` folder you'll find the types for the Kubernetes definitions. E.g. +[here's][Deployment] the type for a Deployment. -Since _most_ of the fields in all definitions are optional, for better ergonomics while coding Dhall we also generate default values for all types, in the `default` folder. -When some fields are required, the default value is a function whose input is a record of required fields, that returns the object with these fields set. E.g. the default for the Deployment is [this function][Deployment-default]. +Since _most_ of the fields in all definitions are optional, for better +ergonomics while coding Dhall we also generate default values for all types, in +the `default` folder. When some fields are required, the default value is a +function whose input is a record of required fields, that returns the object +with these fields set. E.g. the default for the Deployment is [this +function][Deployment-default]. -Since this might sound a bit abstract, let's go with some examples. +Since this might sound a bit abstract, let's go with some examples. You can find +these examples in the [`./examples` folder](./examples) and evaluate them there. ### Example: Deployment Let's say we have several services, whose configuration has this type: ```haskell --- Service.dhall +-- examples/Service.dhall { name : Text , host : Text , version : Text } + ``` So a configuration for a service might look like this: ```haskell --- service-foo.dhall +-- examples/service-foo.dhall { name = "foo" , host = "foo.example.com" , version = "1.0.1" } + ``` We can then make a Deployment object for this service: ```haskell --- deployment.yaml.dhall - +-- examples/deployment.dhall -- Prelude imports let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None -- import dhall-kubernetes types and defaults -in let Deployment = ./types/io.k8s.api.apps.v1beta2.Deployment.dhall -in let Spec = ./types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall -in let PodSpec = ./types/io.k8s.api.core.v1.PodSpec.dhall -in let ContainerPort = ./types/io.k8s.api.core.v1.ContainerPort.dhall -in let defaultDeployment = ./default/io.k8s.api.apps.v1beta2.Deployment.dhall -in let defaultMeta = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall -in let defaultSpec = ./default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall -in let defaultTemplate = ./default/io.k8s.api.core.v1.PodTemplateSpec.dhall -in let defaultPodSpec = ./default/io.k8s.api.core.v1.PodSpec.dhall -in let defaultSelector = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall -in let defaultContainer = ./default/io.k8s.api.core.v1.Container.dhall -in let defaultContainerPort = ./default/io.k8s.api.core.v1.ContainerPort.dhall - --- Our Service type -in let Service = ./Service.dhall +in let Deployment = ../types/io.k8s.api.apps.v1beta2.Deployment.dhall +in let Spec = ../types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall +in let PodSpec = ../types/io.k8s.api.core.v1.PodSpec.dhall +in let ContainerPort = ../types/io.k8s.api.core.v1.ContainerPort.dhall +in let defaultDeployment = ../default/io.k8s.api.apps.v1beta2.Deployment.dhall +in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall +in let defaultSpec = ../default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall +in let defaultTemplate = ../default/io.k8s.api.core.v1.PodTemplateSpec.dhall +in let defaultPodSpec = ../default/io.k8s.api.core.v1.PodSpec.dhall +in let defaultSelector = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall +in let defaultContainer = ../default/io.k8s.api.core.v1.Container.dhall +in let defaultContainerPort = ../default/io.k8s.api.core.v1.ContainerPort.dhall -- and our service in let fooService = ./service-foo.dhall @@ -85,15 +89,15 @@ in let spec = defaultSpec } // { spec = Some PodSpec (defaultPodSpec { containers = [ - defaultContainer - { name = fooService.name } // - { image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}" - , imagePullPolicy = Some Text "Always" - , ports = Some - (List ContainerPort) - [(defaultContainerPort {containerPort = 8080})] - } - ]}) + defaultContainer + { name = fooService.name } // + { image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}" + , imagePullPolicy = Some Text "Always" + , ports = Some + (List ContainerPort) + [(defaultContainerPort {containerPort = 8080})] + } + ]}) } } // { replicas = Some Natural 2 @@ -111,12 +115,13 @@ in defaultDeployment We convert it to yaml with: ```bash -dhall-to-yaml --omitNull < deployment.yaml.dhall +dhall-to-yaml --omitNull < deployment.dhall ``` And we get: ```yaml -apiVersion: extensions/v1beta1 +-- examples/out/deployment.yaml +apiVersion: apps/v1beta2 kind: Deployment spec: revisionHistoryLimit: 10 @@ -138,29 +143,31 @@ spec: replicas: 2 metadata: name: foo + ``` ### Example: Ingress -Let's say we now want to generate an Ingress definition (for an Nginx Ingress) that contains TLS certs and routes for every service. It would be something like this: +Let's say we now want to generate an Ingress definition (for an Nginx Ingress) +that contains TLS certs and routes for every service. It would be something like +this: ```haskell --- ingress.yaml.dhall - +-- examples/ingress.dhall -- Prelude imports let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None -- dhall-kubernetes types and defaults -in let TLS = ./types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall -in let Rule = ./types/io.k8s.api.extensions.v1beta1.IngressRule.dhall -in let RuleVal = ./types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall -in let Spec = ./types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall -in let Ingress = ./types/io.k8s.api.extensions.v1beta1.Ingress.dhall -in let defaultIngress = ./default/io.k8s.api.extensions.v1beta1.Ingress.dhall -in let defaultMeta = ./default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall -in let defaultSpec = ./default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall +in let TLS = ../types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall +in let Rule = ../types/io.k8s.api.extensions.v1beta1.IngressRule.dhall +in let RuleVal = ../types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall +in let Spec = ../types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall +in let Ingress = ../types/io.k8s.api.extensions.v1beta1.Ingress.dhall +in let defaultIngress = ../default/io.k8s.api.extensions.v1beta1.Ingress.dhall +in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall +in let defaultSpec = ../default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall -- Our Service type in let Service = ./Service.dhall @@ -179,10 +186,14 @@ in let makeRule = \(service : Service) -> , servicePort = "80" } , path = None Text - }]}} + }]}} -- Nginx ingress requires a default service as a catchall -in let defaultService = { name = "default", host = "default.example.com" } +in let defaultService = + { name = "default" + , host = "default.example.com" + , version = " 1.0" + } -- List of services in let fooService = ./service-foo.dhall @@ -213,7 +224,7 @@ in defaultIngress { name = "nginx" } // { annotations = annotations } } // -{ spec = spec } : Ingress +{ spec = Some Spec spec } : Ingress ``` @@ -225,6 +236,7 @@ dhall-to-yaml --omitNull < ingress.yaml.dhall And we get: ```yaml +-- examples/out/ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress spec: @@ -253,9 +265,26 @@ metadata: kubernetes.io/ingress.class: nginx kubernetes.io/ingress.allow-http: 'false' name: nginx + ``` +## Development +### Tests + +All tests are defined in `release.nix`. We run these tests in CI in a [Hydra +project][hydra-project]. You can run the tests locally with `nix build --no-link +release.nix`. + +### Changing the README + +We build `README.md` from `docs/README.md.dhall` and check it into source control. +The build script `./scripts/build-readme.sh` inlines source code from the +`examples` directory. If you make changes to the readme or the examples you need +to run `scripts/build-readme.sh`. + + +[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes [dhall-lang]: https://github.com/dhall-lang/dhall-lang [Deployment]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/types/io.k8s.api.apps.v1beta2.Deployment.dhall [Deployment-default]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/default/io.k8s.api.apps.v1beta2.Deployment.dhall diff --git a/docs/README.md.dhall b/docs/README.md.dhall new file mode 100644 index 00000000..fa1fa1c7 --- /dev/null +++ b/docs/README.md.dhall @@ -0,0 +1,108 @@ +'' +# `dhall-kubernetes` + +Dhall bindings to Kubernetes. +This will let you typecheck, template and modularize your Kubernetes definitions with [Dhall][dhall-lang]. + +## Prerequisites + +**NOTE**: `dhall-kubernetes` requires at least version `1.14.0` of [the interpreter](https://github.com/dhall-lang/dhall-haskell). + +You can install the latest version with the following: +```bash +stack install dhall dhall-json --resolver=nightly +``` + +For a version compatible with a previous version, check out [this commit](https://github.com/dhall-lang/dhall-kubernetes/tree/b2357dcfa42a008efa203a850163d26f0d106e01). + +## Quick start + +In the `types` folder you'll find the types for the Kubernetes definitions. E.g. +[here's][Deployment] the type for a Deployment. + +Since _most_ of the fields in all definitions are optional, for better +ergonomics while coding Dhall we also generate default values for all types, in +the `default` folder. When some fields are required, the default value is a +function whose input is a record of required fields, that returns the object +with these fields set. E.g. the default for the Deployment is [this +function][Deployment-default]. + +Since this might sound a bit abstract, let's go with some examples. You can find +these examples in the [`./examples` folder](./examples) and evaluate them there. + +### Example: Deployment + +Let's say we have several services, whose configuration has this type: +```haskell +-- examples/Service.dhall +${../examples/Service.dhall as Text} +``` + +So a configuration for a service might look like this: +```haskell +-- examples/service-foo.dhall +${../examples/service-foo.dhall as Text} +``` + +We can then make a Deployment object for this service: +```haskell +-- examples/deployment.dhall +${../examples/deployment.dhall as Text} +``` + +We convert it to yaml with: + +```bash +dhall-to-yaml --omitNull < deployment.dhall +``` + +And we get: +```yaml +-- examples/out/deployment.yaml +${../examples/out/deployment.yaml as Text} +``` + + +### Example: Ingress + +Let's say we now want to generate an Ingress definition (for an Nginx Ingress) +that contains TLS certs and routes for every service. It would be something like +this: +```haskell +-- examples/ingress.dhall +${../examples/ingress.dhall as Text} +``` + +As usual we get the yaml out by running: + +```bash +dhall-to-yaml --omitNull < ingress.yaml.dhall +``` + +And we get: +```yaml +-- examples/out/ingress.yaml +${../examples/out/ingress.yaml as Text} +``` + +## Development + +### Tests + +All tests are defined in `release.nix`. We run these tests in CI in a [Hydra +project][hydra-project]. You can run the tests locally with `nix build --no-link +release.nix`. + +### Changing the README + +We build `README.md` from `docs/README.md.dhall` and check it into source control. +The build script `./scripts/build-readme.sh` inlines source code from the +`examples` directory. If you make changes to the readme or the examples you need +to run `scripts/build-readme.sh`. + + +[hydra-project]: http://hydra.dhall-lang.org/project/dhall-kubernetes +[dhall-lang]: https://github.com/dhall-lang/dhall-lang +[Deployment]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/types/io.k8s.api.apps.v1beta2.Deployment.dhall +[Deployment-default]: https://github.com/dhall-lang/dhall-kubernetes/blob/master/default/io.k8s.api.apps.v1beta2.Deployment.dhall +'' diff --git a/examples/Service.dhall b/examples/Service.dhall new file mode 100644 index 00000000..10052cf9 --- /dev/null +++ b/examples/Service.dhall @@ -0,0 +1,4 @@ +{ name : Text +, host : Text +, version : Text +} diff --git a/examples/deployment.dhall b/examples/deployment.dhall new file mode 100644 index 00000000..cfde64e3 --- /dev/null +++ b/examples/deployment.dhall @@ -0,0 +1,55 @@ +-- Prelude imports + let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map +in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some +in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None + +-- import dhall-kubernetes types and defaults +in let Deployment = ../types/io.k8s.api.apps.v1beta2.Deployment.dhall +in let Spec = ../types/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall +in let PodSpec = ../types/io.k8s.api.core.v1.PodSpec.dhall +in let ContainerPort = ../types/io.k8s.api.core.v1.ContainerPort.dhall +in let defaultDeployment = ../default/io.k8s.api.apps.v1beta2.Deployment.dhall +in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall +in let defaultSpec = ../default/io.k8s.api.apps.v1beta2.DeploymentSpec.dhall +in let defaultTemplate = ../default/io.k8s.api.core.v1.PodTemplateSpec.dhall +in let defaultPodSpec = ../default/io.k8s.api.core.v1.PodSpec.dhall +in let defaultSelector = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector.dhall +in let defaultContainer = ../default/io.k8s.api.core.v1.Container.dhall +in let defaultContainerPort = ../default/io.k8s.api.core.v1.ContainerPort.dhall + +-- and our service +in let fooService = ./service-foo.dhall + +-- Generate the DeploymentSpec for the service +in let selector = Some + (List { mapKey : Text, mapValue : Text }) + [{ mapKey = "app", mapValue = fooService.name }] + +in let spec = defaultSpec +{ selector = defaultSelector // { matchLabels = selector } +, template = defaultTemplate + { metadata = defaultMeta + { name = fooService.name } // { labels = selector } + } // + { spec = Some PodSpec (defaultPodSpec + { containers = [ + defaultContainer + { name = fooService.name } // + { image = Some Text "your-container-service.io/${fooService.name}:${fooService.version}" + , imagePullPolicy = Some Text "Always" + , ports = Some + (List ContainerPort) + [(defaultContainerPort {containerPort = 8080})] + } + ]}) + } +} // +{ replicas = Some Natural 2 +, revisionHistoryLimit = Some Natural 10 +} + +-- and here's the Deployment +in defaultDeployment +{ metadata = defaultMeta { name = fooService.name } +} // +{ spec = Some Spec spec } : Deployment diff --git a/examples/ingress.dhall b/examples/ingress.dhall new file mode 100644 index 00000000..6d32bcda --- /dev/null +++ b/examples/ingress.dhall @@ -0,0 +1,71 @@ +-- Prelude imports + let map = https://raw.githubusercontent.com/dhall-lang/Prelude/e44284bc37a5808861dacd4c8bd13d18411cb961/List/map +in let Some = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/Some +in let None = https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/Optional/None + +-- dhall-kubernetes types and defaults +in let TLS = ../types/io.k8s.api.extensions.v1beta1.IngressTLS.dhall +in let Rule = ../types/io.k8s.api.extensions.v1beta1.IngressRule.dhall +in let RuleVal = ../types/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue.dhall +in let Spec = ../types/io.k8s.api.extensions.v1beta1.IngressSpec.dhall +in let Ingress = ../types/io.k8s.api.extensions.v1beta1.Ingress.dhall +in let defaultIngress = ../default/io.k8s.api.extensions.v1beta1.Ingress.dhall +in let defaultMeta = ../default/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta.dhall +in let defaultSpec = ../default/io.k8s.api.extensions.v1beta1.IngressSpec.dhall + +-- Our Service type +in let Service = ./Service.dhall + +-- Given a service, make a TLS definition with their host and certificate +in let makeTLS = \(service : Service) -> + { hosts = Some (List Text) [ service.host ] + , secretName = Some Text "${service.name}-certificate" + } + +-- Given a service, make an Ingress Rule +in let makeRule = \(service : Service) -> + { host = Some Text service.host + , http = Some RuleVal + { paths = [ { backend = { serviceName = service.name + , servicePort = "80" + } + , path = None Text + }]}} + +-- Nginx ingress requires a default service as a catchall +in let defaultService = + { name = "default" + , host = "default.example.com" + , version = " 1.0" + } + +-- List of services +in let fooService = ./service-foo.dhall +in let services = +[ fooService +, defaultService +] + +-- Some metadata annotations +-- NOTE: `dhall-to-yaml` will generate a record with arbitrary keys from a list +-- of records where mapKey is the key and mapValue is the value of that key +in let genericRecord = List { mapKey : Text, mapValue : Text } +in let kv = \(k : Text) -> \(v : Text) -> { mapKey = k, mapValue = v } + +in let annotations = Some genericRecord +[ kv "kubernetes.io/ingress.class" "nginx" +, kv "kubernetes.io/ingress.allow-http" "false" +] + +-- Generate spec from services +in let spec = defaultSpec // +{ tls = Some (List TLS) (map Service TLS makeTLS services) +, rules = Some (List Rule) (map Service Rule makeRule services) +} + +in defaultIngress +{ metadata = defaultMeta + { name = "nginx" } // + { annotations = annotations } +} // +{ spec = Some Spec spec } : Ingress diff --git a/examples/out/deployment.yaml b/examples/out/deployment.yaml new file mode 100644 index 00000000..061cda78 --- /dev/null +++ b/examples/out/deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1beta2 +kind: Deployment +spec: + revisionHistoryLimit: 10 + selector: + matchLabels: + app: foo + template: + spec: + containers: + - image: your-container-service.io/foo:1.0.1 + imagePullPolicy: Always + name: foo + ports: + - containerPort: 8080 + metadata: + name: foo + labels: + app: foo + replicas: 2 +metadata: + name: foo diff --git a/examples/out/ingress.yaml b/examples/out/ingress.yaml new file mode 100644 index 00000000..9f0565df --- /dev/null +++ b/examples/out/ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +spec: + rules: + - http: + paths: + - backend: + servicePort: '80' + serviceName: foo + host: foo.example.com + - http: + paths: + - backend: + servicePort: '80' + serviceName: default + host: default.example.com + tls: + - hosts: + - foo.example.com + secretName: foo-certificate + - hosts: + - default.example.com + secretName: default-certificate +metadata: + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/ingress.allow-http: 'false' + name: nginx diff --git a/examples/service-foo.dhall b/examples/service-foo.dhall new file mode 100644 index 00000000..41a036ea --- /dev/null +++ b/examples/service-foo.dhall @@ -0,0 +1,4 @@ +{ name = "foo" +, host = "foo.example.com" +, version = "1.0.1" +} diff --git a/release.nix b/release.nix index be52eee1..c34c9fe2 100644 --- a/release.nix +++ b/release.nix @@ -1,34 +1,55 @@ let pkgs = import ./nixpkgs.nix; - dhall-kubernetes-smoketests = - pkgs.runCommand - "all-generated-files-compile" - { nativeBuildInputs = [ - pkgs.python3 - pkgs.bash - pkgs.dhall - pkgs.glibcLocales - ]; - } + nativeBuildInputs = [ + pkgs.git + pkgs.python3 + pkgs.bash + pkgs.dhall + pkgs.dhall-json + pkgs.dhall-text + pkgs.glibcLocales + ]; + + runCommand = name: script: + pkgs.runCommand name { inherit nativeBuildInputs; } script; + + generatedFilesCompile = + runCommand + "generated-files-compile" '' cd ${./.} - LC_ALL=en_US.UTF-8 python3 ${./check-source.py} + LC_ALL=en_US.UTF-8 ./scripts/check-source.py touch $out ''; - # Derivation that trivially depends on the current directory so that Hydra's - # pull request builder always posts a GitHub status on each revision - pwd = pkgs.runCommand "pwd" { here = ./.; } "touch $out"; + buildReadme = + runCommand + "build-readme" + '' + cd ${./.} + ./scripts/build-readme.sh $out -in - { dhall-kubernetes = pkgs.releaseTools.aggregate { - name = "dhall-kubernetes"; + diff --unified --color=always README.md $out + ''; - constituents = [ - dhall-kubernetes-smoketests - pwd - ]; - }; - } + validateExamples = + runCommand + "validate-examples" + '' + cd ${./.} + mkdir $out + LC_ALL=en_US.UTF-8 ./scripts/build-examples.py $out + ''; + +in { + dhall-kubernetes = pkgs.releaseTools.aggregate { + name = "dhall-kubernetes"; + constituents = [ + generatedFilesCompile + validateExamples + buildReadme + ]; + }; +} diff --git a/scripts/build-examples.py b/scripts/build-examples.py new file mode 100755 index 00000000..e3c5ce31 --- /dev/null +++ b/scripts/build-examples.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Build YAML files from the example Dhall sources. +# +# An optional second argument determines the directory to write the YAML +# files to. Defaults to the exmaples directory. +# +# Also checks that the generated files do not differ from what is +# checked into source control. We list all generated files that differ +# and exit with status code 1 if the list is non-empty. + +import os +from subprocess import run, DEVNULL +import sys + +examples_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'examples')) +examples = [ + 'deployment', + 'ingress' +] + +TERM_FAIL = '\033[91m' +TERM_CLEAR = '\033[0m' + + +def check_clean(path_1, path_2): + if path_1 == path_2: + cmd = ['git', 'diff', '--exit-code', '--', path_1] + else: + cmd = ['diff', '--unified', '--', path_1, path_2] + + result = run(cmd, check=False, stdout=DEVNULL) + if result.returncode == 0: + return True + elif result.returncode == 1: + return False + else: + # Raises a `CalledProcessError` + result.check_returncode() + + +def build_yaml (example, out_dir): + source = os.path.join(examples_dir, example + '.dhall') + print('Building {}'.format(source)) + target = os.path.join(out_dir, example + '.yaml') + expected = os.path.join(examples_dir, 'out', example + '.yaml') + cmd = 'dhall-to-yaml --omitNull <<< "./{source}" >{target}'.format(source=source, target=target) + run(cmd, shell=True, executable='bash', check=True) + return check_clean(expected, target) + + +def main(): + if len(sys.argv) >= 2: + out_dir = sys.argv[1] + else: + out_dir = os.path.join(examples_dir, 'out') + + clean_failures = [] + for example in examples: + is_clean = build_yaml(example, out_dir) + if is_clean == False: + clean_failures.append(example + '.yaml') + + if len(clean_failures) > 0: + print(TERM_FAIL + 'Clean check failed. The following files differ' + TERM_CLEAR) + for failure in clean_failures: + print(' ' + failure) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/scripts/build-readme.sh b/scripts/build-readme.sh new file mode 100755 index 00000000..b5400156 --- /dev/null +++ b/scripts/build-readme.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +if [[ -z $1 ]]; then + output=README.md +else + output="$1" +fi + +LC_ALL=en_US.UTF-8 dhall-to-text <<< './docs/README.md.dhall' > $output diff --git a/check-source.py b/scripts/check-source.py similarity index 100% rename from check-source.py rename to scripts/check-source.py diff --git a/shell.nix b/shell.nix index 83125430..7b718d27 100644 --- a/shell.nix +++ b/shell.nix @@ -7,6 +7,7 @@ in pkgs.git pkgs.dhall pkgs.dhall-json + pkgs.dhall-text pkgs.python3 pkgs.python3Packages.requests pkgs.glibcLocales