Extract and check examples (#27)

* We move all the example code from the readme to the `examples` folder.
* We provide a `scripts/build-readme.sh` script that inlines referenced
  examples in `README.md.in` and outputs `README.md`. The script also
  verifies that the output readme is the same as in version control.
* We provide a `scripts/build-examples.py` script that builds the Yaml
  output for all examples. The script also verifies that the generated
  Yaml files are the same as in version control.
This commit is contained in:
Thomas Scholtes 2018-08-03 16:24:32 +02:00 committed by Fabrizio Ferrai
parent e132545c34
commit 4a7f02cd0d
14 changed files with 496 additions and 71 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/result
/result-*

125
README.md
View File

@ -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

108
docs/README.md.dhall Normal file
View File

@ -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
''

4
examples/Service.dhall Normal file
View File

@ -0,0 +1,4 @@
{ name : Text
, host : Text
, version : Text
}

55
examples/deployment.dhall Normal file
View File

@ -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

71
examples/ingress.dhall Normal file
View File

@ -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

View File

@ -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

28
examples/out/ingress.yaml Normal file
View File

@ -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

View File

@ -0,0 +1,4 @@
{ name = "foo"
, host = "foo.example.com"
, version = "1.0.1"
}

View File

@ -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
];
};
}

71
scripts/build-examples.py Executable file
View File

@ -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()

9
scripts/build-readme.sh Executable file
View File

@ -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

View File

@ -7,6 +7,7 @@ in
pkgs.git
pkgs.dhall
pkgs.dhall-json
pkgs.dhall-text
pkgs.python3
pkgs.python3Packages.requests
pkgs.glibcLocales