├── .gitignore ├── LICENSE ├── README.md ├── cue.mod ├── module.cue └── sums.cue ├── examples ├── cue │ └── in.cue ├── dagger │ ├── dagger.cue │ └── testdata │ │ ├── case_01.txt │ │ └── case_02.txt ├── fail │ └── bad.cue ├── go │ ├── dagger.cue │ ├── go.mod │ ├── go.sum │ └── main.go ├── script │ └── run.sh ├── txtar │ ├── case_01.txt │ └── case_02.txt └── versions │ ├── dagger.cue │ └── list.sh ├── harmony.cue ├── registry ├── cuetorials.cue ├── examples.cue ├── hof.cue └── schema.cue ├── run.py └── testers ├── image.cue ├── plans.cue └── versions.cue /.gitignore: -------------------------------------------------------------------------------- 1 | cue.mod/pkg/ 2 | */cue.mod/pkg/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, _Hofstadter 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # harmony-cue 2 | 3 | `harmony-cue` is built on [harmony](https://github.com/hofstadter-io/harmony) 4 | and enables testing CUE projects against any release or commit. 5 | 6 | Supports tests using 7 | 8 | - `cue` cli 9 | - CUE's GoAPI 10 | - [Dagger](https://dagger.io) 11 | - Testscript (txtar files) 12 | - Bash scripts 13 | 14 | You can see all available tools in 15 | [testers/image.cue](./testers/image.cue) 16 | 17 | ## Running harmony-cue 18 | 19 | ``` 20 | ./run.py [reg] [case] 21 | ``` 22 | 23 | - Use `--cue [tag,branch,commit]` to switch the CUE version used. 24 | - Use `--cuepath ../path/to/cue` to use local CUE repository. 25 | - See `./run.py -h` for full help message, where you can set more 26 | 27 | ## Add a project 28 | 29 | To add your project to `harmony-cue`, 30 | open a pull request to add a file to `registry/.cue`. 31 | 32 | ```cue 33 | package registry 34 | 35 | Registry: : Registration & { 36 | remote: "github.com/org/repo" 37 | ref: "main" // branch, tag, or commit 38 | 39 | // examples using the available short codes 40 | cases: { 41 | cue: { _cue: ["eval", "in.cue"], workdir: "/work/examples/cue" } 42 | hof: { _script: "./test.sh", workdir: "/work/examples/hof" } 43 | goapi: { _goapi: "go run main.go", workdir: "/work/examples/go" } 44 | dagger: { _dagger: "run", workdir: "/work/examples/dagger" } 45 | txtar: { _testscript: "*.txt", workdir: "/work/examples/txtar" } 46 | script: { 47 | workdir: "/work/examples/script" 48 | _script: """ 49 | #!/usr/bin/env bash 50 | set -euo pipefail 51 | 52 | echo "a bash script" 53 | pwd 54 | ls 55 | ./run.sh 56 | """ 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | Test your project with `dagger do ` or `./run.py `. 63 | The python script makes it easier to inject versions and will run cases sequentially. 64 | -------------------------------------------------------------------------------- /cue.mod/module.cue: -------------------------------------------------------------------------------- 1 | module: "github.com/hofstadter-io/harmony-cue" 2 | cue: "v0.4.2" 3 | 4 | require: { 5 | "github.com/hofstadter-io/harmony": "v0.2.0" 6 | } 7 | -------------------------------------------------------------------------------- /cue.mod/sums.cue: -------------------------------------------------------------------------------- 1 | sums: { 2 | "github.com/hofstadter-io/harmony": "v0.2.0": ["h1:dup22pSofUxu2p4WbTTBsWXC/LSW2cqI+SLJMpxW798="] 3 | } 4 | -------------------------------------------------------------------------------- /examples/cue/in.cue: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | foo: "bar" 4 | -------------------------------------------------------------------------------- /examples/dagger/dagger.cue: -------------------------------------------------------------------------------- 1 | package dagger 2 | 3 | import ( 4 | "github.com/hofstadter-io/harmony-cue/testers" 5 | ) 6 | 7 | // todo: how do we handle 'local' cue version in a nested (running dagger from a harmony test case) 8 | // also workdir? 9 | testers.TestscriptPlan & { 10 | actions: glob: "testdata/*.txt" 11 | // shouldn't really need this? 12 | actions: { 13 | versions: { ... } // filled with defaults 14 | builder: testers.Build & { versions: testers.Versions & actions.versions } 15 | image: builder.output 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/dagger/testdata/case_01.txt: -------------------------------------------------------------------------------- 1 | exec cue export in.cue 2 | cmp stdout golden.stdout 3 | 4 | -- in.cue -- 5 | foo: "bar" 6 | 7 | -- golden.stdout -- 8 | { 9 | "foo": "bar" 10 | } 11 | -------------------------------------------------------------------------------- /examples/dagger/testdata/case_02.txt: -------------------------------------------------------------------------------- 1 | exec cue export in.cue 2 | cmp stdout golden.stdout 3 | 4 | -- in.cue -- 5 | cow: "moo" 6 | 7 | -- golden.stdout -- 8 | { 9 | "cow": "moo" 10 | } 11 | -------------------------------------------------------------------------------- /examples/fail/bad.cue: -------------------------------------------------------------------------------- 1 | package bad 2 | 3 | foo bar 4 | -------------------------------------------------------------------------------- /examples/go/dagger.cue: -------------------------------------------------------------------------------- 1 | package go 2 | 3 | import ( 4 | "github.com/hofstadter-io/harmony-cue/testers" 5 | ) 6 | 7 | testers.CueGoApiPlan & { 8 | actions: glob: "testdata/*.txt" 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/go/go.mod: -------------------------------------------------------------------------------- 1 | module hof.io/example/go 2 | 3 | go 1.17 4 | 5 | require cuelang.org/go v0.4.3-beta.2 6 | 7 | require ( 8 | github.com/cockroachdb/apd/v2 v2.0.1 // indirect 9 | github.com/google/uuid v1.2.0 // indirect 10 | github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect 11 | github.com/pkg/errors v0.8.1 // indirect 12 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect 13 | golang.org/x/text v0.3.7 // indirect 14 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect 15 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /examples/go/go.sum: -------------------------------------------------------------------------------- 1 | cuelang.org/go v0.4.3-beta.2 h1:arGL2jGSVaCLcrEfiYSc8RvbguA3TFbPfqr4AhX4Pw4= 2 | cuelang.org/go v0.4.3-beta.2/go.mod h1:GD2xePZahveVcMWn3Gj87D7/eUfL2Nt7Jbs6mEv+Rps= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= 5 | github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw= 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 9 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 10 | github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 11 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 12 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 13 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 14 | github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= 15 | github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= 16 | github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= 17 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 19 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0= 22 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 23 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 25 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= 26 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 27 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 28 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 29 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 30 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 31 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 32 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 33 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 36 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 37 | -------------------------------------------------------------------------------- /examples/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "cuelang.org/go/cue" 7 | "cuelang.org/go/cue/cuecontext" 8 | ) 9 | 10 | const val = ` 11 | i: int 12 | s: string 13 | t: [string]: string 14 | _h: "hidden" 15 | #d: foo: "bar" 16 | ` 17 | 18 | func main() { 19 | var ( 20 | c *cue.Context 21 | v cue.Value 22 | ) 23 | 24 | // create a context 25 | c = cuecontext.New() 26 | 27 | // compile some CUE into a Value 28 | v = c.CompileString(val) 29 | 30 | // print the value 31 | fmt.Printf("// %%v\n%v\n\n// %%# v\n%# v\n", v, v) 32 | } 33 | -------------------------------------------------------------------------------- /examples/script/run.sh: -------------------------------------------------------------------------------- 1 | echo "a meta script" 2 | -------------------------------------------------------------------------------- /examples/txtar/case_01.txt: -------------------------------------------------------------------------------- 1 | exec cue export in.cue 2 | cmp stdout golden.stdout 3 | 4 | -- in.cue -- 5 | foo: "bar" 6 | 7 | -- golden.stdout -- 8 | { 9 | "foo": "bar" 10 | } 11 | -------------------------------------------------------------------------------- /examples/txtar/case_02.txt: -------------------------------------------------------------------------------- 1 | exec cue export in.cue 2 | cmp stdout golden.stdout 3 | 4 | -- in.cue -- 5 | cow: "moo" 6 | 7 | -- golden.stdout -- 8 | { 9 | "cow": "moo" 10 | } 11 | -------------------------------------------------------------------------------- /examples/versions/dagger.cue: -------------------------------------------------------------------------------- 1 | package versions 2 | 3 | import ( 4 | "github.com/hofstadter-io/harmony-cue/testers" 5 | ) 6 | 7 | // todo: how do we handle 'local' cue version in a nested (running dagger from a harmony test case) 8 | // testers.VersionPlan 9 | testers.VersionPlan & { 10 | actions: { 11 | versions: { ... } // filled with defaults 12 | builder: testers.Build & { versions: testers.Versions & actions.versions } 13 | image: builder.output 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/versions/list.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go version 4 | cue version 5 | dagger version 6 | hof version 7 | python3 --version 8 | 9 | tree -d /work/cue.mod 10 | 11 | set +e 12 | ls /localcue 13 | exit 0 14 | -------------------------------------------------------------------------------- /harmony.cue: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "dagger.io/dagger" 5 | "universe.dagger.io/docker" 6 | "universe.dagger.io/docker/cli" 7 | "github.com/hofstadter-io/harmony" 8 | 9 | "github.com/hofstadter-io/harmony-cue/registry" 10 | "github.com/hofstadter-io/harmony-cue/testers" 11 | ) 12 | 13 | // A dagger plan is used as the driver for testing 14 | dagger.#Plan 15 | 16 | // add actions from Harmony 17 | actions: harmony.Harmony 18 | 19 | // project specific configuration follows 20 | 21 | // for docker-in-docker, also required for dagger-in-dagger 22 | client: network: "unix:///var/run/docker.sock": connect: dagger.#Socket 23 | 24 | client: env: DOCKER_USER: string 25 | client: env: DOCKER_TOKEN: dagger.#Secret 26 | 27 | if actions.versions.cue == "local" { 28 | // get the current dir 29 | client: filesystem: "\(actions.pathToCUE)": read: { 30 | // CUE type defines expected content 31 | contents: dagger.#FS 32 | } 33 | } 34 | 35 | actions: { 36 | // path to a local copy of CUE 37 | pathToCUE: string | *"../cue" 38 | 39 | // global version config for this harmony 40 | versions: testers.Versions & { 41 | self: string | *"dirty" 42 | } 43 | 44 | // the registry of downstream projects 45 | "registry": registry.Registry 46 | 47 | name: "hofstadter/harmony-cue:\(versions.self)" 48 | 49 | // the image test cases are run in 50 | // here we have a custom / parameterized base image 51 | build: testers.Build & { 52 | "versions": versions 53 | if versions.cue == "local" { 54 | cuesource: client.filesystem["\(actions.pathToCUE)"].read.contents 55 | } 56 | } 57 | 58 | load: cli.#Load & { 59 | image: build.output 60 | host: client.network."unix:///var/run/docker.sock".connect 61 | tag: name 62 | } 63 | 64 | push: docker.#Push & { 65 | image: build.output 66 | dest: name 67 | auth: { 68 | username: client.env.DOCKER_USER 69 | secret: client.env.DOCKER_TOKEN 70 | } 71 | } 72 | 73 | pull: docker.#Pull & { 74 | source: name 75 | auth: { 76 | username: client.env.DOCKER_USER 77 | secret: client.env.DOCKER_TOKEN 78 | } 79 | } 80 | 81 | runner: docker.#Image | *pull.image 82 | if versions.self == "dirty" { 83 | runner: build.output 84 | } 85 | 86 | // where downstream project code is checked out 87 | workdir: "/work" 88 | 89 | extra: run: { 90 | mounts: { 91 | // for dagger-in-dagger, and docker-in-docker 92 | docker: { 93 | contents: client.network."unix:///var/run/docker.sock".connect 94 | dest: "/var/run/docker.sock" 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /registry/cuetorials.cue: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | Registry: cuetorials: Registration & { 4 | remote: "github.com/hofstadter-io/cuetorials.com" 5 | ref: "main" 6 | 7 | cases: { 8 | verify: { _script: "make verify_code && make verify_diff", workdir: "/work" } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /registry/examples.cue: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | Registry: examples: Registration & { 4 | remote: "github.com/hofstadter-io/harmony-cue" 5 | ref: "main" 6 | 7 | cases: { 8 | cue: { _cue: ["eval", "in.cue"], workdir: "/work/examples/cue" } 9 | hof: { _script: "hof -h", workdir: "/work" } 10 | goapi: { _goapi: "go run main.go", workdir: "/work/examples/go" } 11 | dagger: { _dagger: "run", workdir: "/work/examples/dagger" } 12 | txtar: { _testscript: "*.txt", workdir: "/work/examples/txtar" } 13 | script: { 14 | workdir: "/work/examples/script" 15 | _script: """ 16 | #!/usr/bin/env bash 17 | set -euo pipefail 18 | 19 | echo "a bash script" 20 | pwd 21 | ls 22 | ./run.sh 23 | """ 24 | } 25 | 26 | "versions-example": { _script: "./list.sh", workdir: "/work/examples/versions" } 27 | "versions-dagger": { _dagger: "run --plan ./examples/versions/dagger.cue", workdir: "/work" } 28 | "versions-script": { 29 | workdir: "/work" 30 | _script: """ 31 | #!/usr/bin/env bash 32 | 33 | go version 34 | cue version 35 | dagger version 36 | hof version 37 | python3 --version 38 | 39 | tree -d /work/cue.mod 40 | ls /localcue 41 | exit 0 42 | """ 43 | } 44 | 45 | inception: { 46 | workdir: "/work" 47 | _script: """ 48 | #!/usr/bin/env bash 49 | dagger project update 50 | mkdir -p cue.mod/pkg/github.com/hofstadter-io 51 | pushd cue.mod/pkg/github.com/hofstadter-io 52 | git clone https://github.com/hofstadter-io/harmony 53 | popd 54 | ./run.py examples versions-dagger 55 | """ 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /registry/hof.cue: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | Registry: hof: Registration & { 4 | remote: "github.com/hofstadter-io/hof" 5 | ref: "_dev" 6 | 7 | cases: { 8 | build: { 9 | workdir: "/work" 10 | _goapi: """ 11 | go install ./cmd/hof 12 | hof -h 13 | """ 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /registry/schema.cue: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "strings" 5 | "encoding/json" 6 | 7 | "universe.dagger.io/docker" 8 | "github.com/hofstadter-io/harmony" 9 | ) 10 | 11 | Registration: R=(harmony.Registration & { 12 | // add our short codes 13 | cases: [string]: docker.#Run & { 14 | _dagger?: string 15 | if _dagger != _|_ { 16 | command: { 17 | name: "bash" 18 | args: ["-c", _script] 19 | _vers: json.Marshal(R.versions) 20 | _script: """ 21 | set -xeuo pipefail 22 | dagger version 23 | dagger project update 24 | tree -d /work/cue.mod 25 | dagger do \(_dagger) --with 'actions: versions: \(_vers)' --log-format plain 26 | """ 27 | } 28 | } 29 | 30 | _cue?: [...string] 31 | if _cue != _|_ { 32 | command: { 33 | name: "bash" 34 | args: ["-c", _script] 35 | _script: """ 36 | cue version 37 | cue \(strings.Join(_cue, " ")) 38 | """ 39 | } 40 | } 41 | 42 | _goapi?: string 43 | if _goapi != _|_ { 44 | command: { 45 | name: "bash" 46 | args: ["-c", _script] 47 | _script: string 48 | if R.versions.cue != "local" { 49 | _script: """ 50 | set -xeuo pipefail 51 | go version 52 | go get cuelang.org/go@\(R.versions.cue) 53 | go mod tidy -compat=1.17 54 | \(_goapi) 55 | """ 56 | } 57 | if R.versions.cue == "local" { 58 | _script: """ 59 | set -xeuo pipefail 60 | go version 61 | go mod edit -replace cuelang.org/go=/localcue 62 | go mod tidy -compat=1.17 63 | \(_goapi) 64 | """ 65 | } 66 | } 67 | } 68 | 69 | _testscript?: string 70 | if _testscript != _|_ { 71 | command: { 72 | name: "bash" 73 | args: ["-c", _script] 74 | _script: """ 75 | set -euo pipefail 76 | for f in `ls \(_testscript)`; do 77 | echo $f 78 | testscript $f 79 | echo 80 | done 81 | """ 82 | } 83 | } 84 | 85 | _script?: string 86 | if _script != _|_ { 87 | command: { 88 | name: "bash" 89 | args: ["-c", _script] 90 | } 91 | } 92 | } 93 | }) 94 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import csv 5 | import os 6 | import subprocess 7 | 8 | # args and flags to the script 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('action', nargs='*', help="dagger do action path") 11 | parser.add_argument('--self', help="own version, for push/pull mainly") 12 | parser.add_argument('--cuepath', help="path to local cue repository") 13 | parser.add_argument('--cue', help="cue version") 14 | parser.add_argument('--hof', help="hof version") 15 | parser.add_argument('--dagger', help="dagger version") 16 | parser.add_argument('--go', help="go version") 17 | parser.add_argument('--fmt', help="dagger log format", default="plain") 18 | parser.add_argument('--no-cache', help="disable dagger (buildkit) cache" , action='store_true', default=False) 19 | args = parser.parse_args() 20 | 21 | # get full list from cue 22 | p = os.popen("cue eval -e actions.csv --out text") 23 | out = p.read().strip() 24 | actions = list(csv.reader(out.split("\n"))) 25 | 26 | # start `--with` content 27 | # dagger_with = f"'actions: {{ versions: {{ {versS} }}{pathC} }}'" 28 | dagger_with = "'actions: { " 29 | 30 | # possibly add CUE path 31 | if args.cuepath is not None: 32 | dagger_with += f"pathToCUE: \"{args.cuepath}\"" 33 | args.cue = "local" 34 | 35 | # build up the injected version CUE code 36 | vers = "" 37 | if args.cue is not None: 38 | if args.cue == "local": 39 | dagger_with += ", " 40 | vers += f'cue: "{args.cue}"' 41 | if args.hof is not None: 42 | if vers != "": 43 | vers += ", " 44 | vers += f'hof: "{args.hof}"' 45 | if args.go is not None: 46 | if vers != "": 47 | vers += ", " 48 | vers += f'go: "{args.go}"' 49 | if args.dagger is not None: 50 | if vers != "": 51 | vers += ", " 52 | vers += f'dagger: "{args.dagger}"' 53 | if args.self is not None: 54 | if vers != "": 55 | vers += ", " 56 | vers += f'self: "{args.self}"' 57 | 58 | if vers != "": 59 | dagger_with += f"versions: {{ {vers} }}" 60 | 61 | dagger_with += "}'" 62 | 63 | flags = ["--log-format", args.fmt, "--with", dagger_with] 64 | if args.no_cache: 65 | flags.append("--no-cache") 66 | 67 | # loop over all available dagger actions (reg/case) 68 | matchAny = False 69 | for action in actions: 70 | # enable pass through of dagger do args 71 | match = True 72 | for i, p in enumerate(args.action): 73 | # filter out others when action missing 74 | if p != action[i]: 75 | match = False 76 | break 77 | 78 | if match: 79 | matchAny = True 80 | cmd = ["dagger", "do"] + action + flags 81 | print("Running:", " ".join(cmd)) 82 | try: 83 | subprocess.run(["bash", "-c", " ".join(cmd)], check=True) 84 | except: 85 | # easily grep-able 86 | print("HARMONY-FAILURE:", " ".join(cmd)) 87 | 88 | if not matchAny: 89 | 90 | cmd = ["dagger", "do"] + args.action + flags 91 | print("Running:", " ".join(cmd)) 92 | try: 93 | subprocess.run(["bash", "-c", " ".join(cmd)], check=True) 94 | except: 95 | # easily grep-able 96 | print("HARMONY-FAILURE:", " ".join(cmd)) 97 | -------------------------------------------------------------------------------- /testers/image.cue: -------------------------------------------------------------------------------- 1 | package testers 2 | 3 | import ( 4 | "strings" 5 | 6 | "dagger.io/dagger" 7 | "dagger.io/dagger/core" 8 | "universe.dagger.io/docker" 9 | "universe.dagger.io/git" 10 | "universe.dagger.io/go" 11 | ) 12 | 13 | Base: { 14 | version: string 15 | 16 | packages: [pkgName=string]: version: string | *"" 17 | 18 | // FIXME Basically a copy of alpine.#Build with a different image 19 | // Should we create a special definition? 20 | docker.#Build & { 21 | steps: [ 22 | docker.#Pull & { 23 | source: "index.docker.io/golang:\(version)-alpine" 24 | }, 25 | for pkgName, pkg in packages { 26 | docker.#Run & { 27 | command: { 28 | name: "apk" 29 | args: ["add", "\(pkgName)\(pkg.version)"] 30 | flags: { 31 | "-U": true 32 | "--no-cache": true 33 | } 34 | } 35 | } 36 | }, 37 | ] 38 | } 39 | } 40 | 41 | Build: { 42 | // config 43 | versions: { 44 | cue: string 45 | hof: string 46 | dagger: string 47 | go: string 48 | testscript: string 49 | "txtar-c": testscript 50 | "txtar-x": testscript 51 | } 52 | // provide when cue version is 'local' 53 | cuesource: dagger.#FS 54 | 55 | // Go based image 56 | base: Base & { 57 | version: versions.go 58 | packages: { 59 | bash: {} 60 | "docker-cli": {} 61 | gcc: {} 62 | git: {} 63 | make: {} 64 | "musl-dev": {} 65 | python3: {} 66 | tree: {} 67 | } 68 | } 69 | 70 | gotools: { 71 | [tool=string]: { 72 | code: git.#Pull & { 73 | remote: string 74 | ref: versions["\(tool)"] 75 | keepGitDir: true 76 | } 77 | commit: { 78 | write: docker.#Run & { 79 | input: base.output 80 | command: { 81 | name: "sh" 82 | args: ["-c", "git rev-parse HEAD > /commit.txt"] 83 | } 84 | mounts: source: { 85 | dest: "/src" 86 | contents: code.output 87 | } 88 | workdir: "/src" 89 | } 90 | read: core.#ReadFile & { 91 | input: write.output.rootfs 92 | path: "/commit.txt" 93 | } 94 | } 95 | build: go.#Build & { 96 | container: input: base.output 97 | source: code.output 98 | package: "./cmd/\(tool)" 99 | } 100 | } 101 | 102 | if versions.cue != "local" { 103 | cue: code: remote: "https://github.com/cue-lang/cue" 104 | } 105 | cue: build: { 106 | ldflags: strings.Join([ 107 | "-X cuelang.org/go/cmd/cue/cmd.version=\(versions.cue)", 108 | ], " ") 109 | } 110 | 111 | hof: { 112 | code: remote: "https://github.com/hofstadter-io/hof" 113 | build: { 114 | ldflags: strings.Join([ 115 | "-X github.com/hofstadter-io/hof/cmd/hof/verinfo.Version=\(versions.hof)", 116 | "-X github.com/hofstadter-io/hof/cmd/hof/verinfo.Commit=\(hof.commit.read.contents)", 117 | ], " ") 118 | } 119 | } 120 | 121 | dagger: { 122 | code: remote: "https://github.com/dagger/dagger" 123 | build: { 124 | ldflags: strings.Join([ 125 | "-X go.dagger.io/dagger/version.Version=\(versions.dagger)", 126 | "-X go.dagger.io/dagger/version.Revision=\(dagger.commit.read.contents)", 127 | ], " ") 128 | } 129 | } 130 | 131 | testscript: code: remote: "https://github.com/rogpeppe/go-internal" 132 | "txtar-c": code: remote: "https://github.com/rogpeppe/go-internal" 133 | "txtar-x": code: remote: "https://github.com/rogpeppe/go-internal" 134 | } 135 | 136 | localcue: _ 137 | if versions.cue == "local" { 138 | localcue: go.#Build & { 139 | container: input: base.output 140 | source: cuesource 141 | package: "./cmd/cue" 142 | } 143 | } 144 | 145 | // collect the bins for looping 146 | bins: [ 147 | gotools.hof.build.output, 148 | gotools.dagger.build.output, 149 | gotools.testscript.build.output, 150 | gotools."txtar-c".build.output, 151 | gotools."txtar-x".build.output, 152 | if versions.cue == "local" { localcue.output }, 153 | if versions.cue != "local" { gotools.cue.build.output }, 154 | ] 155 | 156 | // add the binaries to the base image 157 | copy: docker.#Build & { 158 | steps: [{ input: base.output}, ...] 159 | steps: [ 160 | for bin in bins { 161 | docker.#Copy & { 162 | contents: bin 163 | dest: "/usr/local/bin" 164 | } 165 | }, 166 | if versions.cue == "local" { 167 | docker.#Copy & { 168 | contents: cuesource 169 | dest: "/localcue" 170 | } 171 | }, 172 | ] 173 | } 174 | 175 | // the final image users should reference 176 | output: docker.#Image & copy.output 177 | } 178 | -------------------------------------------------------------------------------- /testers/plans.cue: -------------------------------------------------------------------------------- 1 | package testers 2 | 3 | import ( 4 | "dagger.io/dagger" 5 | "universe.dagger.io/docker" 6 | "universe.dagger.io/go" 7 | ) 8 | 9 | // reusable Dagger Plans 10 | 11 | WorkdirPlan: dagger.#Plan & { 12 | // get the current dir 13 | client: filesystem: ".": read: { 14 | // CUE type defines expected content 15 | contents: dagger.#FS 16 | } 17 | 18 | actions: { 19 | // default versions 20 | versions: Versions 21 | 22 | // start with a hidden ref to out base image 23 | image: docker.#Image 24 | 25 | // copy in source 26 | // source: docker.#Copy & { 27 | // input: image.output 28 | // contents: client.filesystem.".".read.contents 29 | // dest: "/work" 30 | // } 31 | 32 | // default Run with common config set 33 | run: docker.#Run & { 34 | input: image 35 | workdir: "/work" 36 | always: true 37 | 38 | // mount in source 39 | mounts: { 40 | work: { 41 | dest: "/work" 42 | contents: client.filesystem.".".read.contents 43 | } 44 | } 45 | } 46 | } 47 | 48 | } 49 | 50 | DaggerPlan: WorkdirPlan & { 51 | actions: run: command: { 52 | name: "dagger" 53 | args: _ | *["do", "run"] 54 | } 55 | } 56 | 57 | CuePlan: WorkdirPlan & { 58 | actions: run: command: {} | *{ 59 | name: "cue" 60 | args: _ | *["eval"] 61 | } 62 | } 63 | 64 | CueGoApiPlan: WorkdirPlan & { 65 | actions: { 66 | // this is set by the unity-dagger driver 67 | versions: cue: string 68 | 69 | source: go.#Image 70 | 71 | cue: docker.#Run & { 72 | input: source.output 73 | command: { 74 | name: "go" 75 | args: _ | *["get", "cuelang.org/go@\(versions.cue)"] 76 | } 77 | } 78 | 79 | tidy: docker.#Run & { 80 | input: cue.output 81 | command: { 82 | name: "go" 83 | args: _ | *["mod", "tidy"] 84 | } 85 | } 86 | 87 | run: docker.#Run & { 88 | input: tidy.output 89 | command: {} | *{ 90 | name: "go" 91 | args: _ | *["mod", "tidy"] 92 | } 93 | } 94 | } 95 | } 96 | 97 | VersionPlan: WorkdirPlan & { 98 | actions: { 99 | run: { 100 | command: { 101 | name: "bash" 102 | args: ["-c", _script] 103 | _script: """ 104 | #!/usr/bin/env bash 105 | 106 | go version 107 | cue version 108 | dagger version 109 | hof version 110 | python3 --version 111 | 112 | tree -d /work/cue.mod 113 | ls /localcue 114 | exit 0 115 | """ 116 | } 117 | } 118 | } 119 | } 120 | 121 | TestscriptPlan: WorkdirPlan & { 122 | actions: { 123 | glob: string 124 | run: { 125 | command: { 126 | name: "sh" 127 | args: ["-c", _script] 128 | _script: """ 129 | set -euo pipefail 130 | 131 | # todo, count errors 132 | for f in `ls \(glob)`; do 133 | echo $f 134 | testscript $f 135 | echo 136 | done 137 | """ 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /testers/versions.cue: -------------------------------------------------------------------------------- 1 | package testers 2 | 3 | Versions: { 4 | cue: string | *"v0.4.3-beta.2" 5 | hof: string | *"v0.6.2-beta.1" 6 | dagger: string | *"v0.2.7" 7 | go: string | *"1.18.1" 8 | testscript: string | *"v1.8.1" 9 | } 10 | --------------------------------------------------------------------------------