├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── embark-oss-ci.yml │ └── validate-projects.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── content ├── beliefs-strategy.md ├── how-to.md ├── maintenance-guidelines.md └── tooling.md └── src ├── error.rs ├── github.rs ├── github └── codeowners.rs ├── main.rs ├── policy.rs ├── slack.rs ├── validate.rs └── validate ├── context.rs ├── project.rs └── tests.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @vojd 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Install the program by... 16 | 2. Run the program with these arguments... 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If useful, add screenshots to help explain your problem. 23 | 24 | **Code example** 25 | If possible, share a minimal code reproduction of the problem. 26 | 27 | **Environment:** 28 | - OS: [e.g. Windows 10, iOS 14] 29 | - Project version [e.g. 22] 30 | - Rust version [e.g. 1.47] (if applicable) 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Checklist 2 | 3 | * [ ] I have read the [Contributor Guide](https://github.com/EmbarkStudios/opensource/blob/main/CONTRIBUTING.md) 4 | * [ ] I have read and agree to the [Code of Conduct](https://github.com/EmbarkStudios/opensource/blob/main/CODE_OF_CONDUCT.md) 5 | * [ ] I have added a description of my changes and why I'd like them included in the section below 6 | 7 | ### Description of Changes 8 | 9 | Describe your changes here 10 | 11 | ### Related Issues 12 | 13 | List related issues here 14 | -------------------------------------------------------------------------------- /.github/workflows/embark-oss-ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | tags: 6 | - "*" 7 | pull_request: 8 | 9 | # Cancel PR actions when new commits are pushed 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | name: embark-oss CI 15 | jobs: 16 | lint: 17 | name: Lint 18 | runs-on: ubuntu-20.04 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: stable 24 | override: true 25 | 26 | # make sure all code has been formatted with rustfmt 27 | - run: rustup component add rustfmt 28 | - name: check rustfmt 29 | run: cargo fmt -- --check --color always 30 | 31 | # run clippy to verify we have no warnings 32 | - run: rustup component add clippy 33 | - run: cargo fetch 34 | - run: cargo clippy --tests -- -D warnings 35 | 36 | test: 37 | name: Test 38 | runs-on: ubuntu-20.04 39 | steps: 40 | - uses: actions/checkout@v2 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | toolchain: stable 44 | override: true 45 | - run: cargo fetch 46 | - run: cargo build --tests 47 | - run: cargo test 48 | -------------------------------------------------------------------------------- /.github/workflows/validate-projects.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | # At 11:00 on every day-of-week from Monday through Friday. See https://crontab.guru/#0_11_*_*_1-5 4 | - cron: "0 11 * * 1-5" 5 | 6 | name: embark-oss CI 7 | jobs: 8 | validate_projects: 9 | name: Validate projects 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | override: true 17 | 18 | - run: cargo fetch 19 | - run: cargo build 20 | - run: cargo run validate-all --slack-webhook-url ${{ secrets.SLACK_WEBHOOK_URL }} --github-api-token ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 2020-09-21 5 | - Initialised the repo 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@embark-studios.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Embark Contributor Guidelines 2 | 3 | Welcome! This project is created by the team at [Embark Studios](https://embark.games). We're glad you're interested in contributing! We welcome contributions from people of all backgrounds who are interested in making great software with us. 4 | 5 | At Embark, we aspire to empower everyone to create interactive experiences. To do this, we're exploring and pushing the boundaries of new technologies, and sharing our learnings with the open source community. 6 | 7 | If you have any difficulties getting involved or finding answers to your questions, please don't hesitate to ask your questions in our [Discord server](https://discord.com/invite/8TW9nfF). 8 | 9 | If you have ideas for collaboration, email us at opensource@embark-studios.com. 10 | 11 | We're also hiring full-time engineers to work with us in Stockholm! Check out our current job postings [here](https://www.embark-studios.com/jobs). 12 | 13 | ## Issues 14 | 15 | ### Feature Requests 16 | 17 | If you have ideas or how to improve our projects, you can suggest features by opening a GitHub issue. Make sure to include details about the feature or change, and describe any uses cases it would enable. 18 | 19 | Feature requests will be tagged as `enhancement` and their status will be updated in the comments of the issue. 20 | 21 | ### Bugs 22 | 23 | When reporting a bug or unexpected behaviour in a project, make sure your issue describes steps to reproduce the behaviour, including the platform you were using, what steps you took, and any error messages. 24 | 25 | Reproducible bugs will be tagged as `bug` and their status will be updated in the comments of the issue. 26 | 27 | ### Wontfix 28 | 29 | Issues will be closed and tagged as `wontfix` if we decide that we do not wish to implement it, usually due to being misaligned with the project vision or out of scope. We will comment on the issue with more detailed reasoning. 30 | 31 | ## Contribution Workflow 32 | 33 | ### Open Issues 34 | 35 | If you're ready to contribute, start by looking at our open issues tagged as [`help wanted`](../../issues?q=is%3Aopen+is%3Aissue+label%3A"help+wanted") or [`good first issue`](../../issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue"). 36 | 37 | You can comment on the issue to let others know you're interested in working on it or to ask questions. 38 | 39 | ### Making Changes 40 | 41 | 1. Fork the repository. 42 | 43 | 2. Create a new feature branch. 44 | 45 | 3. Make your changes. Ensure that there are no build errors by running the project with your changes locally. 46 | 47 | 4. Open a pull request with a name and description of what you did. You can read more about working with pull requests on GitHub [here](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork). 48 | 49 | 5. A maintainer will review your pull request and may ask you to make changes. 50 | 51 | ## Code Guidelines 52 | 53 | ### Rust 54 | 55 | You can read about our standards and recommendations for working with Rust [here](https://github.com/EmbarkStudios/rust-ecosystem/blob/main/guidelines.md). 56 | 57 | ### Python 58 | 59 | We recommend following [PEP8 conventions](https://www.python.org/dev/peps/pep-0008/) when working with Python modules. 60 | 61 | ### JavaScript 62 | 63 | We follow the [AirBnB JavaScript style guide](https://github.com/airbnb/javascript). You can find the ESLint configuration in relevant repositories. 64 | 65 | ## Licensing 66 | 67 | Unless otherwise specified, all Embark open source projects are licensed under a dual MIT OR Apache-2.0 license, allowing licensees to chose either at their option. You can read more in each project's respective README. 68 | 69 | ## Code of Conduct 70 | 71 | Please note that our projects are released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md) to ensure that they are welcoming places for everyone to contribute. By participating in any Embark open source project, you agree to abide by these terms. 72 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.12.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "atty" 25 | version = "0.2.14" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 28 | dependencies = [ 29 | "hermit-abi 0.1.19", 30 | "libc", 31 | "winapi", 32 | ] 33 | 34 | [[package]] 35 | name = "autocfg" 36 | version = "1.1.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 39 | 40 | [[package]] 41 | name = "base64" 42 | version = "0.21.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" 45 | 46 | [[package]] 47 | name = "bitflags" 48 | version = "1.3.2" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 51 | 52 | [[package]] 53 | name = "bumpalo" 54 | version = "3.12.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" 57 | 58 | [[package]] 59 | name = "bytes" 60 | version = "1.4.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 63 | 64 | [[package]] 65 | name = "cc" 66 | version = "1.0.79" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 69 | 70 | [[package]] 71 | name = "cfg-if" 72 | version = "1.0.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 75 | 76 | [[package]] 77 | name = "clap" 78 | version = "2.34.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 81 | dependencies = [ 82 | "ansi_term", 83 | "atty", 84 | "bitflags", 85 | "strsim", 86 | "textwrap", 87 | "unicode-width", 88 | "vec_map", 89 | ] 90 | 91 | [[package]] 92 | name = "either" 93 | version = "1.8.1" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 96 | 97 | [[package]] 98 | name = "embark-oss" 99 | version = "0.1.0" 100 | dependencies = [ 101 | "eyre", 102 | "futures", 103 | "itertools", 104 | "lazy_static", 105 | "regex", 106 | "reqwest", 107 | "serde", 108 | "serde_json", 109 | "structopt", 110 | "tokio", 111 | ] 112 | 113 | [[package]] 114 | name = "encoding_rs" 115 | version = "0.8.32" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" 118 | dependencies = [ 119 | "cfg-if", 120 | ] 121 | 122 | [[package]] 123 | name = "eyre" 124 | version = "0.6.8" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" 127 | dependencies = [ 128 | "indenter", 129 | "once_cell", 130 | ] 131 | 132 | [[package]] 133 | name = "fnv" 134 | version = "1.0.7" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 137 | 138 | [[package]] 139 | name = "form_urlencoded" 140 | version = "1.1.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 143 | dependencies = [ 144 | "percent-encoding", 145 | ] 146 | 147 | [[package]] 148 | name = "futures" 149 | version = "0.3.28" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" 152 | dependencies = [ 153 | "futures-channel", 154 | "futures-core", 155 | "futures-executor", 156 | "futures-io", 157 | "futures-sink", 158 | "futures-task", 159 | "futures-util", 160 | ] 161 | 162 | [[package]] 163 | name = "futures-channel" 164 | version = "0.3.28" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 167 | dependencies = [ 168 | "futures-core", 169 | "futures-sink", 170 | ] 171 | 172 | [[package]] 173 | name = "futures-core" 174 | version = "0.3.28" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 177 | 178 | [[package]] 179 | name = "futures-executor" 180 | version = "0.3.28" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" 183 | dependencies = [ 184 | "futures-core", 185 | "futures-task", 186 | "futures-util", 187 | ] 188 | 189 | [[package]] 190 | name = "futures-io" 191 | version = "0.3.28" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" 194 | 195 | [[package]] 196 | name = "futures-macro" 197 | version = "0.3.28" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" 200 | dependencies = [ 201 | "proc-macro2", 202 | "quote", 203 | "syn 2.0.15", 204 | ] 205 | 206 | [[package]] 207 | name = "futures-sink" 208 | version = "0.3.28" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 211 | 212 | [[package]] 213 | name = "futures-task" 214 | version = "0.3.28" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 217 | 218 | [[package]] 219 | name = "futures-util" 220 | version = "0.3.28" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 223 | dependencies = [ 224 | "futures-channel", 225 | "futures-core", 226 | "futures-io", 227 | "futures-macro", 228 | "futures-sink", 229 | "futures-task", 230 | "memchr", 231 | "pin-project-lite", 232 | "pin-utils", 233 | "slab", 234 | ] 235 | 236 | [[package]] 237 | name = "h2" 238 | version = "0.3.18" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" 241 | dependencies = [ 242 | "bytes", 243 | "fnv", 244 | "futures-core", 245 | "futures-sink", 246 | "futures-util", 247 | "http", 248 | "indexmap", 249 | "slab", 250 | "tokio", 251 | "tokio-util", 252 | "tracing", 253 | ] 254 | 255 | [[package]] 256 | name = "hashbrown" 257 | version = "0.12.3" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 260 | 261 | [[package]] 262 | name = "heck" 263 | version = "0.3.3" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 266 | dependencies = [ 267 | "unicode-segmentation", 268 | ] 269 | 270 | [[package]] 271 | name = "hermit-abi" 272 | version = "0.1.19" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 275 | dependencies = [ 276 | "libc", 277 | ] 278 | 279 | [[package]] 280 | name = "hermit-abi" 281 | version = "0.2.6" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 284 | dependencies = [ 285 | "libc", 286 | ] 287 | 288 | [[package]] 289 | name = "http" 290 | version = "0.2.9" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 293 | dependencies = [ 294 | "bytes", 295 | "fnv", 296 | "itoa", 297 | ] 298 | 299 | [[package]] 300 | name = "http-body" 301 | version = "0.4.5" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 304 | dependencies = [ 305 | "bytes", 306 | "http", 307 | "pin-project-lite", 308 | ] 309 | 310 | [[package]] 311 | name = "httparse" 312 | version = "1.8.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 315 | 316 | [[package]] 317 | name = "httpdate" 318 | version = "1.0.2" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 321 | 322 | [[package]] 323 | name = "hyper" 324 | version = "0.14.26" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" 327 | dependencies = [ 328 | "bytes", 329 | "futures-channel", 330 | "futures-core", 331 | "futures-util", 332 | "h2", 333 | "http", 334 | "http-body", 335 | "httparse", 336 | "httpdate", 337 | "itoa", 338 | "pin-project-lite", 339 | "socket2", 340 | "tokio", 341 | "tower-service", 342 | "tracing", 343 | "want", 344 | ] 345 | 346 | [[package]] 347 | name = "hyper-rustls" 348 | version = "0.23.2" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" 351 | dependencies = [ 352 | "http", 353 | "hyper", 354 | "rustls", 355 | "tokio", 356 | "tokio-rustls", 357 | ] 358 | 359 | [[package]] 360 | name = "idna" 361 | version = "0.3.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 364 | dependencies = [ 365 | "unicode-bidi", 366 | "unicode-normalization", 367 | ] 368 | 369 | [[package]] 370 | name = "indenter" 371 | version = "0.3.3" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 374 | 375 | [[package]] 376 | name = "indexmap" 377 | version = "1.9.3" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 380 | dependencies = [ 381 | "autocfg", 382 | "hashbrown", 383 | ] 384 | 385 | [[package]] 386 | name = "ipnet" 387 | version = "2.7.2" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" 390 | 391 | [[package]] 392 | name = "itertools" 393 | version = "0.10.5" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 396 | dependencies = [ 397 | "either", 398 | ] 399 | 400 | [[package]] 401 | name = "itoa" 402 | version = "1.0.6" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 405 | 406 | [[package]] 407 | name = "js-sys" 408 | version = "0.3.61" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 411 | dependencies = [ 412 | "wasm-bindgen", 413 | ] 414 | 415 | [[package]] 416 | name = "lazy_static" 417 | version = "1.4.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 420 | 421 | [[package]] 422 | name = "libc" 423 | version = "0.2.144" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" 426 | 427 | [[package]] 428 | name = "lock_api" 429 | version = "0.4.9" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 432 | dependencies = [ 433 | "autocfg", 434 | "scopeguard", 435 | ] 436 | 437 | [[package]] 438 | name = "log" 439 | version = "0.4.17" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 442 | dependencies = [ 443 | "cfg-if", 444 | ] 445 | 446 | [[package]] 447 | name = "memchr" 448 | version = "2.5.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 451 | 452 | [[package]] 453 | name = "mime" 454 | version = "0.3.17" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 457 | 458 | [[package]] 459 | name = "mio" 460 | version = "0.8.6" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 463 | dependencies = [ 464 | "libc", 465 | "log", 466 | "wasi", 467 | "windows-sys 0.45.0", 468 | ] 469 | 470 | [[package]] 471 | name = "num_cpus" 472 | version = "1.15.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 475 | dependencies = [ 476 | "hermit-abi 0.2.6", 477 | "libc", 478 | ] 479 | 480 | [[package]] 481 | name = "once_cell" 482 | version = "1.17.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 485 | 486 | [[package]] 487 | name = "parking_lot" 488 | version = "0.12.1" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 491 | dependencies = [ 492 | "lock_api", 493 | "parking_lot_core", 494 | ] 495 | 496 | [[package]] 497 | name = "parking_lot_core" 498 | version = "0.9.7" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 501 | dependencies = [ 502 | "cfg-if", 503 | "libc", 504 | "redox_syscall", 505 | "smallvec", 506 | "windows-sys 0.45.0", 507 | ] 508 | 509 | [[package]] 510 | name = "percent-encoding" 511 | version = "2.2.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 514 | 515 | [[package]] 516 | name = "pin-project-lite" 517 | version = "0.2.9" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 520 | 521 | [[package]] 522 | name = "pin-utils" 523 | version = "0.1.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 526 | 527 | [[package]] 528 | name = "proc-macro-error" 529 | version = "1.0.4" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 532 | dependencies = [ 533 | "proc-macro-error-attr", 534 | "proc-macro2", 535 | "quote", 536 | "syn 1.0.109", 537 | "version_check", 538 | ] 539 | 540 | [[package]] 541 | name = "proc-macro-error-attr" 542 | version = "1.0.4" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 545 | dependencies = [ 546 | "proc-macro2", 547 | "quote", 548 | "version_check", 549 | ] 550 | 551 | [[package]] 552 | name = "proc-macro2" 553 | version = "1.0.56" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 556 | dependencies = [ 557 | "unicode-ident", 558 | ] 559 | 560 | [[package]] 561 | name = "quote" 562 | version = "1.0.27" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" 565 | dependencies = [ 566 | "proc-macro2", 567 | ] 568 | 569 | [[package]] 570 | name = "redox_syscall" 571 | version = "0.2.16" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 574 | dependencies = [ 575 | "bitflags", 576 | ] 577 | 578 | [[package]] 579 | name = "regex" 580 | version = "1.8.1" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" 583 | dependencies = [ 584 | "aho-corasick", 585 | "memchr", 586 | "regex-syntax", 587 | ] 588 | 589 | [[package]] 590 | name = "regex-syntax" 591 | version = "0.7.1" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" 594 | 595 | [[package]] 596 | name = "reqwest" 597 | version = "0.11.17" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" 600 | dependencies = [ 601 | "base64", 602 | "bytes", 603 | "encoding_rs", 604 | "futures-core", 605 | "futures-util", 606 | "h2", 607 | "http", 608 | "http-body", 609 | "hyper", 610 | "hyper-rustls", 611 | "ipnet", 612 | "js-sys", 613 | "log", 614 | "mime", 615 | "once_cell", 616 | "percent-encoding", 617 | "pin-project-lite", 618 | "rustls", 619 | "rustls-pemfile", 620 | "serde", 621 | "serde_json", 622 | "serde_urlencoded", 623 | "tokio", 624 | "tokio-rustls", 625 | "tower-service", 626 | "url", 627 | "wasm-bindgen", 628 | "wasm-bindgen-futures", 629 | "web-sys", 630 | "webpki-roots", 631 | "winreg", 632 | ] 633 | 634 | [[package]] 635 | name = "ring" 636 | version = "0.16.20" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 639 | dependencies = [ 640 | "cc", 641 | "libc", 642 | "once_cell", 643 | "spin", 644 | "untrusted", 645 | "web-sys", 646 | "winapi", 647 | ] 648 | 649 | [[package]] 650 | name = "rustls" 651 | version = "0.20.9" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" 654 | dependencies = [ 655 | "log", 656 | "ring", 657 | "sct", 658 | "webpki", 659 | ] 660 | 661 | [[package]] 662 | name = "rustls-pemfile" 663 | version = "1.0.3" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" 666 | dependencies = [ 667 | "base64", 668 | ] 669 | 670 | [[package]] 671 | name = "ryu" 672 | version = "1.0.13" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 675 | 676 | [[package]] 677 | name = "scopeguard" 678 | version = "1.1.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 681 | 682 | [[package]] 683 | name = "sct" 684 | version = "0.7.0" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 687 | dependencies = [ 688 | "ring", 689 | "untrusted", 690 | ] 691 | 692 | [[package]] 693 | name = "serde" 694 | version = "1.0.162" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" 697 | dependencies = [ 698 | "serde_derive", 699 | ] 700 | 701 | [[package]] 702 | name = "serde_derive" 703 | version = "1.0.162" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" 706 | dependencies = [ 707 | "proc-macro2", 708 | "quote", 709 | "syn 2.0.15", 710 | ] 711 | 712 | [[package]] 713 | name = "serde_json" 714 | version = "1.0.96" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" 717 | dependencies = [ 718 | "itoa", 719 | "ryu", 720 | "serde", 721 | ] 722 | 723 | [[package]] 724 | name = "serde_urlencoded" 725 | version = "0.7.1" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 728 | dependencies = [ 729 | "form_urlencoded", 730 | "itoa", 731 | "ryu", 732 | "serde", 733 | ] 734 | 735 | [[package]] 736 | name = "signal-hook-registry" 737 | version = "1.4.1" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 740 | dependencies = [ 741 | "libc", 742 | ] 743 | 744 | [[package]] 745 | name = "slab" 746 | version = "0.4.8" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 749 | dependencies = [ 750 | "autocfg", 751 | ] 752 | 753 | [[package]] 754 | name = "smallvec" 755 | version = "1.10.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 758 | 759 | [[package]] 760 | name = "socket2" 761 | version = "0.4.9" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 764 | dependencies = [ 765 | "libc", 766 | "winapi", 767 | ] 768 | 769 | [[package]] 770 | name = "spin" 771 | version = "0.5.2" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 774 | 775 | [[package]] 776 | name = "strsim" 777 | version = "0.8.0" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 780 | 781 | [[package]] 782 | name = "structopt" 783 | version = "0.3.26" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 786 | dependencies = [ 787 | "clap", 788 | "lazy_static", 789 | "structopt-derive", 790 | ] 791 | 792 | [[package]] 793 | name = "structopt-derive" 794 | version = "0.4.18" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 797 | dependencies = [ 798 | "heck", 799 | "proc-macro-error", 800 | "proc-macro2", 801 | "quote", 802 | "syn 1.0.109", 803 | ] 804 | 805 | [[package]] 806 | name = "syn" 807 | version = "1.0.109" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 810 | dependencies = [ 811 | "proc-macro2", 812 | "quote", 813 | "unicode-ident", 814 | ] 815 | 816 | [[package]] 817 | name = "syn" 818 | version = "2.0.15" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 821 | dependencies = [ 822 | "proc-macro2", 823 | "quote", 824 | "unicode-ident", 825 | ] 826 | 827 | [[package]] 828 | name = "textwrap" 829 | version = "0.11.0" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 832 | dependencies = [ 833 | "unicode-width", 834 | ] 835 | 836 | [[package]] 837 | name = "tinyvec" 838 | version = "1.6.0" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 841 | dependencies = [ 842 | "tinyvec_macros", 843 | ] 844 | 845 | [[package]] 846 | name = "tinyvec_macros" 847 | version = "0.1.1" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 850 | 851 | [[package]] 852 | name = "tokio" 853 | version = "1.28.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" 856 | dependencies = [ 857 | "autocfg", 858 | "bytes", 859 | "libc", 860 | "mio", 861 | "num_cpus", 862 | "parking_lot", 863 | "pin-project-lite", 864 | "signal-hook-registry", 865 | "socket2", 866 | "tokio-macros", 867 | "windows-sys 0.48.0", 868 | ] 869 | 870 | [[package]] 871 | name = "tokio-macros" 872 | version = "2.1.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" 875 | dependencies = [ 876 | "proc-macro2", 877 | "quote", 878 | "syn 2.0.15", 879 | ] 880 | 881 | [[package]] 882 | name = "tokio-rustls" 883 | version = "0.23.4" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 886 | dependencies = [ 887 | "rustls", 888 | "tokio", 889 | "webpki", 890 | ] 891 | 892 | [[package]] 893 | name = "tokio-util" 894 | version = "0.7.8" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" 897 | dependencies = [ 898 | "bytes", 899 | "futures-core", 900 | "futures-sink", 901 | "pin-project-lite", 902 | "tokio", 903 | "tracing", 904 | ] 905 | 906 | [[package]] 907 | name = "tower-service" 908 | version = "0.3.2" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 911 | 912 | [[package]] 913 | name = "tracing" 914 | version = "0.1.37" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 917 | dependencies = [ 918 | "cfg-if", 919 | "pin-project-lite", 920 | "tracing-core", 921 | ] 922 | 923 | [[package]] 924 | name = "tracing-core" 925 | version = "0.1.30" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 928 | dependencies = [ 929 | "once_cell", 930 | ] 931 | 932 | [[package]] 933 | name = "try-lock" 934 | version = "0.2.4" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" 937 | 938 | [[package]] 939 | name = "unicode-bidi" 940 | version = "0.3.13" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" 943 | 944 | [[package]] 945 | name = "unicode-ident" 946 | version = "1.0.8" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 949 | 950 | [[package]] 951 | name = "unicode-normalization" 952 | version = "0.1.22" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 955 | dependencies = [ 956 | "tinyvec", 957 | ] 958 | 959 | [[package]] 960 | name = "unicode-segmentation" 961 | version = "1.10.1" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 964 | 965 | [[package]] 966 | name = "unicode-width" 967 | version = "0.1.10" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 970 | 971 | [[package]] 972 | name = "untrusted" 973 | version = "0.7.1" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 976 | 977 | [[package]] 978 | name = "url" 979 | version = "2.3.1" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 982 | dependencies = [ 983 | "form_urlencoded", 984 | "idna", 985 | "percent-encoding", 986 | ] 987 | 988 | [[package]] 989 | name = "vec_map" 990 | version = "0.8.2" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 993 | 994 | [[package]] 995 | name = "version_check" 996 | version = "0.9.4" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 999 | 1000 | [[package]] 1001 | name = "want" 1002 | version = "0.3.0" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1005 | dependencies = [ 1006 | "log", 1007 | "try-lock", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "wasi" 1012 | version = "0.11.0+wasi-snapshot-preview1" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1015 | 1016 | [[package]] 1017 | name = "wasm-bindgen" 1018 | version = "0.2.84" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 1021 | dependencies = [ 1022 | "cfg-if", 1023 | "wasm-bindgen-macro", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "wasm-bindgen-backend" 1028 | version = "0.2.84" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 1031 | dependencies = [ 1032 | "bumpalo", 1033 | "log", 1034 | "once_cell", 1035 | "proc-macro2", 1036 | "quote", 1037 | "syn 1.0.109", 1038 | "wasm-bindgen-shared", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "wasm-bindgen-futures" 1043 | version = "0.4.34" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" 1046 | dependencies = [ 1047 | "cfg-if", 1048 | "js-sys", 1049 | "wasm-bindgen", 1050 | "web-sys", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "wasm-bindgen-macro" 1055 | version = "0.2.84" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 1058 | dependencies = [ 1059 | "quote", 1060 | "wasm-bindgen-macro-support", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "wasm-bindgen-macro-support" 1065 | version = "0.2.84" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 1068 | dependencies = [ 1069 | "proc-macro2", 1070 | "quote", 1071 | "syn 1.0.109", 1072 | "wasm-bindgen-backend", 1073 | "wasm-bindgen-shared", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "wasm-bindgen-shared" 1078 | version = "0.2.84" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 1081 | 1082 | [[package]] 1083 | name = "web-sys" 1084 | version = "0.3.61" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 1087 | dependencies = [ 1088 | "js-sys", 1089 | "wasm-bindgen", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "webpki" 1094 | version = "0.22.2" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" 1097 | dependencies = [ 1098 | "ring", 1099 | "untrusted", 1100 | ] 1101 | 1102 | [[package]] 1103 | name = "webpki-roots" 1104 | version = "0.22.6" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" 1107 | dependencies = [ 1108 | "webpki", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "winapi" 1113 | version = "0.3.9" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1116 | dependencies = [ 1117 | "winapi-i686-pc-windows-gnu", 1118 | "winapi-x86_64-pc-windows-gnu", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "winapi-i686-pc-windows-gnu" 1123 | version = "0.4.0" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1126 | 1127 | [[package]] 1128 | name = "winapi-x86_64-pc-windows-gnu" 1129 | version = "0.4.0" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1132 | 1133 | [[package]] 1134 | name = "windows-sys" 1135 | version = "0.45.0" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1138 | dependencies = [ 1139 | "windows-targets 0.42.2", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "windows-sys" 1144 | version = "0.48.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1147 | dependencies = [ 1148 | "windows-targets 0.48.0", 1149 | ] 1150 | 1151 | [[package]] 1152 | name = "windows-targets" 1153 | version = "0.42.2" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1156 | dependencies = [ 1157 | "windows_aarch64_gnullvm 0.42.2", 1158 | "windows_aarch64_msvc 0.42.2", 1159 | "windows_i686_gnu 0.42.2", 1160 | "windows_i686_msvc 0.42.2", 1161 | "windows_x86_64_gnu 0.42.2", 1162 | "windows_x86_64_gnullvm 0.42.2", 1163 | "windows_x86_64_msvc 0.42.2", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "windows-targets" 1168 | version = "0.48.0" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 1171 | dependencies = [ 1172 | "windows_aarch64_gnullvm 0.48.0", 1173 | "windows_aarch64_msvc 0.48.0", 1174 | "windows_i686_gnu 0.48.0", 1175 | "windows_i686_msvc 0.48.0", 1176 | "windows_x86_64_gnu 0.48.0", 1177 | "windows_x86_64_gnullvm 0.48.0", 1178 | "windows_x86_64_msvc 0.48.0", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "windows_aarch64_gnullvm" 1183 | version = "0.42.2" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1186 | 1187 | [[package]] 1188 | name = "windows_aarch64_gnullvm" 1189 | version = "0.48.0" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 1192 | 1193 | [[package]] 1194 | name = "windows_aarch64_msvc" 1195 | version = "0.42.2" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1198 | 1199 | [[package]] 1200 | name = "windows_aarch64_msvc" 1201 | version = "0.48.0" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1204 | 1205 | [[package]] 1206 | name = "windows_i686_gnu" 1207 | version = "0.42.2" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1210 | 1211 | [[package]] 1212 | name = "windows_i686_gnu" 1213 | version = "0.48.0" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1216 | 1217 | [[package]] 1218 | name = "windows_i686_msvc" 1219 | version = "0.42.2" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1222 | 1223 | [[package]] 1224 | name = "windows_i686_msvc" 1225 | version = "0.48.0" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1228 | 1229 | [[package]] 1230 | name = "windows_x86_64_gnu" 1231 | version = "0.42.2" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1234 | 1235 | [[package]] 1236 | name = "windows_x86_64_gnu" 1237 | version = "0.48.0" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1240 | 1241 | [[package]] 1242 | name = "windows_x86_64_gnullvm" 1243 | version = "0.42.2" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1246 | 1247 | [[package]] 1248 | name = "windows_x86_64_gnullvm" 1249 | version = "0.48.0" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1252 | 1253 | [[package]] 1254 | name = "windows_x86_64_msvc" 1255 | version = "0.42.2" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1258 | 1259 | [[package]] 1260 | name = "windows_x86_64_msvc" 1261 | version = "0.48.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1264 | 1265 | [[package]] 1266 | name = "winreg" 1267 | version = "0.10.1" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1270 | dependencies = [ 1271 | "winapi", 1272 | ] 1273 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embark-oss" 3 | version = "0.1.0" 4 | authors = ["Embark Open Source "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | # Async HTTP client 11 | reqwest = { version = "0.11", default-features = false, features = [ 12 | "json", 13 | "rustls-tls", 14 | ] } 15 | # Futures runtime 16 | tokio = { version = "1.0", features = ["full"] } 17 | # Data de/serialisation 18 | serde = { version = "1.0", features = ["derive"] } 19 | # serde_json is just for the example, not required in general 20 | serde_json = "1.0" 21 | # Command line argument parsing 22 | structopt = "0.3" 23 | # Error handling with contextual information 24 | eyre = "0.6" 25 | # Combinators for futures 26 | futures = "0.3" 27 | # Combinators for iterators 28 | itertools = "0.10" 29 | # Lazily created static values 30 | lazy_static = "1.4" 31 | # Regular expressions for text parsing 32 | regex = "1.4" 33 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Embark Studios 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | # `🦕 Embark Open Source` 5 | 6 | **Everything about open source at Embark, as an open source repo. How meta!** 7 | 8 | [![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](http://embark.games) 9 | [![Embark](https://img.shields.io/badge/discord-ark-%237289da.svg?logo=discord)](https://discord.gg/dAuKfZS) 10 | 11 |
12 | 13 | ## Contents 14 | 15 | - [Our Open Source Beliefs and Strategy](content/beliefs-strategy.md) 16 | - [How to Open Source a project at Embark](content/how-to.md) 17 | - [How to maintain an Embark Open Source project](content/maintenance-guidelines.md) 18 | - [embark.dev](https://embark.dev) - Our Open Source portal 19 | - [embark.rs](https://embark.rs) - Our Rust Open Source ecosystem tracking repository 20 | 21 | ## Tooling 22 | 23 | This repo also contains a program for validating Embark's open source projects. See [its documentation](content/tooling.md) for more information. 24 | 25 | ## Contributing 26 | 27 | [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4-ff69b4.svg)](CODE_OF_CONDUCT.md) 28 | 29 | We welcome community contributions to this project. 30 | 31 | Please read our [Contributor Guide](CONTRIBUTING.md) for more information on how to get started. 32 | 33 | ## License 34 | 35 | Licensed under either of 36 | 37 | - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 38 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 39 | 40 | at your option. 41 | 42 | ### Contribution 43 | 44 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 45 | -------------------------------------------------------------------------------- /content/beliefs-strategy.md: -------------------------------------------------------------------------------- 1 | # Embark Studios Open Source Strategy 2 | 3 | At [Embark Studios](https://embark-studios.com), we're on a mission to blur the line between playing and making. 4 | 5 | Technology is reshaping our industry, and open source is a big part of that. We want to be part of this change, by exploring and applying the latest technology, by being honest and transparent in our relationship with each other and our community, and by allowing our curiosity to lead us down unexpected paths. 6 | 7 | Want to know more about how we're applying our strategy day-to-day? Check out [How to Open Source at Embark](how-to.md). 8 | 9 | ## Beliefs 10 | 11 | Why we think open source brings value to Embark. 12 | 13 | 0. **It's the right thing to do.** 14 | 15 | Our computers, our software, our games, and our world run on open source. Without a thriving open source ecosystem, we would not be where we are today. To make sure we (and others) keep innovating, we contribute back to this ecosystem and help enabling an open and collaborative games industry. 16 | 17 | 1. **Supporting open tools is more efficient than building our own** 18 | 19 | The era of massive closed-source game engines and bespoke, proprietary tooling is coming to an end. Today, the open source community is thriving and building amazing and innovative tools in many areas. By investing in or contributing to these open tools, we can build and innovate faster at a lower cost while enabling others at the same time. 20 | 21 | 2. **Sharing our work gives us new perspectives** 22 | 23 | Instead of working in isolation, we can share what we're building with the world. This gives us unique insights from people outside of the company and even outside of the industry, which help us find new ideas and solutions. 24 | 25 | 3. **Being transparent helps us find collaborators** 26 | 27 | By openly sharing what we're interested in and working on, we connect and start to have conversations with others who are interested in collaborating with us. We've already found multiple new unlikely partnerships outside our immediate network and industry. 28 | 29 | ## Strategy 30 | 31 | How we engage with open source as a company. 32 | 33 | 🗃 **Open-source by default.** 34 | 35 | For everything new we develop, we consider if and when it can be open sourced. 36 | 37 | We don't open source everything, but we do open source our projects where it makes sense. If we develop something that is reusable, not tied to proprietary systems or game products, and will be maintained for the foreseeable future, we make it public. 38 | 39 | (See our [how-to guide](how-to.md) for information about assessing whether to open-source a project) 40 | 41 | 🎤 **Lead the conversation.** 42 | 43 | We work in a lot of spaces where there are big problems with no clear answers. Embark should drive open conversations and collaborations around solving these problems, acting as a "thought leader" in the space. We do this by building open projects, joint communities, and actively inviting collaboration. 44 | 45 | 💶 **Support strategically.** 46 | 47 | We give back to the community and support developers who are making great things, and we strategically invest in projects and developers that share and work towards our goals, directly or indirectly. This can involve monetary sponsorship of individuals or projects, contract work for specific tasks, or allocated resources. 48 | 49 | We strongly believe companies that professionally use and rely on open source software have a responsibility to support the developers and maintainers of it. 50 | -------------------------------------------------------------------------------- /content/how-to.md: -------------------------------------------------------------------------------- 1 | # How to Open Source at Embark 2 | 3 | > This document covers the basics of working with open source projects at Embark 4 | 5 | ## New Projects 6 | 7 | ### Should I open-source it? 8 | 9 | You've got a new project - great! At Embark, we believe most things should be open-sourced by default, but there are a few questions you should ask yourself before publishing. 10 | 11 | - **Is it useful to someone else?** 12 | - The answer is usually yes (even for small or "obvious" things) but consider whether your project could be useful to someone outside the organisation. 13 | 14 | - **Does it rely on internal or proprietary systems?** 15 | - We don't want to release anything that could harm Embark from a security or intellectual property perspective. We also don't want to release something that doesn't work for non-Embarkers because it relies on an internal system. **You need to consult with your manager to assess any risks associated with releasing your project.** 16 | 17 | - **Can you commit to maintaining the project for the foreseeable future?** 18 | - See the [maintenance guidelines](maintenance-guidelines.md) to see what is required from an Embark open source maintainer. 19 | - If no, you can still release ["as-is"](#repository-types) in an archived GitHub repository. 20 | 21 | - **Is it high enough quality?** 22 | - Not every project will be mindblowing and innovative (and that's perfectly ok!), but our open source work is still a representation of the company. All repositories should have a minimum level of documentation and the code should be clean and readable. The community team and your manager should review the repository before release and suggest improvements. 23 | - Keep an eye out for stray comments and commit messages - the public will see them! 24 | 25 | ### Step-by-step 26 | 27 | 1. Create a new private or internal repository on GitHub using the [open source template](https://github.com/EmbarkStudios/opensource-template). If you already have a repository and want to maintain the commit history, you can instead add all the files from the template repository to your project. 28 | 1. Edit the `.github/CODEOWNERS` file to contain the GitHub usernames of the project maintainers. 29 | 1. If the project is a Rust project edit `.github/workflows/rust-ci.yml` to your needs, resolving all the comments marked `TODO`. 30 | 1. If the project is not a Rust project remove the `.github/workflows/rust-ci.yml` file. 31 | 1. Customise the README for your project by adding the appropriate name, description, links, and badges. This is also a great time to pick an emoji for the project! 32 | 1. Add the [EmbarkStudios / Open Source Admins](https://github.com/orgs/EmbarkStudios/teams/open-source-admins) group as admins in the repo access settings. Ask for assistance on slack if you do not have access to the repo settings. 33 | 1. Post the private repo link with a description of what it is to the Embark #opensource Slack channel and ask for green-light to go ahead. Do @mention your manager also for visibility. 34 | 1. Make the repository public. 35 | 1. Add the project to the [embark.dev list of open source projects](https://github.com/EmbarkStudios/opensource-website/blob/main/static/data/data.json). 36 | 1. If the project is a Rust project add it to the [embark.rs list of open source Rust projects](https://github.com/EmbarkStudios/rust-ecosystem#open-source). 37 | 1. Announce the release on [our Discord](https://discord.gg/dAuKfZS) opensource channel and any other forums such as [/r/rust](https://reddit.com/r/rust). The ecosystem team can help you with this step. They will have the "community team" role on the Discord. 38 | 39 | ## Publishing new versions 40 | 41 | If the project is a Rust project and the steps above have been completed then new versions can be released by following these steps. 42 | 43 | 1. Update the `## Unreleased` section to include all of the relevant/important changes since the last release 44 | 1. Install [`cargo-release`](https://github.com/crate-ci/cargo-release) if you have not already done so. 45 | 1. Run `cargo release --execute ` which will do the following 46 | 1. Bump the crate version to the next `` or `` version. 47 | 1. Replace any `pre-release-replacements` with the new version. 48 | 1. Commit 49 | 1. Publishes to crates.io (you can provide your API token with `--token` if you have a non-Embark account as your default) 50 | 1. Tags the commit with the version message specified in release.toml 51 | 1. Pushes the commits and tags 52 | 53 | See the [cargo-release docs](https://github.com/crate-ci/cargo-release/tree/master/docs) if you need to do something more advanced or just different. 54 | 55 | ## Repository Types 56 | 57 | **maintained**: this repository has a maintainer who is actively developing it and reviewing contributions 58 | 59 | **as-is**: this repository could still be useful, but doesn't have an active maintainer. It is archived on GitHub. 60 | 61 | ## Communication 62 | 63 | ### Email 64 | 65 | We receive incoming email at opensource@embark-studios.com. The community team is responsible for answering or forwarding to the relevant Embarkers. 66 | 67 | ### Discord 68 | 69 | We have a [public Discord](https://discord.gg/8TW9nfF) for the developer community. There are channels here specifically for open source projects we maintain, which can be helpful for coordinating work, getting feedback, or talking to contributors. Not every project needs its own channel, but if you are a maintainer and you want one to be created for your project, message a Discord admin! 70 | 71 | ### Newsletter 72 | 73 | We publish a monthly developer newsletter which covers updates on our open source work. You can [view the archive here](https://www.embark.dev/newsletter/). As a maintainer, you can let #opensource team on Slack or Discord know about updates you'd like to be included. 74 | -------------------------------------------------------------------------------- /content/maintenance-guidelines.md: -------------------------------------------------------------------------------- 1 | # Open source maintenance guidelines 2 | 3 | This document details Embark's guidelines for open source project 4 | maintenance. All our open source projects have explicit maintainers who are 5 | responsible for managing the project and adhereing to these guidelines. 6 | 7 | ## Share responsibility 8 | 9 | Maintaining an open source project can at times be tiring or time-consuming. 10 | If possible, find another Embark employee to be a co-maintainer of the 11 | project, in order to share the workload and support each other. 12 | 13 | ## Write detailed issues 14 | 15 | Ensure all issues have sufficient context so that people reading them can 16 | understand what they mean, even if they are less familiar with the project. 17 | If the issue is intended to be worked on by community members, add additional 18 | information to help them get started with the implementation. This is 19 | especially important for issues marked “good first contribution”. 20 | 21 | ## Label issues 22 | 23 | If there is an issue for which you would welcome a contribution from someone 24 | outside of Embark, label the issue as “help wanted”. 25 | 26 | If you wish to encourage new contributions from outside of Embark, and you 27 | have the time to review and guide contributions from people less familiar 28 | with the code base, label less challenging issues as “good first 29 | contribution”. 30 | 31 | ## Assign issues 32 | 33 | If someone is working on an issue, assign it to them so that other people 34 | know not to attempt to work on it. 35 | 36 | ## Respond in a timely manner 37 | 38 | Respond to issues and pull requests from people outside of Embark as quickly 39 | as possible, aiming to leave the submitter waiting no more than two working 40 | days. 41 | 42 | If you are unable to resolve the issue or review the pull request within that 43 | period of time, leave a reply letting the submitter know that you have 44 | received the issue or pull request, and that you will get to it at a later 45 | date. Not knowing the status of your work or problem can be frustrating! 46 | 47 | ## Be courteous 48 | 49 | When interacting with people as the project maintainer be sure to be clearly 50 | polite and respectful, and thank people for their contributions. This helps 51 | build goodwill towards the project, encouraging future contributions and 52 | improving our reputation. 53 | 54 | ## Assume positive intent 55 | 56 | Contributing to open source can be stressful or intimidating, English may not 57 | be their native language, or they may have had a particularly bad day. Any of 58 | these reasons or more can result in people coming across as abrasive or rude. 59 | Where possible, assume that any rudeness is an unintentional accident and 60 | attempt to maintain a polite and friendly atmosphere. 61 | 62 | ## Offer clear guidance 63 | 64 | Offer advice and context on how to implement a feature, and give clear and 65 | actionable feedback when reviewing pull requests. 66 | 67 | With simpler contributions, it may be more time-consuming for you to offer 68 | this guidance than it would be for you to just implement the feature 69 | yourself. Nevertheless, it is still worthwhile doing this as it helps the 70 | contributor to learn, building goodwill towards Embark and the project, and 71 | making it more likely they will make more contributions in future. 72 | 73 | ## Say no when required 74 | 75 | There will be times when community members suggest or contribute to changes 76 | that are not suitable for inclusion in the project, either due to a low 77 | quality bar or because the changes are out of scope for the project. 78 | 79 | If it is possible for the contributor to improve the quality of their 80 | contribution so that it can be merged, explain the issues to them and offer 81 | guidance on how to improve it so they can submit further changes later. 82 | 83 | If the issue or contribution is out of scope for the project, politely 84 | explain this to the contributor along with any reasoning, and then close the 85 | issue, thanking them for their contribution. 86 | 87 | ## Close stale pull requests 88 | 89 | If a pull request is in an unfinished and un-mergeable state, and the 90 | contributor has not made any updates or indicated that they are currently 91 | working on it for over 2 weeks, close the pull request. Closing stale pull 92 | requests makes it clear the project is maintained, and makes it easier to 93 | tell the current state of work on the project. 94 | 95 | When closing leave a message thanking them for their contribution and 96 | inviting them to reopen the pull request when they have further changes. 97 | 98 | ## Perform hand-overs 99 | 100 | If you are unable to maintain the project for a period of time, e.g. if you 101 | are going on holiday, find another person at Embark to temporarily take over 102 | your responsibilities. Add to this person as a [code owner][codeowner] to the 103 | repository to ensure they get appropriate notifications to new issues and 104 | pull requests. 105 | 106 | [codeowner]: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners 107 | 108 | If you are leaving Embark or no longer have the desire or capacity to 109 | maintain the project, find another person to adopt the role of maintainer. 110 | 111 | If you are unable to find a suitable person, contact the open-source 112 | team for help. 113 | 114 | ## Get help from the open-source team 115 | 116 | If you find yourself in a situation where you’re not sure what to do, reach 117 | out to the open-source team for support. They can be reached via the 118 | `#opensource` channel in our internal Slack. 119 | 120 | ## Adhere to the code of conduct 121 | 122 | All open source Embark projects have a [code of conduct][coc] which details 123 | the expected standards of behaviour to which all community members must 124 | adhere. Read the code of conduct and ensure that both yourself and your 125 | contributors follow it. If you have a problem with a community member 126 | violating the code of conduct reach out to the ecosystem team for help. 127 | 128 | [coc]: https://github.com/EmbarkStudios/opensource/blob/main/CODE_OF_CONDUCT.md 129 | -------------------------------------------------------------------------------- /content/tooling.md: -------------------------------------------------------------------------------- 1 | # Embark OSS tooling 2 | 3 | This program is used to validate that our open source projects adhere to our open source guidelines. 4 | 5 | ## Periodic jobs 6 | 7 | ### Project validation 8 | 9 | Every weekday the [`validate-all`](#cargo-run-validate-all) command is run on GitHub actions, sending us a Slack notification if any problems are found. 10 | 11 | ## Commands 12 | 13 | ### `cargo run validate-all` 14 | 15 | This command checks to see if every project listed in the Embark 16 | [opensource-website data.json][data.json] and every public repository in the 17 | EmbarkStudios GitHub organisation conforms to our open source guidelines to 18 | the extent that this tool can detect. 19 | 20 | [data.json]: https://github.com/EmbarkStudios/opensource-website/blob/main/static/data/data.json 21 | 22 | #### Flags 23 | 24 | - `--slack-webhook-url`: An optional Slack webhook URL that is used to report problems. 25 | - `--github-api-token`: An optional API token used to raise the rate limit of the GitHub API. Likely only needed on CI where we share an IP with other GitHub API users. 26 | 27 | ### `cargo run validate PROJECT_REPO_NAME` 28 | 29 | This command checks to see if a given Embark open source project conforms to our open source guidelines to the extent that this tool can detect. 30 | 31 | ## Testing 32 | 33 | This tool has unit tests. Run them like so: 34 | 35 | ```shell 36 | cargo test 37 | ``` 38 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use eyre::Chain; 2 | use std::{error::Error, fmt::Write}; 3 | 4 | /// Write the error and the cause 5 | pub fn cause_string(error: &(dyn Error + 'static), should_indent: bool) -> String { 6 | let mut f = String::new(); 7 | 8 | macro_rules! indent { 9 | () => { 10 | if should_indent { 11 | write!(f, " ").unwrap(); 12 | } 13 | }; 14 | } 15 | 16 | indent!(); 17 | writeln!(f, "{}", error).unwrap(); 18 | 19 | if let Some(cause) = error.source() { 20 | indent!(); 21 | writeln!(f, "Caused by:").unwrap(); 22 | for (i, error) in Chain::new(cause).enumerate() { 23 | indent!(); 24 | writeln!(f, " {}: {}", i, error).unwrap(); 25 | } 26 | } 27 | f 28 | } 29 | -------------------------------------------------------------------------------- /src/github.rs: -------------------------------------------------------------------------------- 1 | mod codeowners; 2 | 3 | use std::collections::{HashMap, HashSet}; 4 | 5 | pub use codeowners::CodeOwners; 6 | 7 | use eyre::{eyre, WrapErr}; 8 | use lazy_static::lazy_static; 9 | use regex::Regex; 10 | use serde::de::DeserializeOwned; 11 | 12 | /// A GitHub API client that optionally authenticates requests. 13 | pub struct Client { 14 | github_api_token: Option, 15 | } 16 | 17 | impl Client { 18 | pub fn new(github_api_token: Option) -> Self { 19 | Self { github_api_token } 20 | } 21 | 22 | // https://docs.github.com/en/free-pro-team@latest/rest/reference/orgs#members 23 | pub async fn public_organisation_members( 24 | &self, 25 | organisation: &str, 26 | ) -> eyre::Result> { 27 | #[derive(Debug, serde::Deserialize)] 28 | pub struct Member { 29 | login: String, 30 | } 31 | 32 | let url = format!( 33 | "https://api.github.com/orgs/{}/members?per_page=100", 34 | organisation 35 | ); 36 | Ok(self 37 | .api_list(url) 38 | .await 39 | .wrap_err("Unable to get public members for organisation")? 40 | .into_iter() 41 | .map(|member: Member| member.login) 42 | .collect()) 43 | } 44 | 45 | // https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-organization-repositories 46 | pub async fn organisation_repos( 47 | &self, 48 | organisation: &str, 49 | ) -> eyre::Result> { 50 | let url = format!( 51 | "https://api.github.com/orgs/{}/repos?type=archived&per_page=100", 52 | organisation 53 | ); 54 | Ok(self 55 | .api_list(url) 56 | .await 57 | .wrap_err("Unable to get archived repos for organisation")? 58 | .into_iter() 59 | .map(|repo: Repo| (repo.name.clone(), repo)) 60 | .collect()) 61 | } 62 | 63 | /// Perform a GET request to a paginated GitHub URL that returns a JSON array per 64 | /// page. All pages will be traversed and retuned as a single collection. 65 | async fn api_list(&self, url: String) -> eyre::Result> { 66 | let mut collection = Vec::new(); 67 | let mut next_url = Some(url); 68 | while let Some(url) = next_url { 69 | let response = self.api_get_response(&url).await?; 70 | next_url = next_pagination_page(&response)?; 71 | let items: Vec = response 72 | .json() 73 | .await 74 | .wrap_err("Unable to parse JSON response")?; 75 | collection.extend(items); 76 | } 77 | 78 | Ok(collection) 79 | } 80 | 81 | async fn api_get_response(&self, url: &str) -> eyre::Result { 82 | let request = reqwest::Client::new() 83 | .get(url) 84 | .header("accept", "application/vnd.github.v3+json") 85 | .header("user-agent", "embark-oss"); 86 | let request = match &self.github_api_token { 87 | Some(token) => request.header("authorization", format!("token {}", token)), 88 | _ => request, 89 | }; 90 | let response = request 91 | .send() 92 | .await 93 | .wrap_err(format!("Failed to get {}", url))? 94 | .error_for_status()?; 95 | Ok(response) 96 | } 97 | } 98 | 99 | #[derive(Debug, PartialEq, Clone, serde::Deserialize)] 100 | pub struct Repo { 101 | pub name: String, 102 | pub archived: bool, 103 | pub private: bool, 104 | pub fork: bool, 105 | } 106 | 107 | impl Repo { 108 | pub fn is_public_active_source_project(&self) -> bool { 109 | match self.name.as_str() { 110 | "opensource-template" | ".github" => false, 111 | _ => !(self.archived || self.private || self.fork), 112 | } 113 | } 114 | } 115 | 116 | pub async fn download_repo_file( 117 | org: &str, 118 | repo: &str, 119 | branch: &str, 120 | file: &str, 121 | ) -> eyre::Result { 122 | let (name, response) = download_file(org, repo, branch, file).await?; 123 | response 124 | .text() 125 | .await 126 | .wrap_err(eyre!("Failed to decode {}", name)) 127 | } 128 | 129 | pub async fn download_repo_json_file( 130 | org: &str, 131 | repo: &str, 132 | branch: &str, 133 | file: &str, 134 | ) -> eyre::Result { 135 | let (name, response) = download_file(org, repo, branch, file).await?; 136 | response 137 | .json() 138 | .await 139 | .wrap_err(eyre!("Failed to decode {}", name)) 140 | } 141 | 142 | pub async fn download_file( 143 | org: &str, 144 | repo: &str, 145 | branch: &str, 146 | file: &str, 147 | ) -> eyre::Result<(String, reqwest::Response)> { 148 | let path = format!("{}/{}/{}/{}", org, repo, branch, file); 149 | let name = format!("{}/{}:{}", org, repo, file); 150 | let url = format!("https://raw.githubusercontent.com/{}", path); 151 | let response = reqwest::get(&url) 152 | .await 153 | .wrap_err(format!("Failed to download {}", name))?; 154 | 155 | // Ensure the file was successfully downloaded 156 | if response.status() == 404 { 157 | return Err(eyre!("Expected status code 200, got 404")) 158 | .wrap_err("File not found in repo") 159 | .wrap_err(format!("Unable to download {}", name))?; 160 | } 161 | if response.status() != 200 { 162 | return Err(eyre!("Expected status code 200, got {}", response.status())) 163 | .wrap_err(format!("Unable to download {}", name))?; 164 | } 165 | 166 | Ok((name, response)) 167 | } 168 | 169 | fn next_pagination_page(response: &reqwest::Response) -> eyre::Result> { 170 | match response.headers().get("link") { 171 | None => Ok(None), 172 | Some(link) => Ok(next_pagination_page_from_link_header(link) 173 | .wrap_err("Unable to find next pagination page url in link header")?), 174 | } 175 | } 176 | 177 | fn next_pagination_page_from_link_header( 178 | header: &reqwest::header::HeaderValue, 179 | ) -> eyre::Result> { 180 | Ok(header 181 | .to_str() 182 | .wrap_err("Header was not valid unicode")? 183 | .split(',') 184 | .find_map(parse_next_link_url)) 185 | } 186 | 187 | fn parse_next_link_url(content: &str) -> Option { 188 | lazy_static! { 189 | static ref RE: Regex = Regex::new(r#"<(?P.+)>; *rel="next""#).unwrap(); 190 | } 191 | RE.captures(content)? 192 | .name("url") 193 | .map(|s| s.as_str().to_string()) 194 | } 195 | 196 | #[cfg(test)] 197 | mod tests { 198 | use super::*; 199 | 200 | #[test] 201 | fn test_parse_next_link_url() { 202 | assert_eq!(parse_next_link_url(""), None); 203 | 204 | // A "rel" value other than "next" is not accepted 205 | assert_eq!( 206 | parse_next_link_url(r#"; rel="last""#), 207 | None 208 | ); 209 | 210 | assert_eq!( 211 | parse_next_link_url(r#"; rel="next""#), 212 | Some("https://example.com/members?page=2".to_string()) 213 | ); 214 | 215 | assert_eq!( 216 | parse_next_link_url(r#" ; rel="next" "#), 217 | Some("https://example.com/members?page=2".to_string()) 218 | ); 219 | } 220 | 221 | fn make_repo() -> Repo { 222 | Repo { 223 | name: "name".to_string(), 224 | archived: false, 225 | private: false, 226 | fork: false, 227 | } 228 | } 229 | 230 | #[test] 231 | fn repo_is_public_active_source_project() { 232 | let repo = make_repo(); 233 | assert!(repo.is_public_active_source_project()); 234 | 235 | // Archived are inactive 236 | let mut repo = make_repo(); 237 | repo.archived = true; 238 | assert!(!repo.is_public_active_source_project()); 239 | 240 | // Forks are inactive 241 | let mut repo = make_repo(); 242 | repo.fork = true; 243 | assert!(!repo.is_public_active_source_project()); 244 | 245 | // Private are inactive 246 | let mut repo = make_repo(); 247 | repo.private = true; 248 | assert!(!repo.is_public_active_source_project()); 249 | 250 | // .github is skipped as it's meta information 251 | let mut repo = make_repo(); 252 | repo.name = ".github".to_string(); 253 | assert!(!repo.is_public_active_source_project()); 254 | 255 | // opensource-template is skipped as it cannot conform without making 256 | // the templates errors prone. i.e. forgetting to configure the project 257 | // means it is incorrectly detected as complying. 258 | let mut repo = make_repo(); 259 | repo.name = "opensource-template".to_string(); 260 | assert!(!repo.is_public_active_source_project()); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/github/codeowners.rs: -------------------------------------------------------------------------------- 1 | use eyre::{eyre, WrapErr}; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Debug, PartialEq, Eq)] 5 | pub struct CodeOwners { 6 | /// CODEOWNERS files are ordered so we use a Vec of pairs rather than a 7 | /// a standard Rust dictionary type. 8 | /// 9 | /// Assignments later in the vector have higher precedence than those 10 | /// earlier. 11 | assignments: Vec, 12 | } 13 | 14 | #[derive(Debug, PartialEq, Eq)] 15 | pub struct Assignment { 16 | /// A git compatible glob that specifies which files this assignment applies to. 17 | file_pattern: String, 18 | /// A collection of GitHub usernames or emails for the users that own this 19 | /// code section. 20 | owners: HashSet, 21 | } 22 | 23 | impl Assignment { 24 | pub fn from_line(line: &str) -> eyre::Result { 25 | let mut iter = line.split_whitespace().map(String::from); 26 | let file_pattern = iter 27 | .next() 28 | .ok_or_else(|| eyre!("No file pattern for code owners line"))?; 29 | let owners = iter 30 | .map(validate_name_format) 31 | .collect::>>() 32 | .wrap_err_with(|| format!("Unable to parse code owners for {}", file_pattern))?; 33 | if owners.is_empty() { 34 | return Err(eyre!("File pattern `{}` has no owners", file_pattern)); 35 | } 36 | Ok(Self { 37 | file_pattern, 38 | owners, 39 | }) 40 | } 41 | } 42 | 43 | fn validate_name_format(name: String) -> eyre::Result { 44 | if name.starts_with('@') { 45 | Ok(name.trim_start_matches('@').to_string()) 46 | } else { 47 | Err(eyre!("Code owner `{}` does not start with an @", name)) 48 | } 49 | } 50 | 51 | impl CodeOwners { 52 | pub fn new(source: &str) -> eyre::Result { 53 | let assignments = source 54 | .lines() 55 | .map(|line| line.trim()) 56 | .filter(|line| !line.starts_with('#')) 57 | .filter(|line| !line.is_empty()) 58 | .map(Assignment::from_line) 59 | .collect::>()?; 60 | Ok(Self { assignments }) 61 | } 62 | 63 | pub fn primary_maintainers(&self) -> Option<&HashSet> { 64 | self.assignments 65 | .iter() 66 | .find(|assignment| assignment.file_pattern == "*") 67 | .map(|assignment| &assignment.owners) 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | 75 | #[test] 76 | fn parsing() { 77 | assert_eq!( 78 | CodeOwners::new("").unwrap(), 79 | CodeOwners { 80 | assignments: vec![] 81 | } 82 | ); 83 | 84 | assert_eq!( 85 | CodeOwners::new("# * @lpil").unwrap(), 86 | CodeOwners { 87 | assignments: vec![] 88 | } 89 | ); 90 | 91 | assert_eq!( 92 | CodeOwners::new("* @lpil").unwrap(), 93 | CodeOwners { 94 | assignments: vec![Assignment { 95 | file_pattern: "*".to_string(), 96 | owners: hashset(&["lpil"]) 97 | }] 98 | } 99 | ); 100 | 101 | assert_eq!( 102 | CodeOwners::new("* @lpil @arirawr").unwrap(), 103 | CodeOwners { 104 | assignments: vec![Assignment { 105 | file_pattern: "*".to_string(), 106 | owners: hashset(&["lpil", "arirawr"]) 107 | }] 108 | } 109 | ); 110 | 111 | assert_eq!( 112 | CodeOwners::new("* @lpil arirawr").unwrap_err().to_string(), 113 | "Unable to parse code owners for *", 114 | ); 115 | 116 | assert_eq!( 117 | CodeOwners::new( 118 | "* @lpil @arirawr 119 | # comment 120 | # comment 121 | left @XAMPPRocky 122 | 123 | right/ok @celialewis3 @soniasingla \n" 124 | ) 125 | .unwrap(), 126 | CodeOwners { 127 | assignments: vec![ 128 | Assignment { 129 | file_pattern: "*".to_string(), 130 | owners: ["lpil", "arirawr"] 131 | .iter() 132 | .cloned() 133 | .map(String::from) 134 | .collect() 135 | }, 136 | Assignment { 137 | file_pattern: "left".to_string(), 138 | owners: hashset(&["XAMPPRocky"]) 139 | }, 140 | Assignment { 141 | file_pattern: "right/ok".to_string(), 142 | owners: hashset(&["soniasingla", "celialewis3"]) 143 | } 144 | ] 145 | } 146 | ); 147 | } 148 | 149 | fn hashset(members: &[&str]) -> HashSet { 150 | members.iter().cloned().map(String::from).collect() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![warn( 3 | clippy::all, 4 | clippy::doc_markdown, 5 | clippy::dbg_macro, 6 | clippy::todo, 7 | clippy::empty_enum, 8 | clippy::enum_glob_use, 9 | clippy::mem_forget, 10 | clippy::use_self, 11 | clippy::filter_map_next, 12 | clippy::needless_continue, 13 | clippy::needless_borrow, 14 | clippy::match_wildcard_for_single_variants, 15 | clippy::if_let_mutex, 16 | clippy::mismatched_target_os, 17 | clippy::await_holding_lock, 18 | clippy::match_on_vec_items, 19 | clippy::imprecise_flops, 20 | clippy::suboptimal_flops, 21 | clippy::lossy_float_literal, 22 | clippy::rest_pat_in_fully_bound_structs, 23 | clippy::fn_params_excessive_bools, 24 | clippy::inefficient_to_string, 25 | clippy::linkedlist, 26 | clippy::macro_use_imports, 27 | clippy::option_option, 28 | clippy::verbose_file_reads, 29 | clippy::unnested_or_patterns, 30 | rust_2018_idioms, 31 | future_incompatible, 32 | missing_debug_implementations, 33 | missing_copy_implementations, 34 | trivial_casts, 35 | trivial_numeric_casts, 36 | unstable_features, 37 | nonstandard_style, 38 | unused_import_braces, 39 | unused_qualifications, 40 | unused_results 41 | )] 42 | 43 | mod error; 44 | mod github; 45 | mod policy; 46 | mod slack; 47 | mod validate; 48 | 49 | use structopt::clap::AppSettings; 50 | use structopt::StructOpt; 51 | 52 | #[derive(StructOpt, Debug)] 53 | #[structopt(global_settings = &[AppSettings::ColoredHelp, AppSettings::VersionlessSubcommands])] 54 | enum Command { 55 | #[structopt(about = "Validate all projects listed in Embark's Open Source website data.json")] 56 | ValidateAll(ValidateAll), 57 | 58 | #[structopt(about = "Validate one project from Embark's GitHub organisation")] 59 | Validate { name: String }, 60 | } 61 | 62 | #[derive(StructOpt, Debug)] 63 | #[structopt(flatten)] 64 | struct ValidateAll { 65 | #[structopt(long("slack-webhook-url"))] 66 | slack_webhook_url: Option, 67 | 68 | #[structopt(long("github-api-token"))] 69 | github_api_token: Option, 70 | } 71 | 72 | #[tokio::main] 73 | async fn main() -> eyre::Result<()> { 74 | match Command::from_args() { 75 | Command::ValidateAll(options) => validate::all(options).await, 76 | Command::Validate { name } => validate::one(name).await, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/policy.rs: -------------------------------------------------------------------------------- 1 | //! Policies for specific projects or users 2 | 3 | /// Generally we require that all maintainers of Embark Studios open source projects are 4 | /// part of the Embark org, but this list allows some explicit exceptions 5 | pub const ALLOWED_NON_EMBARK_MAINTAINERS: [&str; 3] = [ 6 | // Emil (https://github.com/emilk) worked at Embark and built 2 open source crates that he continues to co-maintain 7 | // - https://github.com/EmbarkStudios/puffin 8 | // - https://github.com/EmbarkStudios/poll-promise 9 | "emilk", 10 | // Keith (https://github.com/keith) is contributor-to and co-maintainer of the k8s-buildkite-plugin 11 | // https://github.com/EmbarkStudios/k8s-buildkite-plugin 12 | "keith", 13 | // Tomasz (https://github.com/h3r2tic) worked at Embark and built kajiya that he contains to maintain as experimental codebase 14 | "h3r2tic", 15 | ]; 16 | 17 | /// Some project might be public but not quite ready to be listed on the website 18 | pub const IGNORED_PROJECTS: [&str; 1] = [ 19 | // server-framework is still in development (and the name isn't final) so we don't it on the 20 | // website yet. 21 | "server-framework", 22 | ]; 23 | -------------------------------------------------------------------------------- /src/slack.rs: -------------------------------------------------------------------------------- 1 | use eyre::WrapErr; 2 | use serde_json::json; 3 | 4 | #[derive(Debug)] 5 | pub enum Block { 6 | Divider, 7 | Text(String), 8 | } 9 | 10 | impl Block { 11 | pub fn into_json(self) -> serde_json::Value { 12 | match self { 13 | Self::Divider => json!({ "type": "divider" }), 14 | Self::Text(text) => json!({ 15 | "type": "section", 16 | "text": { "type": "mrkdwn", "text": text } 17 | }), 18 | } 19 | } 20 | } 21 | 22 | fn blocks_json(blocks: Vec) -> serde_json::Value { 23 | let blocks = blocks.into_iter().map(|b| b.into_json()).collect(); 24 | json!({ 25 | "blocks": serde_json::Value::Array(blocks), 26 | }) 27 | } 28 | 29 | pub async fn send_webhook(webhook_url: &str, blocks: Vec) -> eyre::Result<()> { 30 | reqwest::Client::new() 31 | .post(webhook_url) 32 | .json(&blocks_json(blocks)) 33 | .send() 34 | .await 35 | .wrap_err("Unable to send webhook to Slack")? 36 | .error_for_status() 37 | .wrap_err("Unable to send webhook to Slack") 38 | .map(|_| ()) 39 | } 40 | -------------------------------------------------------------------------------- /src/validate.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod project; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | 7 | use self::{context::*, project::Project}; 8 | use crate::{policy::IGNORED_PROJECTS, slack, ValidateAll}; 9 | use eyre::eyre; 10 | use itertools::Itertools; 11 | 12 | /// Validate all projects listed in the data.json of the Embark Open Source 13 | /// website. 14 | pub(crate) async fn all(options: ValidateAll) -> eyre::Result<()> { 15 | let ValidateAll { 16 | slack_webhook_url, 17 | github_api_token, 18 | } = options; 19 | 20 | // Lookup required contextual information 21 | let context = Context::get(github_api_token).await?; 22 | 23 | // Download list of maintained projects and then validate each one 24 | let futures = context 25 | .all_projects() 26 | .into_iter() 27 | .filter(|project| !IGNORED_PROJECTS.contains(&project.as_str())) 28 | .map(Project::new) 29 | .map(|project| project.validate(&context)); 30 | let projects = futures::future::join_all(futures).await; 31 | 32 | // Print results 33 | projects.iter().for_each(print_status); 34 | 35 | // Collected the projects with issues 36 | let problem_projects: Vec<_> = projects 37 | .into_iter() 38 | .filter(|project| project.has_errors()) 39 | .collect(); 40 | 41 | // If there is no problem we are done and can return 42 | if problem_projects.is_empty() { 43 | return Ok(()); 44 | } 45 | 46 | // Send a message to slack if a webhook URL has been given 47 | if let Some(url) = slack_webhook_url { 48 | let blocks = slack_notification_blocks(problem_projects.as_slice()); 49 | slack::send_webhook(&url, blocks).await?; 50 | } 51 | 52 | Err(eyre!("Not all projects conform to our guidelines")) 53 | } 54 | 55 | /// Validate a single project from the Embark Studios GitHub organisation. 56 | pub async fn one(project_name: String) -> eyre::Result<()> { 57 | // Lookup required contextual information 58 | let context = Context::get(None).await?; 59 | 60 | // Validate project 61 | let project = Project::new(project_name).validate(&context).await; 62 | print_status(&project); 63 | if project.has_errors() { 64 | Err(eyre!("The project does not conform to our guidelines")) 65 | } else { 66 | Ok(()) 67 | } 68 | } 69 | 70 | fn print_status(project: &Project) { 71 | if let Some(errors) = project.errors_to_string(true) { 72 | return print!("❌ {}\n{}\n", project.name, errors); 73 | } 74 | 75 | if let Ok(maintainers) = &project.maintainers { 76 | return println!("✔️ {} ({})", project.name, maintainers.iter().join(", ")); 77 | } 78 | 79 | unreachable!(); 80 | } 81 | 82 | fn slack_notification_blocks(projects: &[Project]) -> Vec { 83 | use slack::Block::{Divider, Text}; 84 | 85 | let head = "The following Embark open source projects have been found to \ 86 | have maintainership issues."; 87 | let foot = "This message was generated by the \ 88 | \ 89 | on GitHub Actions."; 90 | 91 | let mut blocks = Vec::with_capacity(projects.len() + 4); 92 | 93 | blocks.push(Text(head.to_string())); 94 | blocks.push(Divider); 95 | blocks.extend(projects.iter().flat_map(slack_project_block)); 96 | blocks.push(Divider); 97 | blocks.push(Text(foot.to_string())); 98 | blocks 99 | } 100 | 101 | fn slack_project_block(project: &Project) -> Option { 102 | let text = format!( 103 | ":red_circle: **\n```{error}```", 104 | name = &project.name, 105 | error = project.errors_to_string(false)?, 106 | ); 107 | Some(slack::Block::Text(text)) 108 | } 109 | -------------------------------------------------------------------------------- /src/validate/context.rs: -------------------------------------------------------------------------------- 1 | use crate::github; 2 | use eyre::WrapErr; 3 | use std::collections::{HashMap, HashSet}; 4 | 5 | /// Data about the state of Embark in general, to be used by multiple checks 6 | /// across all open source projects. 7 | /// This is fetched in advance to prevent multiple checks from having to fetch 8 | /// the same data, which would be wasteful and run the risk of hitting rate 9 | /// limits. 10 | #[derive(Debug)] 11 | pub struct Context { 12 | pub embark_github_organisation_members: HashSet, 13 | pub embark_github_repos: HashMap, 14 | pub rust_ecosystem_readme: String, 15 | pub opensource_website_projects: Vec, 16 | } 17 | 18 | impl Context { 19 | pub async fn get(github_api_token: Option) -> eyre::Result { 20 | let client = github::Client::new(github_api_token); 21 | 22 | let ( 23 | opensource_website_projects, 24 | embark_github_organisation_members, 25 | embark_github_repos, 26 | rust_ecosystem_readme, 27 | ) = futures::join!( 28 | download_projects_list(), 29 | client.public_organisation_members("EmbarkStudios"), 30 | client.organisation_repos("EmbarkStudios"), 31 | github::download_repo_file("EmbarkStudios", "rust-ecosystem", "main", "README.md") 32 | ); 33 | 34 | Ok(Self { 35 | embark_github_organisation_members: embark_github_organisation_members?, 36 | opensource_website_projects: opensource_website_projects?, 37 | rust_ecosystem_readme: rust_ecosystem_readme?, 38 | embark_github_repos: embark_github_repos?, 39 | }) 40 | } 41 | 42 | /// Get the names of all projects found on GitHub and in the opensource 43 | /// website data.json. We lookup from both as a project may accidentally be 44 | /// in one but not the other. 45 | pub fn all_projects(&self) -> HashSet { 46 | let website_projects = self.opensource_website_projects.iter().map(|proj| { 47 | proj.repo 48 | .as_ref() 49 | .and_then(|repo_url| repo_url.rfind('/').map(|i| &repo_url[i + 1..])) 50 | .unwrap_or(&proj.name) 51 | .to_owned() 52 | }); 53 | let github_projects = self 54 | .embark_github_repos 55 | .values() 56 | .filter(|project| project.is_public_active_source_project()) 57 | .map(|project| project.name.to_string()); 58 | website_projects.chain(github_projects).collect() 59 | } 60 | } 61 | 62 | async fn download_projects_list() -> eyre::Result> { 63 | let data = github::download_repo_json_file::( 64 | "EmbarkStudios", 65 | "opensource-website", 66 | "main", 67 | "static/data/data.json", 68 | ) 69 | .await 70 | .wrap_err("Unable to get list of open source Embark projects")?; 71 | Ok(data.projects) 72 | } 73 | 74 | #[derive(Debug, Clone, serde::Deserialize)] 75 | pub struct OpenSourceWebsiteData { 76 | pub projects: Vec, 77 | } 78 | 79 | #[derive(Debug, Clone, serde::Deserialize)] 80 | pub struct OpenSourceWebsiteDataProject { 81 | pub name: String, 82 | pub repo: Option, 83 | #[serde(default)] 84 | pub tags: HashSet, 85 | } 86 | -------------------------------------------------------------------------------- /src/validate/project.rs: -------------------------------------------------------------------------------- 1 | use super::context::Context; 2 | use crate::github; 3 | use eyre::{eyre, WrapErr}; 4 | use futures::TryFutureExt; 5 | use itertools::Itertools; 6 | use std::{collections::HashSet, ops::Not}; 7 | 8 | #[derive(Debug)] 9 | pub struct Project { 10 | pub name: String, 11 | /// Projects must have a maintainer at Embark 12 | pub maintainers: eyre::Result>, 13 | // Rust based projects must be included in the rust-ecosystem README. 14 | rust_ecosystem_registration: eyre::Result<()>, 15 | // Projects must be included in the opensource website data.json 16 | website_data_inclusion: eyre::Result<()>, 17 | } 18 | 19 | impl Project { 20 | pub fn new(name: String) -> Self { 21 | Self { 22 | name, 23 | maintainers: not_yet_checked(), 24 | website_data_inclusion: not_yet_checked(), 25 | rust_ecosystem_registration: not_yet_checked(), 26 | } 27 | } 28 | 29 | pub async fn validate(self, context: &Context) -> Self { 30 | let maintainers = self.lookup_project_maintainers(context).await; 31 | let rust_ecosystem_registration = self.check_rust_ecosystem_registration(context); 32 | let website_data_inclusion = self.check_website_data_inclusion(context); 33 | 34 | Self { 35 | name: self.name, 36 | maintainers, 37 | website_data_inclusion, 38 | rust_ecosystem_registration, 39 | } 40 | } 41 | 42 | pub fn has_errors(&self) -> bool { 43 | !self.errors().is_empty() 44 | } 45 | 46 | pub fn errors(&self) -> Vec<&eyre::Report> { 47 | let Self { 48 | name: _, 49 | maintainers, 50 | website_data_inclusion, 51 | rust_ecosystem_registration, 52 | } = self; 53 | vec![ 54 | maintainers.as_ref().err(), 55 | website_data_inclusion.as_ref().err(), 56 | rust_ecosystem_registration.as_ref().err(), 57 | ] 58 | .into_iter() 59 | .flatten() 60 | .collect() 61 | } 62 | 63 | pub fn errors_to_string(&self, indent: bool) -> Option { 64 | let errors = self.errors(); 65 | if errors.is_empty() { 66 | return None; 67 | } 68 | Some( 69 | errors 70 | .into_iter() 71 | .map(|error| crate::error::cause_string(error.as_ref(), indent)) 72 | .join("\n"), 73 | ) 74 | } 75 | 76 | pub async fn lookup_project_maintainers( 77 | &self, 78 | context: &Context, 79 | ) -> eyre::Result> { 80 | // Download CODEOWNERS from one of the accepted branches 81 | let get = |branch| { 82 | github::download_repo_file("EmbarkStudios", &self.name, branch, ".github/CODEOWNERS") 83 | }; 84 | let text = get("main") 85 | .or_else(|_| get("master")) 86 | .await 87 | .wrap_err("Unable to determine maintainers")?; 88 | 89 | // Determine if there is at least 1 primary maintainer listed for each project 90 | let maintainers = github::CodeOwners::new(&text) 91 | .wrap_err("Unable to determine maintainers")? 92 | .primary_maintainers() 93 | .cloned() 94 | .ok_or_else(|| eyre!("No maintainers were found for * the CODEOWNERS file"))?; 95 | 96 | // Ensure all maintainers are in the EmbarkStudios organisation 97 | let mut maintainers_not_in_embark = maintainers 98 | .difference(&context.embark_github_organisation_members) 99 | .filter(|user_name| { 100 | // filter out non-embark users that are explicitly allowed to be maintained 101 | crate::policy::ALLOWED_NON_EMBARK_MAINTAINERS 102 | .iter() 103 | .any(|a| a == user_name) 104 | .not() 105 | }) 106 | .peekable(); 107 | if maintainers_not_in_embark.peek().is_some() { 108 | return Err(eyre!( 109 | "Maintainers not public EmbarkStudios members: {}", 110 | maintainers_not_in_embark.join(", "), 111 | )); 112 | } 113 | 114 | Ok(maintainers) 115 | } 116 | 117 | pub fn check_rust_ecosystem_registration(&self, context: &Context) -> eyre::Result<()> { 118 | let tags = match context 119 | .opensource_website_projects 120 | .iter() 121 | .find(|proj| proj.name == self.name) 122 | { 123 | Some(project) => &project.tags, 124 | None => return Ok(()), 125 | }; 126 | if tags.contains("rust") && !context.rust_ecosystem_readme.contains(&self.name) { 127 | Err(eyre!("Rust project not in the rust-ecosystem README")) 128 | } else { 129 | Ok(()) 130 | } 131 | } 132 | 133 | pub fn check_website_data_inclusion(&self, context: &Context) -> eyre::Result<()> { 134 | if context 135 | .opensource_website_projects 136 | .iter() 137 | .any(|proj| proj.name == self.name) 138 | { 139 | Ok(()) 140 | } else { 141 | Err(eyre!( 142 | "Project not included in opensource-website data.json" 143 | )) 144 | } 145 | } 146 | } 147 | 148 | fn not_yet_checked() -> eyre::Result { 149 | Err(eyre!("This property has not yet been validated")) 150 | } 151 | -------------------------------------------------------------------------------- /src/validate/tests.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | 3 | use super::*; 4 | 5 | fn make_context() -> Context { 6 | Context { 7 | embark_github_organisation_members: HashSet::new(), 8 | embark_github_repos: HashMap::new(), 9 | rust_ecosystem_readme: "Readme!".to_string(), 10 | opensource_website_projects: Vec::new(), 11 | } 12 | } 13 | 14 | fn make_website_project(name: &str) -> OpenSourceWebsiteDataProject { 15 | OpenSourceWebsiteDataProject { 16 | name: name.to_string(), 17 | repo: None, 18 | tags: HashSet::new(), 19 | } 20 | } 21 | 22 | #[test] 23 | fn check_website_data_inclusion_ok() { 24 | let name = "some-project"; 25 | let project = Project::new(name.to_string()); 26 | let mut context = make_context(); 27 | 28 | // OK if the project is in the website data.json 29 | context 30 | .opensource_website_projects 31 | .push(make_website_project(name)); 32 | assert!(project.check_website_data_inclusion(&context).is_ok()) 33 | } 34 | 35 | #[test] 36 | fn check_website_data_inclusion_ko() { 37 | let name = "some-project"; 38 | let project = Project::new(name.to_string()); 39 | let context = make_context(); 40 | 41 | // Error if the project is not in the website data.json 42 | assert!(project.check_website_data_inclusion(&context).is_err()); 43 | } 44 | --------------------------------------------------------------------------------