├── .bazelversion ├── .bcr ├── config.yml ├── metadata.template.json ├── presubmit.yml └── source.template.json ├── .github ├── actions │ └── build-test │ │ └── action.yaml └── workflows │ ├── ci.yaml │ ├── prepare-release │ └── release.yaml ├── .gitignore ├── BUILD.bazel ├── LICENSE.txt ├── MODULE.bazel ├── MODULE.bazel.lock ├── README.rst ├── WORKSPACE.bazel ├── cue ├── BUILD.bazel ├── cue-run-from-archived-runfiles ├── cue-run-from-runfiles ├── cue.bzl ├── extensions.bzl └── private │ ├── BUILD.bazel │ ├── config.bzl │ ├── extensions │ ├── BUILD.bazel │ ├── cue.bzl │ ├── download.bzl │ └── semver.bzl │ ├── future.bzl │ └── tools │ ├── BUILD.bazel │ └── cue │ ├── BUILD.bazel │ ├── BUILD.tool.bazel │ ├── BUILD.toolchains.bazel │ └── toolchain.bzl ├── examples └── bzlmod │ ├── BUILD.bazel │ ├── MODULE.bazel │ └── root │ ├── BUILD.bazel │ ├── entries.cue │ └── schema.cue ├── go.mod ├── test ├── BUILD.bazel ├── cue_test.bzl └── testdata │ ├── consolidated │ ├── BUILD.bazel │ ├── consolidated-golden.cue │ ├── cue.mod │ │ ├── BUILD.bazel │ │ ├── module.cue │ │ └── pkg │ │ │ └── other-example.com │ │ │ └── color │ │ │ ├── BUILD.bazel │ │ │ └── color.cue │ ├── extra.ambiguous │ ├── primary.cue │ └── secondary.cue │ ├── hello_world │ ├── BUILD.bazel │ ├── cue.mod │ │ ├── BUILD.bazel │ │ └── module.cue │ ├── de-golden.json │ ├── de.cue │ ├── en-golden.json │ ├── en.cue │ ├── hello_world-golden.json │ ├── hello_world.cue │ ├── lang │ │ ├── BUILD.bazel │ │ ├── de.cue │ │ └── en.cue │ └── message-golden.txt │ ├── injection │ ├── BUILD.bazel │ ├── injection-golden.json │ └── injection.cue │ ├── module │ ├── cue.mod │ │ ├── BUILD.bazel │ │ ├── module.cue │ │ └── pkg │ │ │ └── other-example.com │ │ │ └── translations │ │ │ ├── en │ │ │ ├── BUILD.bazel │ │ │ └── es.cue │ │ │ └── es │ │ │ ├── BUILD.bazel │ │ │ └── en.cue │ └── greetings │ │ ├── BUILD.bazel │ │ ├── greeting.cue │ │ └── lang │ │ └── en │ │ ├── BUILD.bazel │ │ ├── extras │ │ ├── greeting-golden.yaml │ │ ├── greeting.cue │ │ └── greeting_def-golden.cue │ ├── myservice │ ├── BUILD.bazel │ ├── cue.mod │ │ ├── BUILD.bazel │ │ ├── module.cue │ │ └── pkg │ │ │ └── k8s.io │ │ │ ├── api │ │ │ ├── apps │ │ │ │ └── v1 │ │ │ │ │ ├── BUILD.bazel │ │ │ │ │ ├── register_go_gen.cue │ │ │ │ │ └── types_go_gen.cue │ │ │ └── core │ │ │ │ └── v1 │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── annotation_key_constants_go_gen.cue │ │ │ │ ├── doc_go_gen.cue │ │ │ │ ├── register_go_gen.cue │ │ │ │ ├── types_go_gen.cue │ │ │ │ ├── well_known_labels_go_gen.cue │ │ │ │ └── well_known_taints_go_gen.cue │ │ │ └── apimachinery │ │ │ └── pkg │ │ │ ├── api │ │ │ └── resource │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── amount_go_gen.cue │ │ │ │ ├── math_go_gen.cue │ │ │ │ └── quantity_go_gen.cue │ │ │ ├── apis │ │ │ └── meta │ │ │ │ └── v1 │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── duration_go_gen.cue │ │ │ │ ├── group_version_go_gen.cue │ │ │ │ ├── meta_go_gen.cue │ │ │ │ ├── micro_time_go_gen.cue │ │ │ │ ├── register_go_gen.cue │ │ │ │ ├── time_go_gen.cue │ │ │ │ ├── time_proto_go_gen.cue │ │ │ │ ├── types_go_gen.cue │ │ │ │ └── watch_go_gen.cue │ │ │ ├── runtime │ │ │ ├── BUILD.bazel │ │ │ ├── codec_go_gen.cue │ │ │ ├── conversion_go_gen.cue │ │ │ ├── converter_go_gen.cue │ │ │ ├── doc_go_gen.cue │ │ │ ├── helper_go_gen.cue │ │ │ ├── interfaces_go_gen.cue │ │ │ ├── negotiate_go_gen.cue │ │ │ ├── swagger_doc_generator_go_gen.cue │ │ │ ├── types_go_gen.cue │ │ │ └── types_proto_go_gen.cue │ │ │ ├── types │ │ │ ├── BUILD.bazel │ │ │ ├── doc_go_gen.cue │ │ │ ├── namespacedname_go_gen.cue │ │ │ ├── nodename_go_gen.cue │ │ │ ├── patch_go_gen.cue │ │ │ └── uid_go_gen.cue │ │ │ ├── util │ │ │ └── intstr │ │ │ │ ├── BUILD.bazel │ │ │ │ └── intstr_go_gen.cue │ │ │ └── watch │ │ │ ├── BUILD.bazel │ │ │ ├── doc_go_gen.cue │ │ │ ├── filter_go_gen.cue │ │ │ ├── mux_go_gen.cue │ │ │ ├── streamwatcher_go_gen.cue │ │ │ └── watch_go_gen.cue │ ├── environments │ │ ├── BUILD.bazel │ │ ├── dev-golden.json │ │ ├── dev.cue │ │ ├── prod-golden.json │ │ └── prod.cue │ └── my-deployment.cue │ ├── non-cue │ ├── BUILD.bazel │ ├── merge.cue │ ├── name.txt │ └── non-cue-golden.cue │ ├── path │ ├── BUILD.bazel │ ├── multiple-golden.json │ ├── path.cue │ ├── raw.txt │ └── raw_text-golden.json │ └── stamp │ ├── BUILD.bazel │ ├── stamp-golden.json │ └── stamped.cue └── tools ├── cmd └── replace-stamps │ ├── BUILD.bazel │ └── replace.go └── cue └── BUILD.bazel /.bazelversion: -------------------------------------------------------------------------------- 1 | 8.2.1 2 | -------------------------------------------------------------------------------- /.bcr/config.yml: -------------------------------------------------------------------------------- 1 | fixedReleaser: 2 | login: seh 3 | email: seh@panix.com 4 | -------------------------------------------------------------------------------- /.bcr/metadata.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://github.com/seh/rules_cue", 3 | "maintainers": [ 4 | { 5 | "email": "seh@panix.com", 6 | "github": "seh", 7 | "name": "Steven E. Harris" 8 | } 9 | ], 10 | "repository": [ 11 | "github:seh/rules_cue" 12 | ], 13 | "versions": [], 14 | "yanked_versions": {} 15 | } 16 | -------------------------------------------------------------------------------- /.bcr/presubmit.yml: -------------------------------------------------------------------------------- 1 | matrix: &matrix 2 | platform: 3 | - debian10 4 | - macos 5 | - rockylinux8 6 | - ubuntu2004 7 | # Some valid invocations of the "cue" tool can't succeed (e.g. use 8 | # of the "path" and "expression" rules attributes) until we resolve 9 | # or find a reliable workaround for the following issue: 10 | # 11 | # https://github.com/bazelbuild/bazel/issues/17487 12 | # 13 | # Until then, withdraw promised support for using this module on 14 | # Windows. 15 | #- windows 16 | bazel: 17 | - 7.x 18 | - 8.x 19 | tasks: 20 | verify_targets: 21 | name: Verify that all tests succeed 22 | platform: ${{ platform }} 23 | bazel: ${{ bazel }} 24 | test_targets: 25 | - '@rules_cue//test:all' 26 | bcr_test_module: 27 | module_path: examples/bzlmod 28 | matrix: *matrix 29 | tasks: 30 | run_test_module: 31 | name: Run test module 32 | platform: ${{ platform }} 33 | bazel: ${{ bazel }} 34 | build_targets: 35 | - //root 36 | -------------------------------------------------------------------------------- /.bcr/source.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "integrity": "", 3 | "strip_prefix": "{REPO}-{VERSION}", 4 | "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/vcs-archive-{TAG}.tar.gz" 5 | } 6 | -------------------------------------------------------------------------------- /.github/actions/build-test/action.yaml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | description: | 3 | Build all eligible Bazel targets and run all available 4 | tests, confirming that they all pass. 5 | runs: 6 | using: composite 7 | steps: 8 | - name: Cache Bazel-related artifacts 9 | uses: actions/cache@v3 10 | env: 11 | cache-name: bazel-cache 12 | with: 13 | path: | 14 | ~/.cache/bazelisk 15 | ~/.cache/bazel 16 | key: ${{ runner.os }}-${{ env.cache-name }} 17 | - name: Build all Bazel targets 18 | run: | 19 | bazel build \ 20 | //... 21 | shell: bash 22 | - name: Test all Bazel targets 23 | run: | 24 | bazel test \ 25 | --test_output=errors \ 26 | //... 27 | shell: bash 28 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | # See https://docs.github.com/en/actions/reference/events-that-trigger-workflows. 4 | pull_request: 5 | jobs: 6 | bazel-source-inspection: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Check out VCS repository 10 | uses: actions/checkout@v3 11 | - name: Confirm Bazel files is formatted per "buildifier" 12 | uses: thompsonja/bazel-buildifier@v0.4.0 13 | with: 14 | # See https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md. 15 | warnings: -function-docstring,-module-docstring 16 | buildifier_version: 6.1.0 17 | build-test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Check out VCS repository 21 | uses: actions/checkout@v3 22 | - name: Build and test all Bazel targets 23 | uses: ./.github/actions/build-test 24 | -------------------------------------------------------------------------------- /.github/workflows/prepare-release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This program is adapted from "release_prep.sh" in rules_template: 4 | # https://github.com/bazel-contrib/rules-template/blob/main/.github/workflows/release_prep.sh 5 | 6 | set -e -u -o pipefail 7 | 8 | ruleset_name="$1" 9 | shift 10 | if [ -z "${ruleset_name}" ]; then 11 | echo >&2 'Bazel ruleset name must not be empty.' 12 | exit 1 13 | fi 14 | 15 | if (( $# < 1 )); then 16 | git_tag="${GITHUB_REF_NAME:-}" 17 | else 18 | git_tag="$1" 19 | shift 20 | fi 21 | if [ -z "${git_tag}" ]; then 22 | echo >&2 'Git tag to use for release must not be empty.' 23 | exit 1 24 | fi 25 | 26 | # The prefix is chosen to match what GitHub generates for its own 27 | # source archives. 28 | git archive \ 29 | --format=tar \ 30 | --prefix="${ruleset_name}-${git_tag:1}/" \ 31 | "${git_tag}" \ 32 | | gzip > "vcs-archive-${git_tag}.tar.gz" 33 | 34 | tag_annotation_subject=$(git tag --list --format='%(contents:subject)' "${git_tag}") 35 | if [ -n "${tag_annotation_subject}" ]; then 36 | echo "# ${tag_annotation_subject}" 37 | tag_annotation_body=$(git tag --list --format='%(contents:body)' "${git_tag}") 38 | if [ -n "${tag_annotation_body}" ]; then 39 | echo " 40 | ${tag_annotation_body} 41 | " 42 | fi 43 | echo "# Details" 44 | fi 45 | 46 | cat << EOF 47 | ## Using modules with Bazel 6 and later 48 | 49 | 1. Enable support for modules in your _.bazelrc_ file by adding the following line: 50 | \`common --enable_bzlmod\` 51 | 1. Add the following directive to your _MODULE.bazel_ file: 52 | \`\`\`starlark 53 | bazel_dep(name = "${ruleset_name}", version = "${git_tag:1}") 54 | \`\`\` 55 | EOF 56 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | # See https://docs.github.com/en/actions/reference/events-that-trigger-workflows. 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check out VCS repository 12 | uses: actions/checkout@v3 13 | - name: Build and test all Bazel targets 14 | uses: ./.github/actions/build-test 15 | # Work around lack of the annotated tag for the motivating tag 16 | # being present unless we fetch at depth zero (meaning fetch all 17 | # history, with no depth restriction). 18 | # 19 | # Pertinent issues and PRs: 20 | # https://github.com/actions/checkout/issues/338 21 | # https://github.com/actions/checkout/issues/448 22 | # https://github.com/actions/checkout/issues/701 23 | # https://github.com/actions/checkout/pull/579 24 | # 25 | # Basis of inspiration: 26 | # https://github.com/actions/checkout/issues/701#issuecomment-1139627817 27 | # https://stackoverflow.com/a/54635270 28 | - name: Fetch annotated Git tag 29 | run: | 30 | git fetch origin \ 31 | --no-tags \ 32 | +refs/tags/${{ github.ref_name }}:refs/tags/${{ github.ref_name }} 33 | - name: Prepare release notes and artifacts 34 | run: | 35 | gh_repository='${{ github.repository }}' 36 | tag_name='${{ github.ref_name }}' 37 | .github/workflows/prepare-release \ 38 | "${gh_repository#*/}" \ 39 | "${tag_name}" \ 40 | > release-notes.txt 41 | - name: Issue release 42 | uses: softprops/action-gh-release@v1 43 | with: 44 | generate_release_notes: true 45 | body_path: release-notes.txt 46 | files: vcs-archive-*.tar.gz 47 | fail_on_unmatched_files: true 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bazel-bin 2 | /bazel-out 3 | /bazel-rules_cue 4 | /bazel-testlogs 5 | 6 | *~ -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "@gazelle//:def.bzl", 3 | "gazelle", 4 | ) 5 | 6 | # gazelle:prefix github.com/seh/rules_cue 7 | gazelle( 8 | name = "gazelle", 9 | ) 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | module( 2 | name = "rules_cue", 3 | version = "0.0.0", 4 | ) 5 | 6 | bazel_dep(name = "bazel_skylib", version = "1.7.1") 7 | bazel_dep(name = "platforms", version = "0.0.11") 8 | bazel_dep(name = "rules_go", version = "0.54.0") 9 | 10 | go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") 11 | go_sdk.download( 12 | version = "1.24.2", 13 | ) 14 | 15 | bazel_dep(name = "rules_shell", version = "0.4.0") 16 | 17 | cue = use_extension("//cue:extensions.bzl", "cue") 18 | use_repo( 19 | cue, 20 | "cue_tool_toolchains", 21 | ) 22 | 23 | register_toolchains("@cue_tool_toolchains//:all") 24 | 25 | bazel_dep(name = "gazelle", version = "0.43.0", dev_dependency = True) 26 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. role:: tool(emphasis) 2 | 3 | ================================= 4 | CUE rules for Bazel 5 | ================================= 6 | 7 | .. External links 8 | .. See https://stackoverflow.com/a/4836544/31818 for this abomination: 9 | .. |the cue tool| replace:: the :tool:`cue` tool 10 | .. _the cue tool: 11 | .. _cue: https://cuelang.org/docs/ 12 | 13 | Integrate |the cue tool|_ into your Bazel probjects to define data in CUE and export it in various formats. 14 | 15 | .. contents:: :depth: 2 16 | 17 | ----- 18 | 19 | Overview 20 | ======== 21 | 22 | **TODO:** Write remaining documentation. 23 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | workspace(name = "com_github_seh_rules_cue") 2 | -------------------------------------------------------------------------------- /cue/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue/private:config.bzl", 3 | "cue_config", 4 | ) 5 | 6 | exports_files([ 7 | "cue-run-from-archived-runfiles", 8 | "cue-run-from-runfiles", 9 | ]) 10 | 11 | cue_config( 12 | name = "cue_config", 13 | stamp = select({ 14 | "//cue/private:stamp": True, 15 | "//conditions:default": False, 16 | }), 17 | visibility = ["//visibility:public"], 18 | ) 19 | -------------------------------------------------------------------------------- /cue/cue-run-from-archived-runfiles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copy-pasted from the Bazel Bash runfiles library v2. 4 | set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash 5 | source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ 6 | source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ 7 | source "$0.runfiles/$f" 2>/dev/null || \ 8 | source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 9 | source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 10 | { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e 11 | 12 | function usage() { 13 | printf "usage: %s [-i instance_path] [-m module_file] [-p package_name] cue_tool cue_subcommand extra_args_file packageless_files_file output_file [args...]\n" "$(basename "${0}")" 1>&2 14 | exit 2 15 | } 16 | 17 | instance_path= 18 | module_file= 19 | package_name= 20 | 21 | function parse_args() { 22 | while getopts i:m:p: name 23 | do 24 | case "${name}" in 25 | i) instance_path="${OPTARG}";; 26 | h) usage;; 27 | m) module_file="${OPTARG}";; 28 | p) package_name="${OPTARG}";; 29 | ?) usage;; 30 | esac 31 | done 32 | if [ -n "${instance_path}" ] && [ -z "${module_file}" ]; then 33 | printf "%s: specifying a CUE instance path requires specifying a module path\n" "$(basename "${0}")" 1>&2 34 | exit 1 35 | fi 36 | if [ -n "${package_name}" ] && [ -z "${instance_path}" ]; then 37 | printf "%s: specifying a CUE package name requires specifying an instance path\n" "$(basename "${0}")" 1>&2 38 | exit 1 39 | fi 40 | } 41 | 42 | parse_args "${@}" 43 | shift $((OPTIND - 1)) 44 | 45 | cue=$1; shift 46 | subcommand=$1; shift 47 | extra_args_file=$1; shift 48 | packageless_files_file=$1; shift 49 | output_file=$1; shift 50 | 51 | # Try to find an executable file with a Windows-style name first. 52 | zipper="$(rlocation bazel_tools/tools/zip/zipper/zipper.exe)" 53 | if [ -z "${zipper}" ]; then 54 | zipper="$(rlocation bazel_tools/tools/zip/zipper/zipper)" 55 | if [ -z "${zipper}" ]; then 56 | echo >&2 "Failed to locate the required \"zipper\" tool as a runfile." 57 | exit 1 58 | fi 59 | fi 60 | 61 | zip_manifest_file="$(mktemp zip-manifest-XXXXXX)" 62 | sed -E -e 's/^([^ ]+) /\1=/' "${RUNFILES_MANIFEST_FILE}" > "${zip_manifest_file}" 63 | runfiles_archive_file="$(mktemp runfiles-archive-XXXXXX)" 64 | "${zipper}" c "${runfiles_archive_file}" "@${zip_manifest_file}" 65 | 66 | cue_src_dir="$(mktemp -d cue-src-XXXXXX)" 67 | "${zipper}" x "${runfiles_archive_file}" -d "${cue_src_dir}" 68 | 69 | oldwd="${PWD}" 70 | packageless_file_args=() 71 | qualifier= 72 | while read -r line; do 73 | if [ -z "${line}" ]; then 74 | continue 75 | fi 76 | if [[ "${line}" =~ .+:$ ]]; then 77 | qualifier="${line}" 78 | else 79 | if [ -n "${qualifier}" ]; then 80 | packageless_file_args+=("${qualifier}") 81 | qualifier= 82 | fi 83 | packageless_file_args+=("${oldwd}/${cue_src_dir}/${line}") 84 | fi 85 | done < "${packageless_files_file}" 86 | if [ -n "${qualifier}" ]; then 87 | echo >&2 "No file path followed qualifier \"${qualifier}\"." 88 | exit 1 89 | fi 90 | 91 | # NB: If we don't assign to packageless_file_args above in the loop, 92 | # it winds up being undefined afterward. 93 | if (( ${#packageless_file_args[@]} == 0 )); then 94 | packageless_file_args=() 95 | fi 96 | 97 | cd "${cue_src_dir}" 98 | 99 | if [ -n "${module_file}" ]; then 100 | module_path="$(dirname $(dirname ${module_file}))" 101 | cd "${module_path}" 102 | fi 103 | 104 | # NB: See https://stackoverflow.com/questions/7577052 for the odd 105 | # treatment of the "packageless_file_args" array variable here, 106 | # handling the case where the array winds up empty for lack of 107 | # so-called "packageless files" being used as input. As we are 108 | # uncertain of which Bash we'll wind up using, aim to work around as 109 | # many of their mutually exclusive defects as possible. 110 | "${oldwd}/${cue}" "${subcommand}" --outfile "${oldwd}/${output_file}" \ 111 | ${instance_path}${package_name:+:${package_name}} \ 112 | ${packageless_file_args[@]+"${packageless_file_args[@]}"} \ 113 | $(< "${oldwd}/${extra_args_file}") \ 114 | "${@-}" 115 | -------------------------------------------------------------------------------- /cue/cue-run-from-runfiles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copy-pasted from the Bazel Bash runfiles library v2. 4 | set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash 5 | source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ 6 | source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ 7 | source "$0.runfiles/$f" 2>/dev/null || \ 8 | source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 9 | source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 10 | { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e 11 | 12 | function usage() { 13 | printf "usage: %s [-c cue_cache_directory] [-i instance_path] [-m module_file] [-p package_name] cue_tool cue_subcommand extra_args_file packageless_files_file output_file [args...]\n" "$(basename "${0}")" 1>&2 14 | exit 2 15 | } 16 | 17 | cue_cache_directory= 18 | instance_path= 19 | module_file= 20 | package_name= 21 | 22 | function parse_args() { 23 | while getopts c:i:m:p: name 24 | do 25 | case "${name}" in 26 | c) cue_cache_directory="${OPTARG}";; 27 | i) instance_path="${OPTARG}";; 28 | h) usage;; 29 | m) module_file="${OPTARG}";; 30 | p) package_name="${OPTARG}";; 31 | ?) usage;; 32 | esac 33 | done 34 | if [ -n "${cue_cache_directory}" ] && [ ! -d "${cue_cache_directory}" ]; then 35 | printf "%s: specified CUE cache directory \"%s\" does not exist\n" "$(basename "${0}" "${cue_cache_directory}")" 1>&2 36 | exit 1 37 | fi 38 | if [ -n "${instance_path}" ] && [ -z "${module_file}" ]; then 39 | printf "%s: specifying a CUE instance path requires specifying a module path\n" "$(basename "${0}")" 1>&2 40 | exit 1 41 | fi 42 | if [ -n "${package_name}" ] && [ -z "${instance_path}" ]; then 43 | printf "%s: specifying a CUE package name requires specifying an instance path\n" "$(basename "${0}")" 1>&2 44 | exit 1 45 | fi 46 | } 47 | 48 | parse_args "${@}" 49 | shift $((OPTIND - 1)) 50 | 51 | cue=$1; shift 52 | subcommand=$1; shift 53 | extra_args_file=$1; shift 54 | packageless_files_file=$1; shift 55 | output_file=$1; shift 56 | 57 | oldwd="${PWD}" 58 | packageless_file_args=() 59 | qualifier= 60 | while read -r line; do 61 | if [ -z "${line}" ]; then 62 | continue 63 | fi 64 | if [[ "${line}" =~ .+:$ ]]; then 65 | qualifier="${line}" 66 | else 67 | if [ -n "${qualifier}" ]; then 68 | packageless_file_args+=("${qualifier}") 69 | qualifier= 70 | fi 71 | packageless_file_args+=("${oldwd}/$(rlocation ${line})") 72 | fi 73 | done < "${packageless_files_file}" 74 | if [ -n "${qualifier}" ]; then 75 | echo >&2 "No file path followed qualifier \"${qualifier}\"." 76 | exit 1 77 | fi 78 | 79 | # NB: If we don't assign to packageless_file_args above in the loop, 80 | # it winds up being undefined afterward. 81 | if (( ${#packageless_file_args[@]} == 0 )); then 82 | packageless_file_args=() 83 | fi 84 | 85 | if [ -n "${module_file}" ]; then 86 | adjusted_module_file="$(rlocation ${module_file})" 87 | if [ -z "${adjusted_module_file}" ]; then 88 | echo >&2 "No runfile path available for module file path \"${module_file}\"." 89 | exit 1 90 | fi 91 | module_path="$(dirname $(dirname ${adjusted_module_file}))" 92 | cd "${module_path}" 93 | fi 94 | 95 | # Since we don't have access to the "HOME" environment variable here 96 | # (barring use of the "--action-env" command-line flag), we must tell 97 | # CUE where to store its cache explicitly. 98 | if [ -n "${cue_cache_directory}" ]; then 99 | export CUE_CACHE_DIR="${cue_cache_directory}" 100 | elif [ -z "${HOME:-}" ]; then 101 | cue_cache_directory="$(mktemp -d -t cue-XXXXXX)" 102 | trap "rm -rf '${cue_cache_directory}'" EXIT 103 | export CUE_CACHE_DIR="${cue_cache_directory}" 104 | fi 105 | 106 | # NB: See https://stackoverflow.com/questions/7577052 for the odd 107 | # treatment of the "packageless_file_args" array variable here, 108 | # handling the case where the array winds up empty for lack of 109 | # so-called "packageless files" being used as input. As we are 110 | # uncertain of which Bash we'll wind up using, aim to work around as 111 | # many of their mutually exclusive defects as possible. 112 | "${oldwd}/${cue}" "${subcommand}" --outfile "${oldwd}/${output_file}" \ 113 | ${instance_path}${package_name:+:${package_name}} \ 114 | ${packageless_file_args[@]+"${packageless_file_args[@]}"} \ 115 | $(< "${oldwd}/${extra_args_file}") \ 116 | "${@-}" 117 | -------------------------------------------------------------------------------- /cue/cue.bzl: -------------------------------------------------------------------------------- 1 | load( 2 | "@bazel_skylib//lib:paths.bzl", 3 | "paths", 4 | ) 5 | load( 6 | "@rules_shell//shell:sh_binary.bzl", 7 | "sh_binary", 8 | ) 9 | load( 10 | "//cue/private:config.bzl", 11 | "CUEConfigInfo", 12 | ) 13 | load( 14 | "//cue/private:future.bzl", 15 | _runfile_path = "runfile_path", 16 | ) 17 | 18 | CUEModuleInfo = provider( 19 | doc = "Collects files from cue_module targets for use by referring cue_instance targets.", 20 | fields = { 21 | "module_file": """The "module.cue" file in the module directory.""", 22 | # TODO(seh): Consider abandoning this field in favor of using cue_instance for these. 23 | "external_package_sources": "The set of files in this CUE module defining external packages.", 24 | }, 25 | ) 26 | 27 | CUEInstanceInfo = provider( 28 | doc = "Collects files and references from cue_instance targets for use in downstream consuming targets.", 29 | fields = { 30 | "directory_path": """Directory path (a "short path") to the CUE instance.""", 31 | "files": "The CUE files defining this instance.", 32 | "module": "The CUE module within which this instance sits.", 33 | "package_name": "Name of the CUE package to load for this instance.", 34 | "transitive_files": "The set of files (including other instances) referenced by this instance.", 35 | }, 36 | ) 37 | 38 | def _replacer_if_stamping(stamping_policy): 39 | # NB: We can't access the "_cue_config" attribute here. 40 | return Label("//tools/cmd/replace-stamps") if stamping_policy != "Prevent" else None 41 | 42 | def _add_common_source_consuming_attrs_to(attrs): 43 | attrs.update({ 44 | "qualified_srcs": attr.label_keyed_string_dict( 45 | doc = """Additional input files that are not part of a CUE package, each together with a qualifier. 46 | 47 | The qualifier overrides CUE's normal guessing at a file's type from 48 | its file extension. Specify it here without the trailing colon 49 | character.""", 50 | allow_files = True, 51 | ), 52 | "srcs": attr.label_list( 53 | doc = "Additional input files that are not part of a CUE package.", 54 | allow_files = True, 55 | ), 56 | }) 57 | return attrs 58 | 59 | def _add_common_output_producing_attrs_to(attrs): 60 | attrs = _add_common_source_consuming_attrs_to(attrs) 61 | attrs.update({ 62 | "_cue_config": attr.label( 63 | default = "//cue:cue_config", 64 | ), 65 | "_replacer": attr.label( 66 | default = _replacer_if_stamping, 67 | executable = True, 68 | allow_single_file = True, 69 | cfg = "exec", 70 | ), 71 | "concatenate_objects": attr.bool( 72 | doc = "Concatenate multiple objects into a list.", 73 | ), 74 | # Unfortunately, we can't use a private attribute for an 75 | # implicit dependency here, because we can't fix the default 76 | # label value. 77 | "cue_run": attr.label( 78 | executable = True, 79 | allow_files = True, 80 | cfg = "exec", 81 | mandatory = True, 82 | ), 83 | "expression": attr.string( 84 | doc = "CUE expression selecting a single value to export.", 85 | default = "", 86 | ), 87 | "inject": attr.string_dict( 88 | doc = "Keys and values of tagged fields.", 89 | ), 90 | "inject_shorthand": attr.string_list( 91 | doc = "Shorthand values of tagged fields.", 92 | ), 93 | "inject_system_variables": attr.bool( 94 | doc = "Whether to inject the predefined set of system variables into tagged fields", 95 | ), 96 | "merge_other_files": attr.bool( 97 | doc = "Merge non-CUE files.", 98 | default = True, 99 | ), 100 | "non_cue_file_package_name": attr.string( 101 | doc = """Name of the CUE package within which to merge non-CUE files. 102 | 103 | Deprecated: 104 | Use "output_package_name" instead.""", 105 | ), 106 | "output_package_name": attr.string( 107 | doc = "Name of the CUE package within which to generate CUE output.", 108 | ), 109 | "path": attr.string_list( 110 | doc = """Elements of CUE path at which to place top-level values. 111 | Each entry for an element may nominate either a CUE field, ending with 112 | either ":" for a regular fiield or "::" for a definition, or a CUE 113 | expression, both variants evaluated within the value, unless 114 | "with_context" is true.""", 115 | ), 116 | "stamping_policy": attr.string( 117 | doc = """Whether to stamp tagged field values before injection. 118 | 119 | If "Allow," stamp tagged field values only when the "--stamp" flag is 120 | active. 121 | 122 | If "Force," stamp tagged field values unconditionally, even if the 123 | "--stamp" flag is inactive. 124 | 125 | If "Prevent," never stamp tagged field values, even if the "--stamp" 126 | flag is active.""", 127 | values = [ 128 | "Allow", 129 | "Force", 130 | "Prevent", 131 | ], 132 | default = "Allow", 133 | ), 134 | "with_context": attr.bool( 135 | doc = """Evaluate "path" elements within a struct of contextual data. 136 | Instead of evaluating these elements in the context of the value being 137 | situated, instead evaluate them within a struct identifying the source 138 | data, file name, record index, and record count.""", 139 | ), 140 | }) 141 | return attrs 142 | 143 | def _add_common_module_based_attrs_to(attrs): 144 | attrs = _add_common_output_producing_attrs_to(attrs) 145 | attrs.update({ 146 | "deps": attr.label_list( 147 | doc = """cue_instance targets to include in the evaluation. 148 | 149 | These instances are those mentioned in import declarations in this set 150 | of CUE files.""", 151 | providers = [CUEInstanceInfo], 152 | ), 153 | "module": attr.label( 154 | doc = """CUE module within which these files sit. 155 | 156 | This value must refer either to a target using the cue_module rule or 157 | another rule that yields a CUEModuleInfo provider.""", 158 | providers = [CUEModuleInfo], 159 | mandatory = True, 160 | ), 161 | }) 162 | return attrs 163 | 164 | def _add_common_instance_consuming_attrs_to(attrs): 165 | attrs = _add_common_output_producing_attrs_to(attrs) 166 | attrs.update({ 167 | "instance": attr.label( 168 | doc = """CUE instance to export. 169 | 170 | This value must refer either to a target using the cue_instance rule 171 | or another rule that yields a CUEInstanceInfo provider.""", 172 | providers = [CUEInstanceInfo], 173 | mandatory = True, 174 | ), 175 | }) 176 | return attrs 177 | 178 | def _file_from_label_keyed_string_dict_key(k): 179 | # NB: The Targets in a label_keyed_string_dict attribute have the key's 180 | # source file in a depset, as opposed being represented directly as in a 181 | # label_list attribute. 182 | files = k.files.to_list() 183 | if len(files) != 1: 184 | fail(msg = "Unexpected number of files in target {}: {}".format(k, len(files))) 185 | return files[0] 186 | 187 | def _collect_packageless_file_path(ctx, file, lines): 188 | p = _runfile_path(ctx, file) 189 | if p.find(":") != -1: 190 | fail(msg = "CUE rejects file paths that contain a colon (:): {}".format(p)) 191 | lines.append(p + "\n") 192 | 193 | def _add_common_output_producing_args_to(ctx, args, stamped_args_file, packageless_files_file): 194 | cue_config = ctx.attr._cue_config[CUEConfigInfo] 195 | stamping_enabled = ctx.attr.stamping_policy == "Force" or ctx.attr.stamping_policy == "Allow" and cue_config.stamp 196 | required_stamp_bindings = {} 197 | 198 | if ctx.attr.expression: 199 | args.add("--expression", ctx.attr.expression) 200 | for k, v in ctx.attr.inject.items(): 201 | if len(k) == 0: 202 | fail(msg = "injected key must not empty") 203 | if stamping_enabled and v.startswith("{") and v.endswith("}"): 204 | required_stamp_bindings[k] = v[1:-1] 205 | continue 206 | args.add( 207 | "--inject", 208 | # Allow the empty string as a specified value. 209 | "{}={}".format(k, v), 210 | ) 211 | for v in ctx.attr.inject_shorthand: 212 | if len(v) == 0: 213 | fail(msg = "injected value must not empty") 214 | args.add("--inject", v) 215 | if ctx.attr.concatenate_objects: 216 | args.add("--list") 217 | if ctx.attr.inject_system_variables: 218 | args.add("--inject-vars") 219 | if not ctx.attr.merge_other_files: 220 | args.add("--merge=false") 221 | if ctx.attr.output_package_name: 222 | args.add("--package", ctx.attr.output_package_name) 223 | elif ctx.attr.non_cue_file_package_name: # TODO(seh): Remove this after deprecation period. 224 | args.add("--package", ctx.attr.non_cue_file_package_name) 225 | for p in ctx.attr.path: 226 | if not p: 227 | fail(msg = "path element must not be empty") 228 | args.add("--path", p) 229 | if ctx.attr.with_context: 230 | args.add("--with-context") 231 | 232 | if len(required_stamp_bindings) == 0: 233 | # Create an empty file, in order to unify the command that 234 | # consumes extra arguments when stamping is in effect. 235 | ctx.actions.write( 236 | stamped_args_file, 237 | "", 238 | ) 239 | else: 240 | stamp_placeholders_file = ctx.actions.declare_file("{}-stamp-bindings".format(ctx.label.name)) 241 | ctx.actions.write( 242 | stamp_placeholders_file, 243 | "\n".join([k + "=" + v for k, v in required_stamp_bindings.items()]), 244 | ) 245 | args = ctx.actions.args() 246 | args.add("-prefix", "--inject ") 247 | args.add("-output", stamped_args_file.path) 248 | args.add(stamp_placeholders_file.path) 249 | args.add(ctx.info_file.path) # stable-status.txt 250 | args.add(ctx.version_file.path) # volatile-status.txt 251 | ctx.actions.run( 252 | executable = ctx.executable._replacer, 253 | arguments = [args], 254 | inputs = [ 255 | stamp_placeholders_file, 256 | ctx.info_file, 257 | ctx.version_file, 258 | ], 259 | outputs = [stamped_args_file], 260 | mnemonic = "CUEReplaceStampBindings", 261 | progress_message = "Replacing injection placeholders with stamped values for {}".format(ctx.label.name), 262 | ) 263 | 264 | lines = [] 265 | srcs = list(ctx.files.srcs) 266 | for src in srcs: 267 | _collect_packageless_file_path(ctx, src, lines) 268 | for k, v in ctx.attr.qualified_srcs.items(): 269 | file = _file_from_label_keyed_string_dict_key(k) 270 | if file in srcs: 271 | srcs.remove(file) 272 | if not v: 273 | _collect_packageless_file_path(ctx, file, lines) 274 | continue 275 | lines.append(v + ":") 276 | _collect_packageless_file_path(ctx, file, lines) 277 | ctx.actions.write( 278 | packageless_files_file, 279 | "\n".join(lines), 280 | ) 281 | 282 | def _cue_module_impl(ctx): 283 | module_file = ctx.file.file 284 | expected_module_file = "module.cue" 285 | if module_file.basename != expected_module_file: 286 | fail(msg = """supplied CUE module file is not named "{}"; got "{}" instead""".format(expected_module_file, module_file.basename)) 287 | expected_module_directory = "cue.mod" 288 | 289 | directory = paths.basename(module_file.dirname) 290 | if directory != expected_module_directory: 291 | fail(msg = """supplied CUE module directory is not named "{}"; got "{}" instead""".format(expected_module_directory, directory)) 292 | return [ 293 | CUEModuleInfo( 294 | module_file = module_file, 295 | external_package_sources = depset( 296 | direct = ctx.files.srcs, 297 | ), 298 | ), 299 | ] 300 | 301 | _cue_module = rule( 302 | implementation = _cue_module_impl, 303 | attrs = { 304 | "file": attr.label( 305 | doc = "module.cue file for this CUE module.", 306 | allow_single_file = [".cue"], 307 | mandatory = True, 308 | ), 309 | "srcs": attr.label_list( 310 | doc = """Source files defining external packages from the "gen," "pkg," and "usr" directories.""", 311 | allow_files = [".cue"], 312 | ), 313 | }, 314 | ) 315 | 316 | def cue_module(name = "cue.mod", **kwargs): 317 | file = kwargs.pop("file", "module.cue") 318 | 319 | _cue_module( 320 | name = name, 321 | file = file, 322 | **kwargs 323 | ) 324 | 325 | def _cue_module_root_directory_path(ctx, module): 326 | return paths.dirname(paths.dirname(_runfile_path(ctx, module.module_file))) 327 | 328 | def _cue_instance_directory_path(ctx): 329 | if ctx.file.directory_of: 330 | f = ctx.file.directory_of 331 | runfile_path = _runfile_path(ctx, f) 332 | return runfile_path if f.is_directory else paths.dirname(runfile_path) 333 | return paths.dirname(_runfile_path(ctx, ctx.files.srcs[0])) 334 | 335 | def _cue_instance_impl(ctx): 336 | ancestor_instance = None 337 | if CUEModuleInfo in ctx.attr.ancestor: 338 | module = ctx.attr.ancestor[CUEModuleInfo] 339 | else: 340 | ancestor_instance = ctx.attr.ancestor[CUEInstanceInfo] 341 | module = ancestor_instance.module 342 | for dep in ctx.attr.deps: 343 | instance = dep[CUEInstanceInfo] 344 | if instance.module != module: 345 | fail(msg = """dependency {} of instance {} is not part of CUE module "{}"; got "{}" instead""".format(dep, ctx.label, module, dep.module)) 346 | 347 | instance_directory_path = _cue_instance_directory_path(ctx) 348 | module_root_directory = _cue_module_root_directory_path(ctx, module) 349 | if not (instance_directory_path == module_root_directory or 350 | # The CUE module may be at the root of the Bazel workspace. 351 | not module_root_directory or 352 | instance_directory_path.startswith(module_root_directory + "/")): 353 | fail(msg = "directory {} for instance {} is not dominated by the module root directory {}".format( 354 | instance_directory_path, 355 | ctx.label, 356 | module_root_directory, 357 | )) 358 | return [ 359 | CUEInstanceInfo( 360 | directory_path = instance_directory_path, 361 | files = ctx.files.srcs, 362 | module = module, 363 | package_name = ctx.attr.package_name or paths.basename(instance_directory_path), 364 | transitive_files = depset( 365 | direct = ctx.files.srcs + 366 | ([module.module_file] if not ancestor_instance else []), 367 | transitive = [instance.transitive_files for instance in ( 368 | [dep[CUEInstanceInfo] for dep in ctx.attr.deps] + 369 | ([ancestor_instance] if ancestor_instance else []) 370 | )] + 371 | ([module.external_package_sources] if not ancestor_instance else []), 372 | ), 373 | ), 374 | ] 375 | 376 | cue_instance = rule( 377 | implementation = _cue_instance_impl, 378 | attrs = { 379 | "ancestor": attr.label( 380 | doc = """Containing CUE instance or module root. 381 | 382 | This value must refer either to a dominating target using the 383 | cue_instance rule (or another rule that yields a CUEInstanceInfo 384 | provider) or a dominating target using the cue_module rule (or another 385 | rule that yields a CUEModuleInfo provider). 386 | """, 387 | providers = [[CUEInstanceInfo], [CUEModuleInfo]], 388 | mandatory = True, 389 | ), 390 | "deps": attr.label_list( 391 | doc = """cue_instance targets to include in the evaluation. 392 | 393 | These instances are those mentioned in import declarations in this 394 | instance's CUE files.""", 395 | providers = [CUEInstanceInfo], 396 | ), 397 | "directory_of": attr.label( 398 | doc = """Directory designator to use as the instance directory. 399 | 400 | If the given target is a directory, use that directly. If the given 401 | target is a file, use the file's containing directory. 402 | 403 | If left unspecified, use the directory containing the first CUE file 404 | nominated in this cue_instance's "srcs" attribute.""", 405 | allow_single_file = True, 406 | ), 407 | "package_name": attr.string( 408 | doc = """Name of the CUE package to load for this instance. 409 | 410 | If left unspecified, use the basename of the containing directory as 411 | the CUE pacakge name.""", 412 | ), 413 | "srcs": attr.label_list( 414 | doc = "CUE input files that are part of the nominated CUE package.", 415 | mandatory = True, 416 | allow_empty = False, 417 | allow_files = [".cue"], 418 | ), 419 | }, 420 | ) 421 | 422 | def _call_rule_after(name, rule_fn, prepare_fns = [], **kwargs): 423 | for f in prepare_fns: 424 | kwargs = f( 425 | name = name, 426 | **kwargs 427 | ) 428 | rule_fn( 429 | name = name, 430 | **kwargs 431 | ) 432 | 433 | def _collect_direct_file_sources(ctx): 434 | files = list(ctx.files.srcs) 435 | for k, _ in ctx.attr.qualified_srcs.items(): 436 | file = _file_from_label_keyed_string_dict_key(k) 437 | if file not in files: 438 | files.append(file) 439 | return files 440 | 441 | def _cue_standalone_runfiles_impl(ctx): 442 | return [ 443 | DefaultInfo(runfiles = ctx.runfiles( 444 | files = _collect_direct_file_sources(ctx), 445 | )), 446 | ] 447 | 448 | _cue_standalone_runfiles = rule( 449 | implementation = _cue_standalone_runfiles_impl, 450 | attrs = _add_common_source_consuming_attrs_to({}), 451 | ) 452 | 453 | def _cue_module_runfiles_impl(ctx): 454 | module = ctx.attr.module[CUEModuleInfo] 455 | return [ 456 | DefaultInfo(runfiles = ctx.runfiles( 457 | files = [module.module_file] + 458 | _collect_direct_file_sources(ctx), 459 | transitive_files = depset( 460 | transitive = [module.external_package_sources] + 461 | [dep[CUEInstanceInfo].transitive_files for dep in ctx.attr.deps], 462 | ), 463 | )), 464 | ] 465 | 466 | _cue_module_runfiles = rule( 467 | implementation = _cue_module_runfiles_impl, 468 | attrs = _add_common_source_consuming_attrs_to({ 469 | "deps": attr.label_list( 470 | doc = """cue_instance targets to include in the evaluation. 471 | 472 | These instances are those mentioned in import declarations in this set 473 | of CUE files.""", 474 | providers = [CUEInstanceInfo], 475 | ), 476 | "module": attr.label( 477 | doc = """CUE module within which these files sit. 478 | 479 | This value must refer either to a target using the cue_module rule or 480 | another rule that yields a CUEModuleInfo provider.""", 481 | providers = [CUEModuleInfo], 482 | mandatory = True, 483 | ), 484 | }), 485 | ) 486 | 487 | def _cue_instance_runfiles_impl(ctx): 488 | instance = ctx.attr.instance[CUEInstanceInfo] 489 | return [ 490 | DefaultInfo(runfiles = ctx.runfiles( 491 | files = _collect_direct_file_sources(ctx), 492 | transitive_files = depset( 493 | transitive = [instance.transitive_files], 494 | ), 495 | )), 496 | ] 497 | 498 | _cue_instance_runfiles = rule( 499 | implementation = _cue_instance_runfiles_impl, 500 | attrs = _add_common_source_consuming_attrs_to({ 501 | "instance": attr.label( 502 | doc = """CUE instance to export. 503 | 504 | This value must refer either to a target using the cue_instance rule 505 | or another rule that yields a CUEInstanceInfo provider.""", 506 | providers = [CUEInstanceInfo], 507 | mandatory = True, 508 | ), 509 | }), 510 | ) 511 | 512 | _cue_toolchain_type = "//tools/cue:toolchain_type" 513 | 514 | # TODO(seh): Consider feeding an argument for the 515 | # "cue_cache_directory_path" parameter here by way of new rule 516 | # attributes. 517 | def _make_output_producing_action(ctx, cue_subcommand, mnemonic, description, augment_args = None, module_file = None, instance_directory_path = None, instance_package_name = None, cue_cache_directory_path = None): 518 | cue_tool = ctx.toolchains[_cue_toolchain_type].cueinfo.tool 519 | args = ctx.actions.args() 520 | if module_file: 521 | args.add("-m", _runfile_path(ctx, module_file)) 522 | if instance_directory_path: 523 | args.add("-i", instance_directory_path) 524 | if instance_package_name: 525 | args.add("-p", instance_package_name) 526 | elif instance_directory_path: 527 | fail(msg = "CUE instance directory path provided without a module directory path") 528 | elif instance_package_name: 529 | fail(msg = "CUE package name provided without an instance directory path") 530 | if cue_cache_directory_path: 531 | args.add("-c", cue_cache_directory_path) 532 | args.add(cue_tool.path) 533 | args.add(cue_subcommand) 534 | stamped_args_file = ctx.actions.declare_file("%s-stamped-args" % ctx.label.name) 535 | args.add(stamped_args_file.path) 536 | packageless_files_file = ctx.actions.declare_file("%s-packageless-files" % ctx.label.name) 537 | args.add(packageless_files_file.path) 538 | args.add(ctx.outputs.result.path) 539 | _add_common_output_producing_args_to(ctx, args, stamped_args_file, packageless_files_file) 540 | 541 | if augment_args: 542 | augment_args(ctx, args) 543 | 544 | ctx.actions.run( 545 | executable = ctx.executable.cue_run, 546 | arguments = [args], 547 | inputs = [ 548 | stamped_args_file, 549 | packageless_files_file, 550 | ], 551 | tools = [cue_tool], 552 | outputs = [ctx.outputs.result], 553 | mnemonic = mnemonic, 554 | progress_message = "Capturing the {} CUE configuration for target \"{}\"".format(description, ctx.label.name), 555 | ) 556 | 557 | def _make_module_based_output_producing_action(ctx, cue_subcommand, mnemonic, description, augment_args = None): 558 | module = ctx.attr.module[CUEModuleInfo] 559 | _make_output_producing_action( 560 | ctx, 561 | cue_subcommand, 562 | mnemonic, 563 | description, 564 | augment_args, 565 | module.module_file, 566 | ) 567 | 568 | def _make_instance_consuming_action(ctx, cue_subcommand, mnemonic, description, augment_args = None): 569 | instance = ctx.attr.instance[CUEInstanceInfo] 570 | 571 | # NB: If the input path is equal to the starting path, the 572 | # "paths.relativize" function returns the input path unchanged, as 573 | # opposed to returning "." to indicate that it's the same 574 | # directory. 575 | module_root_directory = _cue_module_root_directory_path(ctx, instance.module) 576 | relative_instance_path = paths.relativize(instance.directory_path, module_root_directory) 577 | if relative_instance_path == instance.directory_path: 578 | relative_instance_path = "." 579 | else: 580 | relative_instance_path = "./" + relative_instance_path 581 | 582 | _make_output_producing_action( 583 | ctx, 584 | cue_subcommand, 585 | mnemonic, 586 | description, 587 | augment_args, 588 | instance.module.module_file, 589 | relative_instance_path, 590 | instance.package_name, 591 | ) 592 | 593 | def _declare_cue_run_binary(name, runfiles_name, tags = []): 594 | native.config_setting( 595 | name = name + "_lacks_runfiles_directory", 596 | constraint_values = [ 597 | Label("@platforms//os:windows"), 598 | ], 599 | visibility = ["//visibility:private"], 600 | ) 601 | cue_run_name = name + "_cue_run_from_runfiles" 602 | sh_binary( 603 | name = cue_run_name, 604 | # NB: On Windows, we don't expect to have a runfiles directory 605 | # available, so instead we rely on a runfiles manifest to tell 606 | # us which files should be present where. We use a ZIP archive 607 | # to collect and project these runfiles into the right place. 608 | srcs = select({ 609 | ":{}_lacks_runfiles_directory".format(name): [Label("//cue:cue-run-from-archived-runfiles")], 610 | "//conditions:default": [Label("//cue:cue-run-from-runfiles")], 611 | }), 612 | data = [":" + runfiles_name] + select({ 613 | ":{}_lacks_runfiles_directory".format(name): ["@bazel_tools//tools/zip:zipper"], 614 | "//conditions:default": [], 615 | }), 616 | deps = ["@bazel_tools//tools/bash/runfiles"], 617 | tags = tags, 618 | ) 619 | return cue_run_name 620 | 621 | def _augment_consolidated_output_args(ctx, args): 622 | if ctx.attr.inline_imports: 623 | args.add("--inline-imports") 624 | args.add("--out", ctx.attr.output_format) 625 | 626 | def _add_common_consolidated_output_attrs_to(attrs): 627 | attrs.update({ 628 | "inline_imports": attr.bool( 629 | doc = "Expand references to non-core imports.", 630 | default = False, 631 | ), 632 | "output_format": attr.string( 633 | doc = "Output format", 634 | default = "cue", 635 | values = [ 636 | "cue", 637 | "json", 638 | "text", 639 | "yaml", 640 | ], 641 | ), 642 | "result": attr.output( 643 | doc = """The built result in the format specified in the "output_format" attribute.""", 644 | mandatory = True, 645 | ), 646 | }) 647 | return attrs 648 | 649 | def _prepare_consolidated_output_rule(name, **kwargs): 650 | extension_by_format = { 651 | "cue": "cue", 652 | "json": "json", 653 | "text": "txt", 654 | "yaml": "yaml", 655 | } 656 | output_format = kwargs.get("output_format", "cue") 657 | result = kwargs.pop("result", name + "." + extension_by_format[output_format]) 658 | return kwargs | { 659 | "result": result, 660 | } 661 | 662 | def _prepare_module_consuming_rule(name, **kwargs): 663 | deps = kwargs.get("deps", []) 664 | module = kwargs["module"] 665 | qualified_srcs = kwargs.get("qualified_srcs", {}) 666 | srcs = kwargs.get("srcs", []) 667 | tags = kwargs.get("tags", []) 668 | 669 | runfiles_name = name + "_cue_runfiles" 670 | _cue_module_runfiles( 671 | name = runfiles_name, 672 | deps = deps, 673 | module = module, 674 | srcs = srcs, 675 | qualified_srcs = qualified_srcs, 676 | tags = tags, 677 | ) 678 | return kwargs | { 679 | "cue_run": ":" + _declare_cue_run_binary(name, runfiles_name, tags), 680 | } 681 | 682 | def _prepare_instance_consuming_rule(name, **kwargs): 683 | instance = kwargs["instance"] 684 | qualified_srcs = kwargs.get("qualified_srcs", {}) 685 | srcs = kwargs.get("srcs", []) 686 | tags = kwargs.get("tags", []) 687 | 688 | runfiles_name = name + "_cue_runfiles" 689 | _cue_instance_runfiles( 690 | name = runfiles_name, 691 | instance = instance, 692 | srcs = srcs, 693 | qualified_srcs = qualified_srcs, 694 | tags = tags, 695 | ) 696 | return kwargs | { 697 | "cue_run": ":" + _declare_cue_run_binary(name, runfiles_name, tags), 698 | } 699 | 700 | def _prepare_standalone_rule(name, **kwargs): 701 | qualified_srcs = kwargs.get("qualified_srcs", {}) 702 | srcs = kwargs.get("srcs", []) 703 | tags = kwargs.get("tags", []) 704 | 705 | runfiles_name = name + "_cue_runfiles" 706 | _cue_standalone_runfiles( 707 | name = runfiles_name, 708 | srcs = srcs, 709 | qualified_srcs = qualified_srcs, 710 | tags = tags, 711 | ) 712 | return kwargs | { 713 | "cue_run": ":" + _declare_cue_run_binary(name, runfiles_name, tags), 714 | } 715 | 716 | def _cue_consolidated_standalone_files_impl(ctx): 717 | _make_output_producing_action( 718 | ctx, 719 | "def", 720 | "CUEDef", 721 | "consolidated", 722 | _augment_consolidated_output_args, 723 | ) 724 | 725 | _cue_consolidated_standalone_files = rule( 726 | implementation = _cue_consolidated_standalone_files_impl, 727 | attrs = _add_common_consolidated_output_attrs_to(_add_common_output_producing_attrs_to({})), 728 | toolchains = [_cue_toolchain_type], 729 | ) 730 | 731 | def cue_consolidated_standalone_files(name, **kwargs): 732 | _call_rule_after( 733 | name, 734 | _cue_consolidated_standalone_files, 735 | [ 736 | _prepare_standalone_rule, 737 | _prepare_consolidated_output_rule, 738 | ], 739 | **kwargs 740 | ) 741 | 742 | def _cue_consolidated_files_impl(ctx): 743 | _make_module_based_output_producing_action(ctx, "def", "CUEDef", "consolidated", _augment_consolidated_output_args) 744 | 745 | _cue_consolidated_files = rule( 746 | implementation = _cue_consolidated_files_impl, 747 | attrs = _add_common_consolidated_output_attrs_to(_add_common_module_based_attrs_to({})), 748 | toolchains = [_cue_toolchain_type], 749 | ) 750 | 751 | def cue_consolidated_files(name, **kwargs): 752 | _call_rule_after( 753 | name, 754 | _cue_consolidated_files, 755 | [ 756 | _prepare_module_consuming_rule, 757 | _prepare_consolidated_output_rule, 758 | ], 759 | **kwargs 760 | ) 761 | 762 | def _cue_consolidated_instance_impl(ctx): 763 | _make_instance_consuming_action(ctx, "def", "CUEDef", "consolidated", _augment_consolidated_output_args) 764 | 765 | _cue_consolidated_instance = rule( 766 | implementation = _cue_consolidated_instance_impl, 767 | attrs = _add_common_consolidated_output_attrs_to(_add_common_instance_consuming_attrs_to({})), 768 | toolchains = [_cue_toolchain_type], 769 | ) 770 | 771 | def cue_consolidated_instance(name, **kwargs): 772 | _call_rule_after( 773 | name, 774 | _cue_consolidated_instance, 775 | [ 776 | _prepare_instance_consuming_rule, 777 | _prepare_consolidated_output_rule, 778 | ], 779 | **kwargs 780 | ) 781 | 782 | def _augment_exported_output_args(ctx, args): 783 | if ctx.attr.escape: 784 | args.add("--escape") 785 | args.add("--out", ctx.attr.output_format) 786 | 787 | def _add_common_exported_output_attrs_to(attrs): 788 | attrs.update({ 789 | "escape": attr.bool( 790 | doc = "Use HTML escaping.", 791 | default = False, 792 | ), 793 | "output_format": attr.string( 794 | doc = "Output format", 795 | default = "json", 796 | values = [ 797 | "cue", 798 | "json", 799 | "text", 800 | "yaml", 801 | ], 802 | ), 803 | "result": attr.output( 804 | doc = """The built result in the format specified in the "output_format" attribute.""", 805 | mandatory = True, 806 | ), 807 | }) 808 | return attrs 809 | 810 | def _prepare_exported_output_rule(name, **kwargs): 811 | extension_by_format = { 812 | "cue": "cue", 813 | "json": "json", 814 | "text": "txt", 815 | "yaml": "yaml", 816 | } 817 | output_format = kwargs.get("output_format", "json") 818 | result = kwargs.pop("result", name + "." + extension_by_format[output_format]) 819 | return kwargs | { 820 | "result": result, 821 | } 822 | 823 | def _cue_exported_standalone_files_impl(ctx): 824 | _make_output_producing_action( 825 | ctx, 826 | "export", 827 | "CUEExport", 828 | "exported", 829 | _augment_exported_output_args, 830 | ) 831 | 832 | _cue_exported_standalone_files = rule( 833 | implementation = _cue_exported_standalone_files_impl, 834 | attrs = _add_common_exported_output_attrs_to(_add_common_output_producing_attrs_to({})), 835 | toolchains = [_cue_toolchain_type], 836 | ) 837 | 838 | def cue_exported_standalone_files(name, **kwargs): 839 | _call_rule_after( 840 | name, 841 | _cue_exported_standalone_files, 842 | [ 843 | _prepare_standalone_rule, 844 | _prepare_exported_output_rule, 845 | ], 846 | **kwargs 847 | ) 848 | 849 | def _cue_exported_files_impl(ctx): 850 | _make_module_based_output_producing_action(ctx, "export", "CUEExport", "exported", _augment_exported_output_args) 851 | 852 | _cue_exported_files = rule( 853 | implementation = _cue_exported_files_impl, 854 | attrs = _add_common_exported_output_attrs_to(_add_common_module_based_attrs_to({})), 855 | toolchains = [_cue_toolchain_type], 856 | ) 857 | 858 | def cue_exported_files(name, **kwargs): 859 | _call_rule_after( 860 | name, 861 | _cue_exported_files, 862 | [ 863 | _prepare_module_consuming_rule, 864 | _prepare_exported_output_rule, 865 | ], 866 | **kwargs 867 | ) 868 | 869 | def _cue_exported_instance_impl(ctx): 870 | _make_instance_consuming_action(ctx, "export", "CUEExport", "exported", _augment_exported_output_args) 871 | 872 | _cue_exported_instance = rule( 873 | implementation = _cue_exported_instance_impl, 874 | attrs = _add_common_exported_output_attrs_to(_add_common_instance_consuming_attrs_to({})), 875 | toolchains = [_cue_toolchain_type], 876 | ) 877 | 878 | def cue_exported_instance(name, **kwargs): 879 | _call_rule_after( 880 | name, 881 | _cue_exported_instance, 882 | [ 883 | _prepare_instance_consuming_rule, 884 | _prepare_exported_output_rule, 885 | ], 886 | **kwargs 887 | ) 888 | -------------------------------------------------------------------------------- /cue/extensions.bzl: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue/private/extensions:cue.bzl", 3 | _cue = "cue", 4 | ) 5 | 6 | cue = _cue 7 | -------------------------------------------------------------------------------- /cue/private/BUILD.bazel: -------------------------------------------------------------------------------- 1 | config_setting( 2 | name = "stamp", 3 | values = { 4 | "stamp": "true", 5 | }, 6 | visibility = ["//cue:__subpackages__"], 7 | ) 8 | -------------------------------------------------------------------------------- /cue/private/config.bzl: -------------------------------------------------------------------------------- 1 | CUEConfigInfo = provider( 2 | doc = "CUE-related build configuration summary", 3 | fields = { 4 | "stamp": "Whether stamping is enabled for this build.", 5 | }, 6 | ) 7 | 8 | def _cue_config_impl(ctx): 9 | return [CUEConfigInfo( 10 | stamp = ctx.attr.stamp, 11 | )] 12 | 13 | cue_config = rule( 14 | doc = "Collects information about the buildsettings in the current configuration.", 15 | implementation = _cue_config_impl, 16 | attrs = { 17 | "stamp": attr.bool( 18 | doc = "Whether stamping is enabled for this build.", 19 | mandatory = True, 20 | ), 21 | }, 22 | provides = [CUEConfigInfo], 23 | ) 24 | -------------------------------------------------------------------------------- /cue/private/extensions/BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seh/rules_cue/52969a471bb54eddf62ca39eb4e075a88fc88ea7/cue/private/extensions/BUILD.bazel -------------------------------------------------------------------------------- /cue/private/extensions/cue.bzl: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue/private/tools/cue:toolchain.bzl", 3 | "download_tool", 4 | "known_release_versions", 5 | ) 6 | load( 7 | ":download.bzl", 8 | "make_tag_class", 9 | "maximal_selected_version", 10 | ) 11 | 12 | visibility("//cue") 13 | 14 | def _cue_impl(ctx): 15 | download_tool( 16 | name = "cue_tool", 17 | version = maximal_selected_version(ctx, "CUE"), 18 | ) 19 | 20 | cue = module_extension( 21 | implementation = _cue_impl, 22 | tag_classes = { 23 | "download": make_tag_class(known_release_versions()), 24 | }, 25 | ) 26 | -------------------------------------------------------------------------------- /cue/private/extensions/download.bzl: -------------------------------------------------------------------------------- 1 | load( 2 | ":semver.bzl", 3 | "semver", 4 | ) 5 | 6 | visibility("private") 7 | 8 | def make_tag_class(accepted_versions): 9 | return tag_class( 10 | attrs = { 11 | "version": attr.string(values = accepted_versions), 12 | "tolerate_newer": attr.bool(default = True), 13 | }, 14 | ) 15 | 16 | def maximal_selected_version(ctx, tool_name): 17 | max_version = None 18 | reached_max_version_limit = False 19 | for mod in ctx.modules: 20 | for download in mod.tags.download: 21 | raw_version = download.version 22 | c = semver.to_comparable(raw_version[1:] if raw_version.startswith("v") else raw_version) 23 | if max_version: 24 | if c > max_version[1]: 25 | if reached_max_version_limit: 26 | fail("{} version {} requested by module \"{}\" exceeds maximum tolerated version {}".format(tool_name, raw_version, mod.name, max_version[0])) 27 | max_version = (raw_version, c) 28 | reached_max_version_limit = not download.tolerate_newer 29 | elif c < max_version[1] and not download.tolerate_newer: 30 | # NB: This module's tag may not be the maximum 31 | # tolerated version, because a later tag (in this 32 | # module or any other) could require an even lower 33 | # version. We don't bother sorting the full set 34 | # ahead of time. 35 | fail("{} version {} exceeds tolerated version {}".format(tool_name, max_version[0], raw_version)) 36 | else: 37 | max_version = (raw_version, c) 38 | reached_max_version_limit = not download.tolerate_newer 39 | 40 | return max_version[0] if max_version else None 41 | -------------------------------------------------------------------------------- /cue/private/extensions/semver.bzl: -------------------------------------------------------------------------------- 1 | visibility("private") 2 | 3 | # This file is copied from the "bazel-gazelle" project's file 4 | # bazel-gazelle/internal/bzlmod/semver.bzl. 5 | # (https://github.com/bazelbuild/bazel-gazelle/blob/8adf04f8f7587ec64dc0d616b7b5243cf49b3c9d/internal/bzlmod/semver.bzl) 6 | # 7 | # The file's author Fabian Meumertzheim (fmeum) suggested that I copy 8 | # it and use it here, avoiding introducing a dependency on that 9 | # project. 10 | 11 | # Compares lower than any non-numeric identifier. 12 | _COMPARES_LOWEST_SENTINEL = "" 13 | 14 | # Compares higher than any valid non-numeric identifier (containing only [A-Za-z0-9-]). 15 | _COMPARES_HIGHEST_SENTINEL = "{" 16 | 17 | def _identifier_to_comparable(ident): 18 | if not ident: 19 | fail("Identifiers in semantic version strings must not be empty") 20 | if ident.isdigit(): 21 | if ident[0] == "0" and ident != "0": 22 | fail("Numeric identifiers in semantic version strings must not include leading zeroes") 23 | 24 | # 11.4.1: 25 | # "Identifiers consisting of only digits are compared numerically." 26 | # 11.4.3: 27 | # "Numeric identifiers always have lower precedence than non-numeric identifiers." 28 | return (_COMPARES_LOWEST_SENTINEL, int(ident)) 29 | else: 30 | # 11.4.2: 31 | # "Identifiers with letters or hyphens are compared lexically in ASCII sort order." 32 | return (ident,) 33 | 34 | def _semver_to_comparable(v): 35 | """ 36 | Parses a string representation of a semver version into an opaque comparable object. 37 | """ 38 | 39 | # Strip build metadata as it is not relevant for comparisons. 40 | v, _, _ = v.partition("+") 41 | 42 | major_minor_patch_str, _, prerelease_str = v.partition("-") 43 | if prerelease_str: 44 | # 11.4.4: 45 | # "A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding 46 | # identifiers are equal." 47 | prerelease = tuple([_identifier_to_comparable(ident) for ident in prerelease_str.split(".")]) 48 | else: 49 | # 11.3: 50 | # "When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version." 51 | prerelease = ((_COMPARES_HIGHEST_SENTINEL,),) 52 | 53 | major_str, minor_str, patch_str = major_minor_patch_str.split(".") 54 | return ( 55 | int(major_str), 56 | int(minor_str), 57 | int(patch_str), 58 | prerelease, 59 | ) 60 | 61 | semver = struct( 62 | to_comparable = _semver_to_comparable, 63 | ) 64 | -------------------------------------------------------------------------------- /cue/private/future.bzl: -------------------------------------------------------------------------------- 1 | visibility("//cue/...") 2 | 3 | # Returns the path of a runfile that can be used to look up its 4 | # absolute path via the rlocation function provided by Bazel's 5 | # runfiles libraries. 6 | # 7 | # See https://github.com/bazelbuild/bazel/issues/17259 for making a 8 | # function like this available within Bazel or Skylib. 9 | # 10 | # Basis of inspiration: https://github.com/bazelbuild/rules_fuzzing/blob/22a866a3c98f374ab3284a7d26fc8318c17a711c/fuzzing/private/util.bzl#L39-L42 11 | def runfile_path(ctx, runfile): 12 | # For files that sit within the same containing repository, the 13 | # short path will match the corresponding label's unqualified 14 | # package name relative to the repository root. For files that sit 15 | # within a different repository, the short path will start with 16 | # "../" in order to escape upward out 17 | # of the current repository's directory tree. 18 | # 19 | # Alternate implementation: 20 | # return paths.normalize(paths.join(ctx.workspace_name, runfile.short_path)) 21 | p = runfile.short_path 22 | return p[3:] if p.startswith("../") else ctx.workspace_name + "/" + p 23 | -------------------------------------------------------------------------------- /cue/private/tools/BUILD.bazel: -------------------------------------------------------------------------------- 1 | [ 2 | alias( 3 | name = "os_{}".format(os), 4 | actual = actual, 5 | visibility = ["//visibility:public"], 6 | ) 7 | for os, actual in { 8 | "darwin": "@platforms//os:osx", 9 | "linux": "@platforms//os:linux", 10 | "windows": "@platforms//os:windows", 11 | }.items() 12 | ] 13 | 14 | [ 15 | alias( 16 | name = "cpu_{}".format(cpu), 17 | actual = actual, 18 | visibility = ["//visibility:public"], 19 | ) 20 | for cpu, actual in { 21 | "amd64": "@platforms//cpu:x86_64", 22 | "arm64": "@platforms//cpu:aarch64", 23 | }.items() 24 | ] 25 | -------------------------------------------------------------------------------- /cue/private/tools/cue/BUILD.bazel: -------------------------------------------------------------------------------- 1 | constraint_setting( 2 | name = "tool_version", 3 | visibility = ["//visibility:public"], 4 | ) 5 | -------------------------------------------------------------------------------- /cue/private/tools/cue/BUILD.tool.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "{containing_package_prefix}:toolchain.bzl", 3 | "cue_tool", 4 | "declare_cue_toolchains", 5 | ) 6 | 7 | cue_tool( 8 | name = "tool", 9 | binary = "cue{extension}", 10 | version = "{version}", 11 | ) 12 | 13 | declare_cue_toolchains( 14 | cue_tool = ":tool", 15 | ) 16 | -------------------------------------------------------------------------------- /cue/private/tools/cue/BUILD.toolchains.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "{containing_package_prefix}:toolchain.bzl", 3 | "declare_bazel_toolchains", 4 | ) 5 | 6 | declare_bazel_toolchains( 7 | toolchain_prefix = "@{tool_repo}//", 8 | version = "{version}", 9 | ) 10 | -------------------------------------------------------------------------------- /cue/private/tools/cue/toolchain.bzl: -------------------------------------------------------------------------------- 1 | # We must accommodate loading this file from repositories generated by 2 | # our repository rules. 3 | visibility("public") 4 | 5 | _TOOLS_BY_RELEASE = { 6 | "v0.13.0": { 7 | struct(os = "darwin", arch = "amd64"): "a31d34e22a9c3082fbfaaa9a9b8ef92085b4cbd95090cc67efc2e4fb501e2030", 8 | struct(os = "darwin", arch = "arm64"): "d70bcd278b8b20c78d550fa6a59da88b5fa65688ff3e8fc32a5aeec39aa4868a", 9 | struct(os = "linux", arch = "amd64"): "59ba96137da07cd2cdd2e17ec33af81f850126f022f25dd96516f0b42071b6a9", 10 | struct(os = "linux", arch = "arm64"): "59dcf4af25bf872decb44e2a706d75f822b32bbd06e83252cd181a177957ad00", 11 | struct(os = "windows", arch = "amd64"): "a17423528e176f984ae4778fe5c757955783985913a8e18d6ccc663eaf91c279", 12 | struct(os = "windows", arch = "arm64"): "fef57df2265c9ce92426ea698c9bf38e053f6b579a5f7cf1b8963926252aac00", 13 | }, 14 | "v0.12.1": { 15 | struct(os = "darwin", arch = "amd64"): "a39201b519d26388074fdb5aaafc653e98bade9e3d4472d4044f75a6888bfcba", 16 | struct(os = "darwin", arch = "arm64"): "3eb7180adc231503b519cfa0934c0f856a2ad850d84ad3f6988bf86f6b2ace9f", 17 | struct(os = "linux", arch = "amd64"): "40ef0a84594494c953945fb297842d3168c92170694928c9565cc0b581235ac5", 18 | struct(os = "linux", arch = "arm64"): "ee296a58a17cc9141ad9abfd25e33faefd3fdb376e27fe6e599ceadb12fd0901", 19 | struct(os = "windows", arch = "amd64"): "a80b570bd5d2d7d7e653f3c8689b37a7396a98d486901a14954f1ef6a83bd275", 20 | struct(os = "windows", arch = "arm64"): "61563bf81f0020bf7b6dd7f69b981f0e77dd0a014b17323905236623ca3ca705", 21 | }, 22 | "v0.12.0": { 23 | struct(os = "darwin", arch = "amd64"): "8474e522a978ecadef49b06d706ff276cd07629b1aa107b88adfc1284d3f93cc", 24 | struct(os = "darwin", arch = "arm64"): "7055a6423f753c8ea763699d48d78d341e8543397399daee281c66ecdc9ec5a5", 25 | struct(os = "linux", arch = "amd64"): "e55cd5abd98a592c110f87a7da9ef15bc72515200aecfe1bed04bf86311f5ba1", 26 | struct(os = "linux", arch = "arm64"): "488012bb0e5c080e2a9694ef8765403dd1075a4ec373dda618efa2d37b47f14f", 27 | struct(os = "windows", arch = "amd64"): "268bf95f4767b37d5db01450d55ca9d10e9a8bd8a1417c31dff456b5f9775abf", 28 | struct(os = "windows", arch = "arm64"): "91ad090eb86b0d21186d8de0a7b985d235b27e67cd464f7738663e457b042505", 29 | }, 30 | "v0.11.2": { 31 | struct(os = "darwin", arch = "amd64"): "75023d1b98ce8a4398b9b652c093cd44aa5255976f162d099aca3dd68abf1d58", 32 | struct(os = "darwin", arch = "arm64"): "7fde93169c13b830b3a9a9009cea8c564488b464f39f543f066498e4b844e84a", 33 | struct(os = "linux", arch = "amd64"): "cb9391bbea35cffbec8992f6f5816dea71919f8cfc5e5f201cd87bfc47e0bac6", 34 | struct(os = "linux", arch = "arm64"): "2094660133df37981e3f40973f065e9bdcab1413f79b9b7cb705d9b5eaef3df4", 35 | struct(os = "windows", arch = "amd64"): "6ed58000562d070282dd060a48f059d152c99b50b80eb602af1b986b8e6f01b6", 36 | struct(os = "windows", arch = "arm64"): "165c244331713e28c2a621cc9beb1ba9939618a3ad0efffa843f2190d3a70846", 37 | }, 38 | "v0.11.1": { 39 | struct(os = "darwin", arch = "amd64"): "aba5b59b0a8454bcc9f1c25818a57e32925127f174ad105f88b7ef1ef35ba6bf", 40 | struct(os = "darwin", arch = "arm64"): "dcbb58af56d5833c3bb1525e8a16b92af95338d416e07cdd3f1997ec9fe65725", 41 | struct(os = "linux", arch = "amd64"): "60c46ec5b90190c22a96ab3529ad363d03b687331bbeb6d7d8097a2983d7716d", 42 | struct(os = "linux", arch = "arm64"): "2734671d7bb4bb4916ddd96bf0cd7c9e10f645f7f654d7684e79b68cb88afb93", 43 | struct(os = "windows", arch = "amd64"): "9ec0d4b82fbe682c5a7e5d752fe6240d69ad277216f832bdbd53bd3f496c09e0", 44 | struct(os = "windows", arch = "arm64"): "180b2da0dd893bbe1bc55ecb79125ea5f45cc4f776bb3cfb0483b008c63f9190", 45 | }, 46 | "v0.11.0": { 47 | struct(os = "darwin", arch = "amd64"): "55aabc7c279e20654b734275cbbc64f4f6a6be034cdca9eee73cb06813e8bd2d", 48 | struct(os = "darwin", arch = "arm64"): "8db8868b184be737835fe1e4414249b70284b07bf3ebf425f0444d48b90be4ab", 49 | struct(os = "linux", arch = "amd64"): "fff7385999390c05c785a5fde5375002c1b02c2cdeae7195efa5e9997000dd47", 50 | struct(os = "linux", arch = "arm64"): "fc77673c9e3a3363f045748bad4beda55e8c0c2b371a24c12007ecabc01b1053", 51 | struct(os = "windows", arch = "amd64"): "aa58bc7e8623d6da6667de5d3774d8e665c5cb06db059dd071e2fa5e64492519", 52 | struct(os = "windows", arch = "arm64"): "33a71461e6dcc9a40ece45ad2b852e0795a1a11a91181a91731913da173f676e", 53 | }, 54 | } 55 | 56 | _DEFAULT_TOOL_VERSION = "v0.13.0" 57 | 58 | def known_release_versions(): 59 | return _TOOLS_BY_RELEASE.keys() 60 | 61 | CUEInfo = provider( 62 | doc = "Details pertaining to the CUE toolchain.", 63 | fields = { 64 | "tool": "CUE tool to invoke", 65 | "version": "This tool's released version name", 66 | }, 67 | ) 68 | 69 | CUEToolInfo = provider( 70 | doc = "Details pertaining to the CUE tool.", 71 | fields = { 72 | "binary": "CUE tool to invoke", 73 | "version": "This tool's released version name", 74 | }, 75 | ) 76 | 77 | def _cue_tool_impl(ctx): 78 | return [CUEToolInfo( 79 | binary = ctx.executable.binary, 80 | version = ctx.attr.version, 81 | )] 82 | 83 | cue_tool = rule( 84 | implementation = _cue_tool_impl, 85 | attrs = { 86 | "binary": attr.label( 87 | mandatory = True, 88 | allow_single_file = True, 89 | executable = True, 90 | cfg = "exec", 91 | doc = "CUE tool to invoke", 92 | ), 93 | "version": attr.string( 94 | mandatory = True, 95 | doc = "This tool's released version name", 96 | ), 97 | }, 98 | ) 99 | 100 | def _toolchain_impl(ctx): 101 | tool = ctx.attr.tool[CUEToolInfo] 102 | toolchain_info = platform_common.ToolchainInfo( 103 | cueinfo = CUEInfo( 104 | tool = tool.binary, 105 | version = tool.version, 106 | ), 107 | ) 108 | return [toolchain_info] 109 | 110 | cue_toolchain = rule( 111 | implementation = _toolchain_impl, 112 | attrs = { 113 | "tool": attr.label( 114 | mandatory = True, 115 | providers = [CUEToolInfo], 116 | cfg = "exec", 117 | doc = "CUE tool to use for validating and exporting data.", 118 | ), 119 | }, 120 | ) 121 | 122 | # buildifier: disable=unnamed-macro 123 | def declare_cue_toolchains(cue_tool): 124 | for version, platforms in _TOOLS_BY_RELEASE.items(): 125 | for platform in platforms.keys(): 126 | cue_toolchain( 127 | name = "{}_{}_{}".format(platform.os, platform.arch, version), 128 | tool = cue_tool, 129 | ) 130 | 131 | def _translate_host_platform(ctx): 132 | # NB: This is adapted from rules_go's "_detect_host_platform" function. 133 | os = ctx.os.name 134 | if os == "mac os x": 135 | os = "darwin" 136 | elif os.startswith("windows"): 137 | os = "windows" 138 | 139 | arch = ctx.os.arch 140 | if arch == "aarch64": 141 | arch = "arm64" 142 | elif arch == "x86_64": 143 | arch = "amd64" 144 | 145 | return os, arch 146 | 147 | _MODULE_REPOSITORY_NAME = "rules_cue" 148 | _CONTAINING_PACKAGE_PREFIX = "//cue/private/tools/cue" 149 | 150 | def _download_tool_impl(ctx): 151 | if not ctx.attr.arch and not ctx.attr.os: 152 | os, arch = _translate_host_platform(ctx) 153 | else: 154 | if not ctx.attr.arch: 155 | fail('"os" is set but "arch" is not') 156 | if not ctx.attr.os: 157 | fail('"arch" is set but "os" is not') 158 | os, arch = ctx.attr.os, ctx.attr.arch 159 | version = ctx.attr.version 160 | 161 | sha256sum = _TOOLS_BY_RELEASE[version][struct(os = os, arch = arch)] 162 | if not sha256sum: 163 | fail('No CUE tool is available for OS "{}" and CPU architecture "{}" at version {}'.format(os, arch, version)) 164 | ctx.report_progress('Downloading CUE tool for OS "{}" and CPU architecture "{}" at version {}.'.format(os, arch, version)) 165 | ctx.download_and_extract( 166 | url = "https://github.com/cue-lang/cue/releases/download/{version}/cue_{version}_{os}_{arch}.{extension}".format( 167 | version = version, 168 | os = os, 169 | arch = arch, 170 | extension = "zip" if os == "windows" else "tar.gz", 171 | ), 172 | sha256 = sha256sum, 173 | ) 174 | 175 | ctx.template( 176 | "BUILD.bazel", 177 | Label("{}:BUILD.tool.bazel".format(_CONTAINING_PACKAGE_PREFIX)), 178 | executable = False, 179 | substitutions = { 180 | "{containing_package_prefix}": "@{}{}".format(_MODULE_REPOSITORY_NAME, _CONTAINING_PACKAGE_PREFIX), 181 | "{extension}": ".exe" if os == "windows" else "", 182 | "{version}": version, 183 | }, 184 | ) 185 | return None 186 | 187 | _download_tool = repository_rule( 188 | implementation = _download_tool_impl, 189 | attrs = { 190 | "arch": attr.string(), 191 | "os": attr.string(), 192 | "version": attr.string( 193 | values = _TOOLS_BY_RELEASE.keys(), 194 | default = _DEFAULT_TOOL_VERSION, 195 | ), 196 | }, 197 | ) 198 | 199 | # buildifier: disable=unnamed-macro 200 | def declare_bazel_toolchains(version, toolchain_prefix): 201 | native.constraint_value( 202 | name = version, 203 | constraint_setting = "{}:tool_version".format(_CONTAINING_PACKAGE_PREFIX), 204 | ) 205 | constraint_value_prefix = "@{}//cue/private/tools".format(_MODULE_REPOSITORY_NAME) 206 | for platform in _TOOLS_BY_RELEASE[version].keys(): 207 | native.toolchain( 208 | name = "{}_{}_{}_toolchain".format(platform.os, platform.arch, version), 209 | exec_compatible_with = [ 210 | "{}:cpu_{}".format(constraint_value_prefix, platform.arch), 211 | "{}:os_{}".format(constraint_value_prefix, platform.os), 212 | ], 213 | toolchain = toolchain_prefix + (":{}_{}_{}".format(platform.os, platform.arch, version)), 214 | toolchain_type = "@{}//tools/cue:toolchain_type".format(_MODULE_REPOSITORY_NAME), 215 | ) 216 | 217 | def _toolchains_impl(ctx): 218 | ctx.template( 219 | "BUILD.bazel", 220 | Label("{}:BUILD.toolchains.bazel".format(_CONTAINING_PACKAGE_PREFIX)), 221 | executable = False, 222 | substitutions = { 223 | "{containing_package_prefix}": "@{}{}".format(_MODULE_REPOSITORY_NAME, _CONTAINING_PACKAGE_PREFIX), 224 | "{tool_repo}": ctx.attr.tool_repo, 225 | "{version}": ctx.attr.version, 226 | }, 227 | ) 228 | 229 | _toolchains_repo = repository_rule( 230 | implementation = _toolchains_impl, 231 | attrs = { 232 | "tool_repo": attr.string(mandatory = True), 233 | "version": attr.string( 234 | values = _TOOLS_BY_RELEASE.keys(), 235 | default = _DEFAULT_TOOL_VERSION, 236 | ), 237 | }, 238 | ) 239 | 240 | def download_tool(name, version = None): 241 | _download_tool( 242 | name = name, 243 | version = version, 244 | ) 245 | _toolchains_repo( 246 | name = name + "_toolchains", 247 | tool_repo = name, 248 | version = version, 249 | ) 250 | -------------------------------------------------------------------------------- /examples/bzlmod/BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seh/rules_cue/52969a471bb54eddf62ca39eb4e075a88fc88ea7/examples/bzlmod/BUILD.bazel -------------------------------------------------------------------------------- /examples/bzlmod/MODULE.bazel: -------------------------------------------------------------------------------- 1 | module( 2 | name = "rules_cue_examples_bzlmod", 3 | version = "0.0.0", 4 | ) 5 | 6 | bazel_dep(name = "bazel_skylib", version = "1.7.1") 7 | bazel_dep(name = "rules_cue", version = "0.0.0") 8 | local_path_override( 9 | module_name = "rules_cue", 10 | path = "../../", 11 | ) 12 | -------------------------------------------------------------------------------- /examples/bzlmod/root/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "@bazel_skylib//rules:write_file.bzl", 3 | "write_file", 4 | ) 5 | load( 6 | "@rules_cue//cue:cue.bzl", 7 | "cue_exported_standalone_files", 8 | ) 9 | 10 | write_file( 11 | name = "generated_entries", 12 | out = "extra-entries.cue", 13 | content = [ 14 | "package contacts", 15 | "extra_entries: [{", 16 | " name: common: \"Cher\"", 17 | " birth: month: \"May\"", 18 | " birth: year: 1946", 19 | "}]", 20 | ], 21 | ) 22 | 23 | cue_exported_standalone_files( 24 | name = "root", 25 | srcs = [ 26 | "entries.cue", 27 | "schema.cue", 28 | ":generated_entries", 29 | ], 30 | # Sort only by common name, given the complexity of accommodating 31 | # the other two optional name components. 32 | expression = "list.Sort(list.Concat([entries, extra_entries]), {x: {}, y: {}, less: x.name.common < y.name.common})", 33 | ) 34 | -------------------------------------------------------------------------------- /examples/bzlmod/root/entries.cue: -------------------------------------------------------------------------------- 1 | package contacts 2 | 3 | entries: [ 4 | { 5 | name: { 6 | common: "Johnny" 7 | surname: "Appleseed" 8 | } 9 | birth: { 10 | month: "September" 11 | year: 1774 12 | } 13 | }, 14 | { 15 | name: { 16 | common: "John" 17 | surname: "Lydon" 18 | } 19 | birth: { 20 | month: "January" 21 | year: 1956 22 | } 23 | }, 24 | ] 25 | -------------------------------------------------------------------------------- /examples/bzlmod/root/schema.cue: -------------------------------------------------------------------------------- 1 | package contacts 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | #Entry: { 8 | name: { 9 | common: strings.MinRunes(1) 10 | // Not all people have a middle name. 11 | middle?: strings.MinRunes(1) 12 | // Accommodate mononymous people. 13 | surname?: strings.MinRunes(1) 14 | } 15 | birth: { 16 | month: "January" | 17 | "February" | 18 | "March" | 19 | "April" | 20 | "May" | 21 | "June" | 22 | "July" | 23 | "August" | 24 | "September" | 25 | "October" | 26 | "November" | 27 | "December" 28 | year: int 29 | } 30 | } 31 | 32 | entries: [...#Entry] 33 | extra_entries: [...#Entry] 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/seh/rules_cue 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /test/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "cue_test.bzl", 3 | "cue_test", 4 | ) 5 | 6 | cue_test( 7 | name = "consolidated", 8 | generated_output_file = "//test/testdata/consolidated:consolidated.cue", 9 | ) 10 | 11 | cue_test( 12 | name = "hello_world_hello_world", 13 | generated_output_file = "//test/testdata/hello_world:hello_world.json", 14 | ) 15 | 16 | cue_test( 17 | name = "hello_world_message", 18 | generated_output_file = "//test/testdata/hello_world:message.txt", 19 | ) 20 | 21 | cue_test( 22 | name = "hello_world_de", 23 | generated_output_file = "//test/testdata/hello_world:de.json", 24 | ) 25 | 26 | cue_test( 27 | name = "hello_world_en", 28 | generated_output_file = "//test/testdata/hello_world:en.json", 29 | ) 30 | 31 | cue_test( 32 | name = "injection", 33 | generated_output_file = "//test/testdata/injection:injection.json", 34 | ) 35 | 36 | cue_test( 37 | name = "module_greeting", 38 | generated_output_file = "//test/testdata/module/greetings/lang/en:greeting.yaml", 39 | ) 40 | 41 | cue_test( 42 | name = "module_greeting_def", 43 | generated_output_file = "//test/testdata/module/greetings/lang/en:greeting_def.cue", 44 | ) 45 | 46 | cue_test( 47 | name = "my_service_dev", 48 | generated_output_file = "//test/testdata/myservice/environments:dev.json", 49 | ) 50 | 51 | cue_test( 52 | name = "my_service_prod", 53 | generated_output_file = "//test/testdata/myservice/environments:prod.json", 54 | ) 55 | 56 | cue_test( 57 | name = "non_cue", 58 | generated_output_file = "//test/testdata/non-cue:non-cue.cue", 59 | ) 60 | 61 | cue_test( 62 | name = "path_raw_text", 63 | generated_output_file = "//test/testdata/path:raw_text.json", 64 | ) 65 | 66 | cue_test( 67 | name = "path_multiple", 68 | generated_output_file = "//test/testdata/path:multiple.json", 69 | ) 70 | 71 | cue_test( 72 | name = "stamp", 73 | generated_output_file = "//test/testdata/stamp:stamp.json", 74 | ) 75 | -------------------------------------------------------------------------------- /test/cue_test.bzl: -------------------------------------------------------------------------------- 1 | load( 2 | "@bazel_skylib//lib:paths.bzl", 3 | "paths", 4 | ) 5 | load( 6 | "@bazel_skylib//rules:diff_test.bzl", 7 | "diff_test", 8 | ) 9 | 10 | def cue_test( 11 | name, 12 | generated_output_file, 13 | golden_file = None): 14 | if not golden_file: 15 | base, extension = paths.split_extension(generated_output_file) 16 | parts = base.rpartition(":") 17 | if parts[1] == ":": 18 | base = parts[0] 19 | golden_basename_prefix = parts[2] + "-" 20 | else: 21 | base = parts[2] 22 | golden_basename_prefix = "" 23 | golden_file = "{}:{}golden{}".format(base, golden_basename_prefix, extension) 24 | 25 | diff_test( 26 | name = name + "_test", 27 | file1 = generated_output_file, 28 | file2 = golden_file, 29 | size = "small", 30 | ) 31 | -------------------------------------------------------------------------------- /test/testdata/consolidated/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_consolidated_files", 4 | ) 5 | 6 | exports_files(["consolidated-golden.cue"]) 7 | 8 | cue_consolidated_files( 9 | name = "consolidated", 10 | srcs = [ 11 | "primary.cue", 12 | "secondary.cue", 13 | ], 14 | # See https://github.com/cue-lang/cue/issues/2252 for why we don't 15 | # merge any non-CUE data in this test case. 16 | inline_imports = True, 17 | module = "//test/testdata/consolidated/cue.mod", 18 | qualified_srcs = { 19 | "extra.ambiguous": "cue", 20 | }, 21 | visibility = ["//test:__subpackages__"], 22 | deps = [ 23 | "//test/testdata/consolidated/cue.mod/pkg/other-example.com/color", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /test/testdata/consolidated/consolidated-golden.cue: -------------------------------------------------------------------------------- 1 | package merged 2 | 3 | import ( 4 | "math" 5 | "math/bits" 6 | ) 7 | 8 | eyes: 255 9 | favorite: =0 & ::= 14 | // (Note that may be empty, from the "" case in .) 15 | // ::= 0 | 1 | ... | 9 16 | // ::= | 17 | // ::= | . | . | . 18 | // ::= "+" | "-" 19 | // ::= | 20 | // ::= | | 21 | // ::= Ki | Mi | Gi | Ti | Pi | Ei 22 | // (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) 23 | // ::= m | "" | k | M | G | T | P | E 24 | // (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) 25 | // ::= "e" | "E" 26 | // 27 | // No matter which of the three exponent forms is used, no quantity may represent 28 | // a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal 29 | // places. Numbers larger or more precise will be capped or rounded up. 30 | // (E.g.: 0.1m will rounded up to 1m.) 31 | // This may be extended in the future if we require larger or smaller quantities. 32 | // 33 | // When a Quantity is parsed from a string, it will remember the type of suffix 34 | // it had, and will use the same type again when it is serialized. 35 | // 36 | // Before serializing, Quantity will be put in "canonical form". 37 | // This means that Exponent/suffix will be adjusted up or down (with a 38 | // corresponding increase or decrease in Mantissa) such that: 39 | // a. No precision is lost 40 | // b. No fractional digits will be emitted 41 | // c. The exponent (or suffix) is as large as possible. 42 | // The sign will be omitted unless the number is negative. 43 | // 44 | // Examples: 45 | // 1.5 will be serialized as "1500m" 46 | // 1.5Gi will be serialized as "1536Mi" 47 | // 48 | // Note that the quantity will NEVER be internally represented by a 49 | // floating point number. That is the whole point of this exercise. 50 | // 51 | // Non-canonical values will still parse as long as they are well formed, 52 | // but will be re-emitted in their canonical form. (So always use canonical 53 | // form, or don't diff.) 54 | // 55 | // This format is intended to make it difficult to use these numbers without 56 | // writing some sort of special handling code in the hopes that that will 57 | // cause implementors to also use a fixed point implementation. 58 | // 59 | // +protobuf=true 60 | // +protobuf.embed=string 61 | // +protobuf.options.marshal=false 62 | // +protobuf.options.(gogoproto.goproto_stringer)=false 63 | // +k8s:deepcopy-gen=true 64 | // +k8s:openapi-gen=true 65 | #Quantity: _ 66 | 67 | // CanonicalValue allows a quantity amount to be converted to a string. 68 | #CanonicalValue: _ 69 | 70 | // Format lists the three possible formattings of a quantity. 71 | #Format: string // #enumFormat 72 | 73 | #enumFormat: 74 | #DecimalExponent | 75 | #BinarySI | 76 | #DecimalSI 77 | 78 | #DecimalExponent: #Format & "DecimalExponent" 79 | #BinarySI: #Format & "BinarySI" 80 | #DecimalSI: #Format & "DecimalSI" 81 | 82 | // splitREString is used to separate a number from its suffix; as such, 83 | // this is overly permissive, but that's OK-- it will be checked later. 84 | _#splitREString: "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$" 85 | 86 | _#int64QuantityExpectedBytes: 18 87 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_instance", 4 | ) 5 | 6 | cue_instance( 7 | name = "cue_v1_library", 8 | srcs = [ 9 | "duration_go_gen.cue", 10 | "group_version_go_gen.cue", 11 | "meta_go_gen.cue", 12 | "micro_time_go_gen.cue", 13 | "register_go_gen.cue", 14 | "time_go_gen.cue", 15 | "time_proto_go_gen.cue", 16 | "types_go_gen.cue", 17 | "watch_go_gen.cue", 18 | ], 19 | ancestor = "//test/testdata/myservice/cue.mod", 20 | visibility = ["//test/testdata/myservice:__subpackages__"], 21 | deps = [ 22 | "//test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime:cue_runtime_library", 23 | "//test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types:cue_types_library", 24 | "//test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch:cue_watch_library", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/duration_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | // Duration is a wrapper around time.Duration which supports correct 8 | // marshaling to YAML and JSON. In particular, it marshals into strings, which 9 | // can be used as map keys in json. 10 | #Duration: _ 11 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/group_version_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | // GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying 8 | // concepts during lookup stages without having partially valid types 9 | // 10 | // +protobuf.options.(gogoproto.goproto_stringer)=false 11 | #GroupResource: { 12 | group: string @go(Group) @protobuf(1,bytes,opt) 13 | resource: string @go(Resource) @protobuf(2,bytes,opt) 14 | } 15 | 16 | // GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion 17 | // to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling 18 | // 19 | // +protobuf.options.(gogoproto.goproto_stringer)=false 20 | #GroupVersionResource: { 21 | group: string @go(Group) @protobuf(1,bytes,opt) 22 | version: string @go(Version) @protobuf(2,bytes,opt) 23 | resource: string @go(Resource) @protobuf(3,bytes,opt) 24 | } 25 | 26 | // GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying 27 | // concepts during lookup stages without having partially valid types 28 | // 29 | // +protobuf.options.(gogoproto.goproto_stringer)=false 30 | #GroupKind: { 31 | group: string @go(Group) @protobuf(1,bytes,opt) 32 | kind: string @go(Kind) @protobuf(2,bytes,opt) 33 | } 34 | 35 | // GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion 36 | // to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling 37 | // 38 | // +protobuf.options.(gogoproto.goproto_stringer)=false 39 | #GroupVersionKind: { 40 | group: string @go(Group) @protobuf(1,bytes,opt) 41 | version: string @go(Version) @protobuf(2,bytes,opt) 42 | kind: string @go(Kind) @protobuf(3,bytes,opt) 43 | } 44 | 45 | // GroupVersion contains the "group" and the "version", which uniquely identifies the API. 46 | // 47 | // +protobuf.options.(gogoproto.goproto_stringer)=false 48 | #GroupVersion: _ 49 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/meta_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | // TODO: move this, Object, List, and Type to a different package 8 | #ObjectMetaAccessor: _ 9 | 10 | // Object lets you work with object metadata from any of the versioned or 11 | // internal API objects. Attempting to set or retrieve a field on an object that does 12 | // not support that field (Name, UID, Namespace on lists) will be a no-op and return 13 | // a default value. 14 | #Object: _ 15 | 16 | // ListMetaAccessor retrieves the list interface from an object 17 | #ListMetaAccessor: _ 18 | 19 | // Common lets you work with core metadata from any of the versioned or 20 | // internal API objects. Attempting to set or retrieve a field on an object that does 21 | // not support that field will be a no-op and return a default value. 22 | // TODO: move this, and TypeMeta and ListMeta, to a different package 23 | #Common: _ 24 | 25 | // ListInterface lets you work with list metadata from any of the versioned or 26 | // internal API objects. Attempting to set or retrieve a field on an object that does 27 | // not support that field will be a no-op and return a default value. 28 | // TODO: move this, and TypeMeta and ListMeta, to a different package 29 | #ListInterface: _ 30 | 31 | // Type exposes the type and APIVersion of versioned or internal API objects. 32 | // TODO: move this, and TypeMeta and ListMeta, to a different package 33 | #Type: _ 34 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | #RFC3339Micro: "2006-01-02T15:04:05.000000Z07:00" 8 | 9 | // MicroTime is version of Time with microsecond level precision. 10 | // 11 | // +protobuf.options.marshal=false 12 | // +protobuf.as=Timestamp 13 | // +protobuf.options.(gogoproto.goproto_stringer)=false 14 | #MicroTime: _ 15 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/register_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | #GroupName: "meta.k8s.io" 8 | 9 | #WatchEventKind: "WatchEvent" 10 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/time_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | // Time is a wrapper around time.Time which supports correct 8 | // marshaling to YAML and JSON. Wrappers are provided for many 9 | // of the factory methods that the time package offers. 10 | // 11 | // +protobuf.options.marshal=false 12 | // +protobuf.as=Timestamp 13 | // +protobuf.options.(gogoproto.goproto_stringer)=false 14 | #Time: _ 15 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/time_proto_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | // Timestamp is a struct that is equivalent to Time, but intended for 8 | // protobuf marshalling/unmarshalling. It is generated into a serialization 9 | // that matches Time. Do not use in Go structs. 10 | #Timestamp: { 11 | // Represents seconds of UTC time since Unix epoch 12 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 13 | // 9999-12-31T23:59:59Z inclusive. 14 | seconds: int64 @go(Seconds) @protobuf(1,varint,opt) 15 | 16 | // Non-negative fractions of a second at nanosecond resolution. Negative 17 | // second values with fractions must still have non-negative nanos values 18 | // that count forward in time. Must be from 0 to 999,999,999 19 | // inclusive. This field may be limited in precision depending on context. 20 | nanos: int32 @go(Nanos) @protobuf(2,varint,opt) 21 | } 22 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/watch_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/apis/meta/v1 4 | 5 | package v1 6 | 7 | import ( 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/apimachinery/pkg/watch" 10 | ) 11 | 12 | // Event represents a single event to a watched resource. 13 | // 14 | // +protobuf=true 15 | // +k8s:deepcopy-gen=true 16 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 17 | #WatchEvent: { 18 | type: string @go(Type) @protobuf(1,bytes,opt) 19 | 20 | // Object is: 21 | // * If Type is Added or Modified: the new state of the object. 22 | // * If Type is Deleted: the state of the object immediately before deletion. 23 | // * If Type is Error: *Status is recommended; other types may make sense 24 | // depending on context. 25 | object: runtime.#RawExtension @go(Object) @protobuf(2,bytes,opt) 26 | } 27 | 28 | // InternalEvent makes watch.Event versioned 29 | // +protobuf=false 30 | #InternalEvent: watch.#Event 31 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_instance", 4 | ) 5 | 6 | cue_instance( 7 | name = "cue_runtime_library", 8 | srcs = [ 9 | "codec_go_gen.cue", 10 | "conversion_go_gen.cue", 11 | "converter_go_gen.cue", 12 | "doc_go_gen.cue", 13 | "helper_go_gen.cue", 14 | "interfaces_go_gen.cue", 15 | "negotiate_go_gen.cue", 16 | "swagger_doc_generator_go_gen.cue", 17 | "types_go_gen.cue", 18 | "types_proto_go_gen.cue", 19 | ], 20 | ancestor = "//test/testdata/myservice/cue.mod", 21 | visibility = ["//test/testdata/myservice:__subpackages__"], 22 | ) 23 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/codec_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding. 8 | #NoopEncoder: { 9 | Decoder: #Decoder 10 | } 11 | 12 | _#noopEncoderIdentifier: #Identifier & "noop" 13 | 14 | // NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding. 15 | #NoopDecoder: { 16 | Encoder: #Encoder 17 | } 18 | 19 | _#internalGroupVersionerIdentifier: "internal" 20 | _#disabledGroupVersionerIdentifier: "disabled" 21 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/conversion_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | // Package runtime defines conversions between generic types and structs to map query strings 6 | // to struct objects. 7 | package runtime 8 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/converter_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // UnstructuredConverter is an interface for converting between interface{} 8 | // and map[string]interface representation. 9 | #UnstructuredConverter: _ 10 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/doc_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | // Package runtime includes helper functions for working with API objects 6 | // that follow the kubernetes API object conventions, which are: 7 | // 8 | // 0. Your API objects have a common metadata struct member, TypeMeta. 9 | // 10 | // 1. Your code refers to an internal set of API objects. 11 | // 12 | // 2. In a separate package, you have an external set of API objects. 13 | // 14 | // 3. The external set is considered to be versioned, and no breaking 15 | // changes are ever made to it (fields may be added but not changed 16 | // or removed). 17 | // 18 | // 4. As your api evolves, you'll make an additional versioned package 19 | // with every major change. 20 | // 21 | // 5. Versioned packages have conversion functions which convert to 22 | // and from the internal version. 23 | // 24 | // 6. You'll continue to support older versions according to your 25 | // deprecation policy, and you can easily provide a program/library 26 | // to update old versions into new versions because of 5. 27 | // 28 | // 7. All of your serializations and deserializations are handled in a 29 | // centralized place. 30 | // 31 | // Package runtime provides a conversion helper to make 5 easy, and the 32 | // Encode/Decode/DecodeInto trio to accomplish 7. You can also register 33 | // additional "codecs" which use a version of your choice. It's 34 | // recommended that you register your types with runtime in your 35 | // package's init function. 36 | // 37 | // As a bonus, a few common types useful from all api objects and versions 38 | // are provided in types.go. 39 | package runtime 40 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/helper_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // MultiObjectTyper returns the types of objects across multiple schemes in order. 8 | #MultiObjectTyper: [...#ObjectTyper] 9 | 10 | // WithVersionEncoder serializes an object and ensures the GVK is set. 11 | #WithVersionEncoder: { 12 | Version: #GroupVersioner 13 | Encoder: #Encoder 14 | ObjectTyper: #ObjectTyper 15 | } 16 | 17 | // WithoutVersionDecoder clears the group version kind of a deserialized object. 18 | #WithoutVersionDecoder: { 19 | Decoder: #Decoder 20 | } 21 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/interfaces_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // APIVersionInternal may be used if you are registering a type that should not 8 | // be considered stable or serialized - it is a convention only and has no 9 | // special behavior in this package. 10 | #APIVersionInternal: "__internal" 11 | 12 | // GroupVersioner refines a set of possible conversion targets into a single option. 13 | #GroupVersioner: _ 14 | 15 | // Identifier represents an identifier. 16 | // Identitier of two different objects should be equal if and only if for every 17 | // input the output they produce is exactly the same. 18 | #Identifier: string // #enumIdentifier 19 | 20 | #enumIdentifier: 21 | _#noopEncoderIdentifier 22 | 23 | // Encoder writes objects to a serialized form 24 | #Encoder: _ 25 | 26 | // Decoder attempts to load an object from data. 27 | #Decoder: _ 28 | 29 | // Serializer is the core interface for transforming objects into a serialized format and back. 30 | // Implementations may choose to perform conversion of the object, but no assumptions should be made. 31 | #Serializer: _ 32 | 33 | // Codec is a Serializer that deals with the details of versioning objects. It offers the same 34 | // interface as Serializer, so this is a marker to consumers that care about the version of the objects 35 | // they receive. 36 | #Codec: #Serializer 37 | 38 | // ParameterCodec defines methods for serializing and deserializing API objects to url.Values and 39 | // performing any necessary conversion. Unlike the normal Codec, query parameters are not self describing 40 | // and the desired version must be specified. 41 | #ParameterCodec: _ 42 | 43 | // Framer is a factory for creating readers and writers that obey a particular framing pattern. 44 | #Framer: _ 45 | 46 | // SerializerInfo contains information about a specific serialization format 47 | #SerializerInfo: { 48 | // MediaType is the value that represents this serializer over the wire. 49 | MediaType: string 50 | 51 | // MediaTypeType is the first part of the MediaType ("application" in "application/json"). 52 | MediaTypeType: string 53 | 54 | // MediaTypeSubType is the second part of the MediaType ("json" in "application/json"). 55 | MediaTypeSubType: string 56 | 57 | // EncodesAsText indicates this serializer can be encoded to UTF-8 safely. 58 | EncodesAsText: bool 59 | 60 | // Serializer is the individual object serializer for this media type. 61 | Serializer: #Serializer 62 | 63 | // PrettySerializer, if set, can serialize this object in a form biased towards 64 | // readability. 65 | PrettySerializer: #Serializer 66 | 67 | // StreamSerializer, if set, describes the streaming serialization format 68 | // for this media type. 69 | StreamSerializer?: null | #StreamSerializerInfo @go(,*StreamSerializerInfo) 70 | } 71 | 72 | // StreamSerializerInfo contains information about a specific stream serialization format 73 | #StreamSerializerInfo: { 74 | // EncodesAsText indicates this serializer can be encoded to UTF-8 safely. 75 | EncodesAsText: bool 76 | 77 | // Serializer is the top level object serializer for this type when streaming 78 | Serializer: #Serializer 79 | 80 | // Framer is the factory for retrieving streams that separate objects on the wire 81 | Framer: #Framer 82 | } 83 | 84 | // NegotiatedSerializer is an interface used for obtaining encoders, decoders, and serializers 85 | // for multiple supported media types. This would commonly be accepted by a server component 86 | // that performs HTTP content negotiation to accept multiple formats. 87 | #NegotiatedSerializer: _ 88 | 89 | // ClientNegotiator handles turning an HTTP content type into the appropriate encoder. 90 | // Use NewClientNegotiator or NewVersionedClientNegotiator to create this interface from 91 | // a NegotiatedSerializer. 92 | #ClientNegotiator: _ 93 | 94 | // StorageSerializer is an interface used for obtaining encoders, decoders, and serializers 95 | // that can read and write data at rest. This would commonly be used by client tools that must 96 | // read files, or server side storage interfaces that persist restful objects. 97 | #StorageSerializer: _ 98 | 99 | // NestedObjectEncoder is an optional interface that objects may implement to be given 100 | // an opportunity to encode any nested Objects / RawExtensions during serialization. 101 | #NestedObjectEncoder: _ 102 | 103 | // NestedObjectDecoder is an optional interface that objects may implement to be given 104 | // an opportunity to decode any nested Objects / RawExtensions during serialization. 105 | #NestedObjectDecoder: _ 106 | 107 | #ObjectDefaulter: _ 108 | 109 | #ObjectVersioner: _ 110 | 111 | // ObjectConvertor converts an object to a different version. 112 | #ObjectConvertor: _ 113 | 114 | // ObjectTyper contains methods for extracting the APIVersion and Kind 115 | // of objects. 116 | #ObjectTyper: _ 117 | 118 | // ObjectCreater contains methods for instantiating an object by kind and version. 119 | #ObjectCreater: _ 120 | 121 | // EquivalentResourceMapper provides information about resources that address the same underlying data as a specified resource 122 | #EquivalentResourceMapper: _ 123 | 124 | // EquivalentResourceRegistry provides an EquivalentResourceMapper interface, 125 | // and allows registering known resource[/subresource] -> kind 126 | #EquivalentResourceRegistry: _ 127 | 128 | // ResourceVersioner provides methods for setting and retrieving 129 | // the resource version from an API object. 130 | #ResourceVersioner: _ 131 | 132 | // SelfLinker provides methods for setting and retrieving the SelfLink field of an API object. 133 | #SelfLinker: _ 134 | 135 | // Object interface must be supported by all API types registered with Scheme. Since objects in a scheme are 136 | // expected to be serialized to the wire, the interface an Object must provide to the Scheme allows 137 | // serializers to set the kind, version, and group the object is represented as. An Object may choose 138 | // to return a no-op ObjectKindAccessor in cases where it is not expected to be serialized. 139 | #Object: _ 140 | 141 | // CacheableObject allows an object to cache its different serializations 142 | // to avoid performing the same serialization multiple times. 143 | #CacheableObject: _ 144 | 145 | // Unstructured objects store values as map[string]interface{}, with only values that can be serialized 146 | // to JSON allowed. 147 | #Unstructured: _ 148 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/negotiate_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // NegotiateError is returned when a ClientNegotiator is unable to locate 8 | // a serializer for the requested operation. 9 | #NegotiateError: { 10 | ContentType: string 11 | Stream: bool 12 | } 13 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/swagger_doc_generator_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // Pair of strings. We keed the name of fields and the doc 8 | #Pair: { 9 | Name: string 10 | Doc: string 11 | } 12 | 13 | // KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself 14 | #KubeTypes: [...#Pair] 15 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/types_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | // TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, 8 | // like this: 9 | // type MyAwesomeAPIObject struct { 10 | // runtime.TypeMeta `json:",inline"` 11 | // ... // other fields 12 | // } 13 | // func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind 14 | // 15 | // TypeMeta is provided here for convenience. You may use it directly from this package or define 16 | // your own with the same fields. 17 | // 18 | // +k8s:deepcopy-gen=false 19 | // +protobuf=true 20 | // +k8s:openapi-gen=true 21 | #TypeMeta: { 22 | // +optional 23 | apiVersion?: string @go(APIVersion) @protobuf(1,bytes,opt) 24 | 25 | // +optional 26 | kind?: string @go(Kind) @protobuf(2,bytes,opt) 27 | } 28 | 29 | #ContentTypeJSON: "application/json" 30 | #ContentTypeYAML: "application/yaml" 31 | #ContentTypeProtobuf: "application/vnd.kubernetes.protobuf" 32 | 33 | // RawExtension is used to hold extensions in external versions. 34 | // 35 | // To use this, make a field which has RawExtension as its type in your external, versioned 36 | // struct, and Object in your internal struct. You also need to register your 37 | // various plugin types. 38 | // 39 | // // Internal package: 40 | // type MyAPIObject struct { 41 | // runtime.TypeMeta `json:",inline"` 42 | // MyPlugin runtime.Object `json:"myPlugin"` 43 | // } 44 | // type PluginA struct { 45 | // AOption string `json:"aOption"` 46 | // } 47 | // 48 | // // External package: 49 | // type MyAPIObject struct { 50 | // runtime.TypeMeta `json:",inline"` 51 | // MyPlugin runtime.RawExtension `json:"myPlugin"` 52 | // } 53 | // type PluginA struct { 54 | // AOption string `json:"aOption"` 55 | // } 56 | // 57 | // // On the wire, the JSON will look something like this: 58 | // { 59 | // "kind":"MyAPIObject", 60 | // "apiVersion":"v1", 61 | // "myPlugin": { 62 | // "kind":"PluginA", 63 | // "aOption":"foo", 64 | // }, 65 | // } 66 | // 67 | // So what happens? Decode first uses json or yaml to unmarshal the serialized data into 68 | // your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. 69 | // The next step is to copy (using pkg/conversion) into the internal struct. The runtime 70 | // package's DefaultScheme has conversion functions installed which will unpack the 71 | // JSON stored in RawExtension, turning it into the correct object type, and storing it 72 | // in the Object. (TODO: In the case where the object is of an unknown type, a 73 | // runtime.Unknown object will be created and stored.) 74 | // 75 | // +k8s:deepcopy-gen=true 76 | // +protobuf=true 77 | // +k8s:openapi-gen=true 78 | #RawExtension: _ 79 | 80 | // Unknown allows api objects with unknown types to be passed-through. This can be used 81 | // to deal with the API objects from a plug-in. Unknown objects still have functioning 82 | // TypeMeta features-- kind, version, etc. 83 | // TODO: Make this object have easy access to field based accessors and settors for 84 | // metadata and field mutatation. 85 | // 86 | // +k8s:deepcopy-gen=true 87 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 88 | // +protobuf=true 89 | // +k8s:openapi-gen=true 90 | #Unknown: _ 91 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime/types_proto_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/runtime 4 | 5 | package runtime 6 | 7 | #ProtobufMarshaller: _ 8 | 9 | #ProtobufReverseMarshaller: _ 10 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_instance", 4 | ) 5 | 6 | cue_instance( 7 | name = "cue_types_library", 8 | srcs = [ 9 | "doc_go_gen.cue", 10 | "namespacedname_go_gen.cue", 11 | "nodename_go_gen.cue", 12 | "patch_go_gen.cue", 13 | "uid_go_gen.cue", 14 | ], 15 | ancestor = "//test/testdata/myservice/cue.mod", 16 | visibility = ["//test/testdata/myservice:__subpackages__"], 17 | ) 18 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/doc_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/types 4 | 5 | // Package types implements various generic types used throughout kubernetes. 6 | package types 7 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/namespacedname_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/types 4 | 5 | package types 6 | 7 | #NamespacedName: { 8 | Namespace: string 9 | Name: string 10 | } 11 | 12 | #Separator: 47 // '/' 13 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/nodename_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/types 4 | 5 | package types 6 | 7 | // NodeName is a type that holds a api.Node's Name identifier. 8 | // Being a type captures intent and helps make sure that the node name 9 | // is not confused with similar concepts (the hostname, the cloud provider id, 10 | // the cloud provider name etc) 11 | // 12 | // To clarify the various types: 13 | // 14 | // * Node.Name is the Name field of the Node in the API. This should be stored in a NodeName. 15 | // Unfortunately, because Name is part of ObjectMeta, we can't store it as a NodeName at the API level. 16 | // 17 | // * Hostname is the hostname of the local machine (from uname -n). 18 | // However, some components allow the user to pass in a --hostname-override flag, 19 | // which will override this in most places. In the absence of anything more meaningful, 20 | // kubelet will use Hostname as the Node.Name when it creates the Node. 21 | // 22 | // * The cloudproviders have the own names: GCE has InstanceName, AWS has InstanceId. 23 | // 24 | // For GCE, InstanceName is the Name of an Instance object in the GCE API. On GCE, Instance.Name becomes the 25 | // Hostname, and thus it makes sense also to use it as the Node.Name. But that is GCE specific, and it is up 26 | // to the cloudprovider how to do this mapping. 27 | // 28 | // For AWS, the InstanceID is not yet suitable for use as a Node.Name, so we actually use the 29 | // PrivateDnsName for the Node.Name. And this is _not_ always the same as the hostname: if 30 | // we are using a custom DHCP domain it won't be. 31 | #NodeName: string 32 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/patch_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/types 4 | 5 | package types 6 | 7 | // Similarly to above, these are constants to support HTTP PATCH utilized by 8 | // both the client and server that didn't make sense for a whole package to be 9 | // dedicated to. 10 | #PatchType: string // #enumPatchType 11 | 12 | #enumPatchType: 13 | #JSONPatchType | 14 | #MergePatchType | 15 | #StrategicMergePatchType | 16 | #ApplyPatchType 17 | 18 | #JSONPatchType: #PatchType & "application/json-patch+json" 19 | #MergePatchType: #PatchType & "application/merge-patch+json" 20 | #StrategicMergePatchType: #PatchType & "application/strategic-merge-patch+json" 21 | #ApplyPatchType: #PatchType & "application/apply-patch+yaml" 22 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/types/uid_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/types 4 | 5 | package types 6 | 7 | // UID is a type that holds unique ID values, including UUIDs. Because we 8 | // don't ONLY use UUIDs, this is an alias to string. Being a type captures 9 | // intent and helps make sure that UIDs and names do not get conflated. 10 | #UID: string 11 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/util/intstr/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_instance", 4 | ) 5 | 6 | cue_instance( 7 | name = "cue_intstr_library", 8 | srcs = ["intstr_go_gen.cue"], 9 | ancestor = "//test/testdata/myservice/cue.mod", 10 | visibility = ["//test/testdata/myservice:__subpackages__"], 11 | ) 12 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/util/intstr/intstr_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/util/intstr 4 | 5 | package intstr 6 | 7 | // IntOrString is a type that can hold an int32 or a string. When used in 8 | // JSON or YAML marshalling and unmarshalling, it produces or consumes the 9 | // inner type. This allows you to have, for example, a JSON field that can 10 | // accept a name or number. 11 | // TODO: Rename to Int32OrString 12 | // 13 | // +protobuf=true 14 | // +protobuf.options.(gogoproto.goproto_stringer)=false 15 | // +k8s:openapi-gen=true 16 | #IntOrString: _ 17 | 18 | // Type represents the stored type of IntOrString. 19 | #Type: int64 // #enumType 20 | 21 | #enumType: 22 | #Int | 23 | #String 24 | 25 | #Int: #Type & 0 26 | #String: #Type & 1 27 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_instance", 4 | ) 5 | 6 | cue_instance( 7 | name = "cue_watch_library", 8 | srcs = [ 9 | "doc_go_gen.cue", 10 | "filter_go_gen.cue", 11 | "mux_go_gen.cue", 12 | "streamwatcher_go_gen.cue", 13 | "watch_go_gen.cue", 14 | ], 15 | ancestor = "//test/testdata/myservice/cue.mod", 16 | visibility = ["//test/testdata/myservice:__subpackages__"], 17 | deps = ["//test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/runtime:cue_runtime_library"], 18 | ) 19 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/doc_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/watch 4 | 5 | // Package watch contains a generic watchable interface, and a fake for 6 | // testing code that uses the watch interface. 7 | package watch 8 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/filter_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/watch 4 | 5 | package watch 6 | 7 | // Recorder records all events that are sent from the watch until it is closed. 8 | #Recorder: { 9 | Interface: #Interface 10 | } 11 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/mux_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/watch 4 | 5 | package watch 6 | 7 | // FullChannelBehavior controls how the Broadcaster reacts if a watcher's watch 8 | // channel is full. 9 | #FullChannelBehavior: int // #enumFullChannelBehavior 10 | 11 | #enumFullChannelBehavior: 12 | #WaitIfChannelFull | 13 | #DropIfChannelFull 14 | 15 | #WaitIfChannelFull: #FullChannelBehavior & 0 16 | #DropIfChannelFull: #FullChannelBehavior & 1 17 | 18 | _#incomingQueueLength: 25 19 | 20 | _#internalRunFunctionMarker: "internal-do-function" 21 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/streamwatcher_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/watch 4 | 5 | package watch 6 | 7 | // Decoder allows StreamWatcher to watch any stream for which a Decoder can be written. 8 | #Decoder: _ 9 | 10 | // Reporter hides the details of how an error is turned into a runtime.Object for 11 | // reporting on a watch stream since this package may not import a higher level report. 12 | #Reporter: _ 13 | -------------------------------------------------------------------------------- /test/testdata/myservice/cue.mod/pkg/k8s.io/apimachinery/pkg/watch/watch_go_gen.cue: -------------------------------------------------------------------------------- 1 | // Code generated by cue get go. DO NOT EDIT. 2 | 3 | //cue:generate cue get go k8s.io/apimachinery/pkg/watch 4 | 5 | package watch 6 | 7 | import "k8s.io/apimachinery/pkg/runtime" 8 | 9 | // Interface can be implemented by anything that knows how to watch and report changes. 10 | #Interface: _ 11 | 12 | // EventType defines the possible types of events. 13 | #EventType: string // #enumEventType 14 | 15 | #enumEventType: 16 | #Added | 17 | #Modified | 18 | #Deleted | 19 | #Bookmark | 20 | #Error 21 | 22 | #Added: #EventType & "ADDED" 23 | #Modified: #EventType & "MODIFIED" 24 | #Deleted: #EventType & "DELETED" 25 | #Bookmark: #EventType & "BOOKMARK" 26 | #Error: #EventType & "ERROR" 27 | 28 | // Event represents a single event to a watched resource. 29 | // +k8s:deepcopy-gen=true 30 | #Event: { 31 | Type: #EventType 32 | 33 | // Object is: 34 | // * If Type is Added or Modified: the new state of the object. 35 | // * If Type is Deleted: the state of the object immediately before deletion. 36 | // * If Type is Bookmark: the object (instance of a type being watched) where 37 | // only ResourceVersion field is set. On successful restart of watch from a 38 | // bookmark resourceVersion, client is guaranteed to not get repeat event 39 | // nor miss any events. 40 | // * If Type is Error: *api.Status is recommended; other types may make sense 41 | // depending on context. 42 | Object: runtime.#Object 43 | } 44 | 45 | // RaceFreeFakeWatcher lets you test anything that consumes a watch.Interface; threadsafe. 46 | #RaceFreeFakeWatcher: { 47 | Stopped: bool 48 | } 49 | -------------------------------------------------------------------------------- /test/testdata/myservice/environments/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_exported_files", 4 | ) 5 | 6 | exports_files([ 7 | "dev-golden.json", 8 | "prod-golden.json", 9 | ]) 10 | 11 | cue_exported_files( 12 | name = "dev", 13 | srcs = ["dev.cue"], 14 | module = "//test/testdata/myservice/cue.mod", 15 | visibility = ["//test:__subpackages__"], 16 | deps = [ 17 | "//test/testdata/myservice:cue_myservice", 18 | ], 19 | ) 20 | 21 | cue_exported_files( 22 | name = "prod", 23 | srcs = ["prod.cue"], 24 | module = "//test/testdata/myservice/cue.mod", 25 | visibility = ["//test:__subpackages__"], 26 | deps = [ 27 | "//test/testdata/myservice:cue_myservice", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /test/testdata/myservice/environments/dev-golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "name": "myservice", 6 | "namespace": "myservice", 7 | "labels": { 8 | "app": "myservice" 9 | } 10 | }, 11 | "spec": { 12 | "replicas": 1, 13 | "selector": { 14 | "matchLabels": { 15 | "app": "myservice" 16 | } 17 | }, 18 | "strategy": { 19 | "type": "Recreate" 20 | }, 21 | "template": { 22 | "metadata": { 23 | "labels": { 24 | "app": "myservice" 25 | } 26 | }, 27 | "spec": { 28 | "volumes": [ 29 | { 30 | "name": "config", 31 | "configMap": { 32 | "name": "myservice" 33 | } 34 | }, 35 | { 36 | "name": "credentials", 37 | "secret": { 38 | "secretName": "credentials" 39 | } 40 | } 41 | ], 42 | "containers": [ 43 | { 44 | "name": "myservice", 45 | "image": "myorg/myservice:latest", 46 | "imagePullPolicy": "Always", 47 | "lifecycle": { 48 | "postStart": { 49 | "exec": { 50 | "command": [ 51 | "/busybox/sh", 52 | "-c", 53 | "chmod 400 /etc/myservice/secure/*" 54 | ] 55 | } 56 | } 57 | }, 58 | "volumeMounts": [ 59 | { 60 | "name": "config", 61 | "mountPath": "/etc/myservice/config.yaml", 62 | "subPath": "config.yaml" 63 | }, 64 | { 65 | "name": "credentials", 66 | "mountPath": "/etc/myservice/secure" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/testdata/myservice/environments/dev.cue: -------------------------------------------------------------------------------- 1 | import ( 2 | "example.com/myservice" 3 | ) 4 | 5 | myservice.#MyDeployment & { 6 | #Replicas: 1 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/myservice/environments/prod-golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "name": "myservice", 6 | "namespace": "myservice", 7 | "labels": { 8 | "app": "myservice" 9 | } 10 | }, 11 | "spec": { 12 | "replicas": 3, 13 | "selector": { 14 | "matchLabels": { 15 | "app": "myservice" 16 | } 17 | }, 18 | "strategy": { 19 | "type": "Recreate" 20 | }, 21 | "template": { 22 | "metadata": { 23 | "labels": { 24 | "app": "myservice" 25 | } 26 | }, 27 | "spec": { 28 | "volumes": [ 29 | { 30 | "name": "config", 31 | "configMap": { 32 | "name": "myservice" 33 | } 34 | }, 35 | { 36 | "name": "credentials", 37 | "secret": { 38 | "secretName": "credentials" 39 | } 40 | } 41 | ], 42 | "containers": [ 43 | { 44 | "name": "myservice", 45 | "image": "myorg/myservice:latest", 46 | "imagePullPolicy": "Always", 47 | "lifecycle": { 48 | "postStart": { 49 | "exec": { 50 | "command": [ 51 | "/busybox/sh", 52 | "-c", 53 | "chmod 400 /etc/myservice/secure/*" 54 | ] 55 | } 56 | } 57 | }, 58 | "volumeMounts": [ 59 | { 60 | "name": "config", 61 | "mountPath": "/etc/myservice/config.yaml", 62 | "subPath": "config.yaml" 63 | }, 64 | { 65 | "name": "credentials", 66 | "mountPath": "/etc/myservice/secure" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/testdata/myservice/environments/prod.cue: -------------------------------------------------------------------------------- 1 | import ( 2 | "example.com/myservice" 3 | ) 4 | 5 | myservice.#MyDeployment & { 6 | #Replicas: 3 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/myservice/my-deployment.cue: -------------------------------------------------------------------------------- 1 | package myservice 2 | 3 | import ( 4 | appsv1 "k8s.io/api/apps/v1" 5 | ) 6 | 7 | #MyDeployment: appsv1.#Deployment & { 8 | #Replicas: int 9 | 10 | apiVersion: "extensions/v1beta1" 11 | kind: "Deployment" 12 | metadata: { 13 | name: "myservice" 14 | namespace: "myservice" 15 | labels: app: "myservice" 16 | } 17 | spec: { 18 | replicas: #Replicas 19 | strategy: type: "Recreate" 20 | selector: matchLabels: app: "myservice" 21 | template: { 22 | metadata: { 23 | labels: app: "myservice" 24 | } 25 | spec: { 26 | volumes: [{ 27 | name: "config" 28 | configMap: name: "myservice" 29 | }, { 30 | name: "credentials" 31 | secret: secretName: "credentials" 32 | }] 33 | containers: [{ 34 | name: "myservice" 35 | image: "myorg/myservice:latest" 36 | imagePullPolicy: "Always" 37 | volumeMounts: [{ 38 | name: "config" 39 | mountPath: "/etc/myservice/config.yaml" 40 | subPath: "config.yaml" 41 | }, { 42 | name: "credentials" 43 | mountPath: "/etc/myservice/secure" 44 | }] 45 | lifecycle: postStart: exec: command: [ 46 | "/busybox/sh", 47 | "-c", 48 | "chmod 400 /etc/myservice/secure/*", 49 | ] 50 | }] 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/testdata/non-cue/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_consolidated_standalone_files", 4 | ) 5 | 6 | exports_files(["non-cue-golden.cue"]) 7 | 8 | cue_consolidated_standalone_files( 9 | name = "non-cue", 10 | srcs = [ 11 | "merge.cue", 12 | "name.txt", 13 | ], 14 | output_package_name = "merged", 15 | path = [ 16 | "name:", 17 | ], 18 | visibility = ["//test:__subpackages__"], 19 | ) 20 | -------------------------------------------------------------------------------- /test/testdata/non-cue/merge.cue: -------------------------------------------------------------------------------- 1 | package merged 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | name: strings.MinRunes(1) 8 | -------------------------------------------------------------------------------- /test/testdata/non-cue/name.txt: -------------------------------------------------------------------------------- 1 | Aardvark -------------------------------------------------------------------------------- /test/testdata/non-cue/non-cue-golden.cue: -------------------------------------------------------------------------------- 1 | package merged 2 | 3 | import "strings" 4 | 5 | name: strings.MinRunes(1) & { 6 | "Aardvark" 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/path/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_exported_standalone_files", 4 | ) 5 | 6 | exports_files([ 7 | "raw_text-golden.json", 8 | "multiple-golden.json", 9 | ]) 10 | 11 | cue_exported_standalone_files( 12 | name = "raw_text", 13 | srcs = ["raw.txt"], 14 | path = [ 15 | "top:", 16 | "mid:", 17 | ], 18 | visibility = ["//test:__subpackages__"], 19 | ) 20 | 21 | cue_exported_standalone_files( 22 | name = "multiple", 23 | srcs = [ 24 | "path.cue", 25 | "raw.txt", 26 | ], 27 | path = [ 28 | "path.Base(filename)", 29 | ], 30 | result = "multiple.json", 31 | visibility = ["//test:__subpackages__"], 32 | with_context = True, 33 | ) 34 | -------------------------------------------------------------------------------- /test/testdata/path/multiple-golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 1, 3 | "b": 2, 4 | "raw.txt": "Hello!" 5 | } 6 | -------------------------------------------------------------------------------- /test/testdata/path/path.cue: -------------------------------------------------------------------------------- 1 | a: 1 2 | b: 2 3 | -------------------------------------------------------------------------------- /test/testdata/path/raw.txt: -------------------------------------------------------------------------------- 1 | Hello! -------------------------------------------------------------------------------- /test/testdata/path/raw_text-golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": { 3 | "mid": "Hello!" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/testdata/stamp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "//cue:cue.bzl", 3 | "cue_exported_standalone_files", 4 | ) 5 | 6 | exports_files(["stamp-golden.json"]) 7 | 8 | cue_exported_standalone_files( 9 | name = "stamp", 10 | srcs = ["stamped.cue"], 11 | inject = { 12 | "builtat": "{BUILD_TIMESTAMP}", 13 | "builtby": "{BUILD_USER}", 14 | "message": "Goodbye.", 15 | }, 16 | stamping_policy = "Force", 17 | visibility = ["//test:__subpackages__"], 18 | ) 19 | -------------------------------------------------------------------------------- /test/testdata/stamp/stamp-golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "builtAtWidth": 20, 3 | "builtByPopulated": true, 4 | "message": "Goodbye." 5 | } 6 | -------------------------------------------------------------------------------- /test/testdata/stamp/stamped.cue: -------------------------------------------------------------------------------- 1 | import ( 2 | "strings" 3 | "time" 4 | ) 5 | 6 | _builtAtSecondsSinceEpoch: int @tag(builtat,type=int) 7 | _builtAt: time.Unix(_builtAtSecondsSinceEpoch, 0) 8 | // Emit something stable that we can check in unit tests at any time. CUE's time.Unix uses RFC 3339 9 | // format which guarantees a fixed width so long as the time is captured in UTC. 10 | builtAtWidth: len(strings.Runes(_builtAt)) 11 | // In order to allow tests to pass when run by various users, don't actually emit the value injected 12 | // for the "BUILD_USER" placeholder here. Instead, just mandate that some nonempty value makes it in 13 | // by way of injection. 14 | _builtBy: string | *"" @tag(builtby) 15 | builtByPopulated: len(_builtBy) > 0 16 | message: string | *"Hello." @tag(message) 17 | -------------------------------------------------------------------------------- /tools/cmd/replace-stamps/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_go//go:def.bzl", "go_binary", "go_library") 2 | 3 | go_library( 4 | name = "replace_stamps_lib", 5 | srcs = ["replace.go"], 6 | importpath = "github.com/seh/rules_cue/tools/cmd/replace-stamps", 7 | visibility = ["//visibility:private"], 8 | ) 9 | 10 | go_binary( 11 | name = "replace-stamps", 12 | embed = [":replace_stamps_lib"], 13 | visibility = ["//visibility:public"], 14 | ) 15 | -------------------------------------------------------------------------------- /tools/cmd/replace-stamps/replace.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "regexp" 12 | "strings" 13 | "sync" 14 | ) 15 | 16 | const usageExitCode = 2 17 | 18 | func fatalln(code int, args ...interface{}) { 19 | fmt.Fprintln(os.Stderr, args...) 20 | os.Exit(code) 21 | } 22 | 23 | func fatalf(code int, format string, args ...interface{}) { 24 | fmt.Fprintf(os.Stderr, format, args...) 25 | os.Exit(code) 26 | } 27 | 28 | var ( 29 | prohibitedPattern *regexp.Regexp 30 | once sync.Once 31 | ) 32 | 33 | // Adapted from https://github.com/alessio/shellescape/blob/v1.4.1/shellescape.go: 34 | // (Avoid the dependency here.) 35 | func quoteShellArg(s string) string { 36 | if len(s) == 0 { 37 | return "''" 38 | } 39 | once.Do(func() { 40 | prohibitedPattern = regexp.MustCompile(`[^\w@%+=:,./-]`) 41 | }) 42 | if prohibitedPattern.MatchString(s) { 43 | return "'" + strings.ReplaceAll(s, "'", `'"'"'`) + "'" 44 | } 45 | return s 46 | } 47 | 48 | func readStampBindings(bindingFile string) (b map[string]string, err error) { 49 | f, err := os.Open(bindingFile) 50 | if err != nil { 51 | if errors.Is(err, os.ErrNotExist) { 52 | return nil, fmt.Errorf("binding file %q does not exist", bindingFile) 53 | } 54 | var pe *os.PathError 55 | errors.As(err, &pe) 56 | return nil, fmt.Errorf("failed to determine whether binding file %q exists: %w", bindingFile, pe.Err) 57 | } 58 | defer func() { 59 | if ferr := f.Close(); ferr != nil && err == nil { 60 | err = ferr 61 | b = nil 62 | } 63 | }() 64 | 65 | // Basis of inspiration: 66 | // https://github.com/bazelbuild/rules_go/blob/4cd45a2ac59bd00ba54d23ebbdb7e5e2aed69007/go/tools/builders/link.go#L76-L97 67 | bindings := make(map[string]string, 10) 68 | scanner := bufio.NewScanner(f) 69 | for scanner.Scan() { 70 | line := strings.SplitN(scanner.Text(), " ", 2) 71 | switch len(line) { 72 | case 0: 73 | // Blank line 74 | case 1: 75 | // Empty binding 76 | bindings[line[0]] = "" 77 | case 2: 78 | bindings[line[0]] = line[1] 79 | } 80 | } 81 | if err := scanner.Err(); err != nil { 82 | return nil, fmt.Errorf("failed reading binding file: %q: %w", bindingFile, err) 83 | } 84 | return bindings, nil 85 | } 86 | 87 | func replacePlaceholderValues(w io.Writer, r io.Reader, stampBindings map[string]string, prefix string) error { 88 | scanner := bufio.NewScanner(r) 89 | for scanner.Scan() { 90 | binding := scanner.Text() 91 | if len(binding) == 0 { 92 | continue 93 | } 94 | eq := strings.IndexByte(binding, '=') 95 | switch eq { 96 | case -1: 97 | return fmt.Errorf("placeholder line lacks '=': %q", binding) 98 | case 0: 99 | return fmt.Errorf("placeholder line has empty key: %q", binding) 100 | default: 101 | // Omit replacements for placeholders that match no stamp binding. 102 | if replacement, ok := stampBindings[binding[eq+1:]]; ok { 103 | fmt.Fprintf(w, "%s%s\n", prefix, quoteShellArg(binding[:eq]+"="+replacement)) 104 | } 105 | } 106 | 107 | } 108 | return scanner.Err() 109 | } 110 | 111 | func replacePlaceholderValuesIn(placeholderFile string, w io.Writer, stampBindings map[string]string, prefix string) (err error) { 112 | consume := func(r io.Reader) error { 113 | return replacePlaceholderValues(w, r, stampBindings, prefix) 114 | } 115 | if placeholderFile == "-" { 116 | return consume(os.Stdin) 117 | } 118 | f, err := os.Open(placeholderFile) 119 | if err != nil { 120 | if errors.Is(err, os.ErrNotExist) { 121 | return fmt.Errorf("placeholder file %q does not exist", placeholderFile) 122 | } 123 | var pe *os.PathError 124 | errors.As(err, &pe) 125 | return fmt.Errorf("failed to determine whether placeholder file %q exists: %w", placeholderFile, pe.Err) 126 | } 127 | defer func() { 128 | if ferr := f.Close(); ferr != nil && err == nil { 129 | err = ferr 130 | } 131 | }() 132 | return consume(f) 133 | } 134 | 135 | func main() { 136 | prefix := flag.String("prefix", "", 137 | "Prefix to insert before each emitted line of stamped replacements") 138 | output := flag.String("output", "", 139 | "`File path` to which to write output ('-' means standard output)") 140 | flag.Parse() 141 | args := flag.Args() 142 | 143 | switch len(args) { 144 | case 0: 145 | fatalf(usageExitCode, "%s: at least argument nominating the required replacement file is required.\n", os.Args[0]) 146 | case 1: 147 | return 148 | } 149 | 150 | var stampBindings map[string]string 151 | for _, f := range args[1:] { 152 | overlay, err := readStampBindings(f) 153 | if err != nil { 154 | fatalln(1, err) 155 | } 156 | if stampBindings == nil { 157 | stampBindings = overlay 158 | } else { 159 | for k, v := range overlay { 160 | stampBindings[k] = v 161 | } 162 | } 163 | } 164 | 165 | replace := func(w io.Writer) error { 166 | return replacePlaceholderValuesIn(args[0], w, stampBindings, *prefix) 167 | } 168 | if *output == "" || *output == "-" { 169 | if err := replace(os.Stdout); err != nil { 170 | fatalln(1, err) 171 | } 172 | return 173 | } 174 | // Don't try writing to the directory reported by os.TempDir, as it's likely sitting within a 175 | // different filesystem from the directory containing the eventual output file. Our later 176 | // attempt to move a file (via os.Rename) between two different filesystems will fail. Instead, 177 | // write the temporary file as a sibling to the eventual output file. 178 | f, err := os.CreateTemp(filepath.Dir(*output), "replace-") 179 | if err != nil { 180 | fatalln(1, "failed to create temporary output file:", err) 181 | } 182 | if err := replace(f); err != nil { 183 | if rerr := os.Remove(f.Name()); rerr != nil { 184 | fmt.Fprintf(os.Stderr, "failed to delete temporary output file %q: %v\n", f.Name(), rerr) 185 | } 186 | fatalln(1, err) 187 | } 188 | if err := f.Close(); err != nil { 189 | fatalf(1, "failed to close temporary output file %q: %v\n", f.Name(), err) 190 | } 191 | if err := os.Rename(f.Name(), *output); err != nil { 192 | fatalf(1, "failed to move temporary output file %q to destination file %q: %v\n", f.Name(), *output, err) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /tools/cue/BUILD.bazel: -------------------------------------------------------------------------------- 1 | toolchain_type( 2 | name = "toolchain_type", 3 | visibility = ["//visibility:public"], 4 | ) 5 | --------------------------------------------------------------------------------