├── .github └── workflows │ ├── autofix.yml │ └── ci.yml ├── .gitignore ├── .prettierrc ├── .release-plz.toml ├── .rustfmt.toml ├── .schema └── latest.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── crates ├── gh-workflow-macros │ ├── CHANGELOG.md │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── gh-workflow-tailcall │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── standard.rs │ └── tests │ │ └── ci.rs └── gh-workflow │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── src │ ├── cargo.rs │ ├── comment.yml │ ├── ctx.rs │ ├── error.rs │ ├── event.rs │ ├── generate.rs │ ├── lib.rs │ ├── release_plz.rs │ ├── rust_flag.rs │ ├── snapshots │ │ ├── gh_workflow__generate__tests__add_needs_job.snap │ │ └── gh_workflow__generate__tests__missing_add_job.snap │ ├── toolchain.rs │ └── workflow.rs │ └── tests │ ├── fixtures │ ├── workflow-bench.yml │ ├── workflow-ci.yml │ ├── workflow-demo.yml │ └── workflow-rust.yml │ └── test-workflow.rs └── renovate.json /.github/workflows/autofix.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------- 2 | # ------------------------------- WARNING --------------------------- 3 | # ------------------------------------------------------------------- 4 | # 5 | # This file was automatically generated by gh-workflows using the 6 | # gh-workflow-gen bin. You should add and commit this file to your 7 | # git repository. **DO NOT EDIT THIS FILE BY HAND!** Any manual changes 8 | # will be lost if the file is regenerated. 9 | # 10 | # To make modifications, update your `build.rs` configuration to adjust 11 | # the workflow description as needed, then regenerate this file to apply 12 | # those changes. 13 | # 14 | # ------------------------------------------------------------------- 15 | # ----------------------------- END WARNING ------------------------- 16 | # ------------------------------------------------------------------- 17 | 18 | name: autofix.ci 19 | env: 20 | RUSTFLAGS: -Dwarnings 21 | on: 22 | pull_request: 23 | types: 24 | - opened 25 | - synchronize 26 | - reopened 27 | branches: 28 | - main 29 | push: 30 | branches: 31 | - main 32 | tags: 33 | - v* 34 | jobs: 35 | lint: 36 | name: Lint Fix 37 | runs-on: ubuntu-latest 38 | permissions: 39 | contents: read 40 | steps: 41 | - name: Checkout Code 42 | uses: actions/checkout@v4 43 | - name: Setup Rust Toolchain 44 | uses: actions-rust-lang/setup-rust-toolchain@v1 45 | with: 46 | toolchain: nightly 47 | components: clippy, rustfmt 48 | cache: true 49 | cache-directories: |- 50 | ~/.cargo/registry 51 | ~/.cargo/git 52 | target 53 | - name: Cargo Fmt 54 | run: cargo +nightly fmt --all 55 | - name: Cargo Clippy 56 | run: cargo +nightly clippy --fix --allow-dirty --all-features --workspace -- -D warnings 57 | - uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef 58 | concurrency: 59 | group: autofix-${{github.ref}} 60 | cancel-in-progress: false 61 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------- 2 | # ------------------------------- WARNING --------------------------- 3 | # ------------------------------------------------------------------- 4 | # 5 | # This file was automatically generated by gh-workflows using the 6 | # gh-workflow-gen bin. You should add and commit this file to your 7 | # git repository. **DO NOT EDIT THIS FILE BY HAND!** Any manual changes 8 | # will be lost if the file is regenerated. 9 | # 10 | # To make modifications, update your `build.rs` configuration to adjust 11 | # the workflow description as needed, then regenerate this file to apply 12 | # those changes. 13 | # 14 | # ------------------------------------------------------------------- 15 | # ----------------------------- END WARNING ------------------------- 16 | # ------------------------------------------------------------------- 17 | 18 | name: ci 19 | env: 20 | RUSTFLAGS: -Dwarnings 21 | on: 22 | pull_request: 23 | types: 24 | - opened 25 | - synchronize 26 | - reopened 27 | branches: 28 | - main 29 | push: 30 | branches: 31 | - main 32 | tags: 33 | - v* 34 | jobs: 35 | build: 36 | name: Build and Test 37 | runs-on: ubuntu-latest 38 | permissions: 39 | contents: read 40 | steps: 41 | - name: Checkout Code 42 | uses: actions/checkout@v4 43 | - name: Setup Rust Toolchain 44 | uses: actions-rust-lang/setup-rust-toolchain@v1 45 | with: 46 | toolchain: stable 47 | - name: Install nextest 48 | run: cargo install cargo-nextest --locked 49 | - name: Cargo Nextest 50 | run: cargo nextest run --all-features --workspace 51 | lint: 52 | name: Lint 53 | runs-on: ubuntu-latest 54 | permissions: 55 | contents: read 56 | steps: 57 | - name: Checkout Code 58 | uses: actions/checkout@v4 59 | - name: Setup Rust Toolchain 60 | uses: actions-rust-lang/setup-rust-toolchain@v1 61 | with: 62 | toolchain: nightly 63 | components: clippy, rustfmt 64 | cache: true 65 | cache-directories: |- 66 | ~/.cargo/registry 67 | ~/.cargo/git 68 | target 69 | - name: Cargo Fmt 70 | run: cargo +nightly fmt --all --check 71 | - name: Cargo Clippy 72 | run: cargo +nightly clippy --all-features --workspace -- -D warnings 73 | release: 74 | needs: 75 | - build 76 | - lint 77 | if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }} 78 | name: Release 79 | runs-on: ubuntu-latest 80 | permissions: 81 | contents: write 82 | pull-requests: write 83 | packages: write 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 87 | steps: 88 | - name: Checkout Code 89 | uses: actions/checkout@v4 90 | - name: Release Plz 91 | uses: release-plz/action@v0.5 92 | with: 93 | command: release 94 | concurrency: 95 | group: release-${{github.ref}} 96 | cancel-in-progress: false 97 | release-pr: 98 | needs: 99 | - build 100 | - lint 101 | if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }} 102 | name: Release Pr 103 | runs-on: ubuntu-latest 104 | permissions: 105 | contents: write 106 | pull-requests: write 107 | packages: write 108 | env: 109 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 110 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 111 | steps: 112 | - name: Checkout Code 113 | uses: actions/checkout@v4 114 | - name: Release Plz 115 | uses: release-plz/action@v0.5 116 | with: 117 | command: release-pr 118 | concurrency: 119 | group: release-${{github.ref}} 120 | cancel-in-progress: false 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /**/target/ 3 | .forge.* 4 | .env 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true 3 | } 4 | -------------------------------------------------------------------------------- /.release-plz.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | dependencies_update = true 3 | release_always = false 4 | 5 | [[package]] 6 | name = "gh-workflow-macros" 7 | version_group = "core" 8 | 9 | [[package]] 10 | name = "gh-workflow" 11 | version_group = "core" 12 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | struct_lit_width = 60 3 | imports_granularity = "Module" 4 | group_imports = "StdExternalCrate" 5 | wrap_comments = true 6 | comment_width = 80 7 | -------------------------------------------------------------------------------- /.schema/latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://github.com/release-plz/release-plz/.schema/latest.json", 4 | "title": "Config", 5 | "description": "You can find the documentation of the configuration file [here](https://release-plz.dev/docs/config).", 6 | "type": "object", 7 | "properties": { 8 | "changelog": { 9 | "default": { 10 | "body": null, 11 | "commit_parsers": null, 12 | "commit_preprocessors": null, 13 | "header": null, 14 | "link_parsers": null, 15 | "protect_breaking_commits": null, 16 | "sort_commits": null, 17 | "tag_pattern": null, 18 | "trim": null 19 | }, 20 | "allOf": [ 21 | { 22 | "$ref": "#/definitions/ChangelogCfg" 23 | } 24 | ] 25 | }, 26 | "package": { 27 | "title": "Package", 28 | "description": "Package-specific configuration. This overrides `workspace`. Not all settings of `workspace` can be overridden.", 29 | "default": [], 30 | "type": "array", 31 | "items": { 32 | "$ref": "#/definitions/PackageSpecificConfigWithName" 33 | } 34 | }, 35 | "workspace": { 36 | "title": "Workspace", 37 | "description": "Global configuration. Applied to all packages by default.", 38 | "default": { 39 | "allow_dirty": null, 40 | "changelog_config": null, 41 | "changelog_path": null, 42 | "changelog_update": null, 43 | "dependencies_update": null, 44 | "features_always_increment_minor": null, 45 | "git_release_body": null, 46 | "git_release_draft": null, 47 | "git_release_enable": null, 48 | "git_release_latest": null, 49 | "git_release_name": null, 50 | "git_release_type": null, 51 | "git_tag_enable": null, 52 | "git_tag_name": null, 53 | "pr_body": null, 54 | "pr_branch_prefix": null, 55 | "pr_draft": false, 56 | "pr_labels": [], 57 | "pr_name": null, 58 | "publish": null, 59 | "publish_all_features": null, 60 | "publish_allow_dirty": null, 61 | "publish_features": null, 62 | "publish_no_verify": null, 63 | "publish_timeout": null, 64 | "release": null, 65 | "release_always": null, 66 | "release_commits": null, 67 | "repo_url": null, 68 | "semver_check": null 69 | }, 70 | "allOf": [ 71 | { 72 | "$ref": "#/definitions/Workspace" 73 | } 74 | ] 75 | } 76 | }, 77 | "additionalProperties": false, 78 | "definitions": { 79 | "ChangelogCfg": { 80 | "type": "object", 81 | "properties": { 82 | "body": { 83 | "description": "Template that represents a single release in the changelog. It contains the commit messages. This is a [tera](https://keats.github.io/tera/) template.", 84 | "type": [ 85 | "string", 86 | "null" 87 | ] 88 | }, 89 | "commit_parsers": { 90 | "description": "Commits that don't match any of the commit parsers are skipped.", 91 | "type": [ 92 | "array", 93 | "null" 94 | ], 95 | "items": { 96 | "$ref": "#/definitions/CommitParser" 97 | } 98 | }, 99 | "commit_preprocessors": { 100 | "description": "An array of commit preprocessors for manipulating the commit messages before parsing/grouping them.", 101 | "type": [ 102 | "array", 103 | "null" 104 | ], 105 | "items": { 106 | "$ref": "#/definitions/TextProcessor" 107 | } 108 | }, 109 | "header": { 110 | "description": "Text at the beginning of the changelog.", 111 | "type": [ 112 | "string", 113 | "null" 114 | ] 115 | }, 116 | "link_parsers": { 117 | "description": "An array of link parsers for extracting external references, and turning them into URLs, using regex.", 118 | "type": [ 119 | "array", 120 | "null" 121 | ], 122 | "items": { 123 | "$ref": "#/definitions/LinkParser" 124 | } 125 | }, 126 | "protect_breaking_commits": { 127 | "description": "Whether to protect all breaking changes from being skipped by a commit parser.", 128 | "type": [ 129 | "boolean", 130 | "null" 131 | ] 132 | }, 133 | "sort_commits": { 134 | "description": "How to sort the commits inside the various sections.", 135 | "anyOf": [ 136 | { 137 | "$ref": "#/definitions/Sorting" 138 | }, 139 | { 140 | "type": "null" 141 | } 142 | ] 143 | }, 144 | "tag_pattern": { 145 | "description": "A regular expression for matching the git tags to add to the changelog.", 146 | "type": [ 147 | "string", 148 | "null" 149 | ] 150 | }, 151 | "trim": { 152 | "description": "If set to `true`, leading and trailing whitespace are removed from [`Self::body`].", 153 | "type": [ 154 | "boolean", 155 | "null" 156 | ] 157 | } 158 | }, 159 | "additionalProperties": false 160 | }, 161 | "CommitParser": { 162 | "description": "Parser for grouping commits.", 163 | "type": "object", 164 | "properties": { 165 | "body": { 166 | "description": "Regex for matching the commit body.", 167 | "type": [ 168 | "string", 169 | "null" 170 | ] 171 | }, 172 | "default_scope": { 173 | "description": "Default scope of the commit.", 174 | "type": [ 175 | "string", 176 | "null" 177 | ] 178 | }, 179 | "field": { 180 | "description": "Field name of the commit to match the regex against.", 181 | "type": [ 182 | "string", 183 | "null" 184 | ] 185 | }, 186 | "group": { 187 | "description": "Group of the commit.", 188 | "type": [ 189 | "string", 190 | "null" 191 | ] 192 | }, 193 | "message": { 194 | "description": "Regex for matching the commit message.", 195 | "type": [ 196 | "string", 197 | "null" 198 | ] 199 | }, 200 | "pattern": { 201 | "description": "Regex for matching the field value.", 202 | "type": [ 203 | "string", 204 | "null" 205 | ] 206 | }, 207 | "scope": { 208 | "description": "Commit scope for overriding the default scope.", 209 | "type": [ 210 | "string", 211 | "null" 212 | ] 213 | }, 214 | "sha": { 215 | "description": "SHA1 of the commit.", 216 | "type": [ 217 | "string", 218 | "null" 219 | ] 220 | }, 221 | "skip": { 222 | "description": "Whether to skip this commit group.", 223 | "type": [ 224 | "boolean", 225 | "null" 226 | ] 227 | } 228 | } 229 | }, 230 | "LinkParser": { 231 | "type": "object", 232 | "required": [ 233 | "href", 234 | "pattern" 235 | ], 236 | "properties": { 237 | "href": { 238 | "description": "The string used to generate the link URL.", 239 | "type": "string" 240 | }, 241 | "pattern": { 242 | "description": "Regex for finding links in the commit message.", 243 | "type": "string" 244 | }, 245 | "text": { 246 | "description": "The string used to generate the link text.", 247 | "type": [ 248 | "string", 249 | "null" 250 | ] 251 | } 252 | } 253 | }, 254 | "PackageSpecificConfigWithName": { 255 | "description": "Config at the `[[package]]` level.", 256 | "type": "object", 257 | "required": [ 258 | "name" 259 | ], 260 | "properties": { 261 | "changelog_include": { 262 | "title": "Changelog Include", 263 | "description": "List of package names. Include the changelogs of these packages in the changelog of the current package.", 264 | "type": [ 265 | "array", 266 | "null" 267 | ], 268 | "items": { 269 | "type": "string" 270 | } 271 | }, 272 | "changelog_path": { 273 | "title": "Changelog Path", 274 | "description": "Normally the changelog is placed in the same directory of the Cargo.toml file. The user can provide a custom path here. `changelog_path` is propagated to the commands: `update`, `release-pr` and `release`.", 275 | "type": [ 276 | "string", 277 | "null" 278 | ] 279 | }, 280 | "changelog_update": { 281 | "title": "Changelog Update", 282 | "description": "Whether to create/update changelog or not. If unspecified, the changelog is updated.", 283 | "type": [ 284 | "boolean", 285 | "null" 286 | ] 287 | }, 288 | "features_always_increment_minor": { 289 | "title": "Features Always Increment Minor Version", 290 | "description": "- If `true`, feature commits will always bump the minor version, even in 0.x releases. - If `false` (default), feature commits will only bump the minor version starting with 1.x releases.", 291 | "type": [ 292 | "boolean", 293 | "null" 294 | ] 295 | }, 296 | "git_release_body": { 297 | "title": "Git Release Body", 298 | "description": "Tera template of the git release body created by release-plz.", 299 | "type": [ 300 | "string", 301 | "null" 302 | ] 303 | }, 304 | "git_release_draft": { 305 | "title": "Git Release Draft", 306 | "description": "If true, will not auto-publish the release.", 307 | "type": [ 308 | "boolean", 309 | "null" 310 | ] 311 | }, 312 | "git_release_enable": { 313 | "title": "Git Release Enable", 314 | "description": "Publish the GitHub/Gitea release for the created git tag. Enabled by default.", 315 | "type": [ 316 | "boolean", 317 | "null" 318 | ] 319 | }, 320 | "git_release_latest": { 321 | "title": "Git Release Latest", 322 | "description": "If true, will set the git release as latest.", 323 | "type": [ 324 | "boolean", 325 | "null" 326 | ] 327 | }, 328 | "git_release_name": { 329 | "title": "Git Release Name", 330 | "description": "Tera template of the git release name created by release-plz.", 331 | "type": [ 332 | "string", 333 | "null" 334 | ] 335 | }, 336 | "git_release_type": { 337 | "title": "Git Release Type", 338 | "description": "Whether to mark the created release as not ready for production.", 339 | "anyOf": [ 340 | { 341 | "$ref": "#/definitions/ReleaseType" 342 | }, 343 | { 344 | "type": "null" 345 | } 346 | ] 347 | }, 348 | "git_tag_enable": { 349 | "title": "Git Tag Enable", 350 | "description": "Publish the git tag for the new package version. Enabled by default.", 351 | "type": [ 352 | "boolean", 353 | "null" 354 | ] 355 | }, 356 | "git_tag_name": { 357 | "title": "Git Tag Name", 358 | "description": "Tera template of the git tag name created by release-plz.", 359 | "type": [ 360 | "string", 361 | "null" 362 | ] 363 | }, 364 | "name": { 365 | "type": "string" 366 | }, 367 | "publish": { 368 | "title": "Publish", 369 | "description": "If `false`, don't run `cargo publish`.", 370 | "type": [ 371 | "boolean", 372 | "null" 373 | ] 374 | }, 375 | "publish_all_features": { 376 | "title": "Publish All Features", 377 | "description": "If `true`, add the `--all-features` flag to the `cargo publish` command.", 378 | "type": [ 379 | "boolean", 380 | "null" 381 | ] 382 | }, 383 | "publish_allow_dirty": { 384 | "title": "Publish Allow Dirty", 385 | "description": "If `true`, add the `--allow-dirty` flag to the `cargo publish` command.", 386 | "type": [ 387 | "boolean", 388 | "null" 389 | ] 390 | }, 391 | "publish_features": { 392 | "title": "Publish Features", 393 | "description": "If `[\"a\", \"b\", \"c\"]`, add the `--features=a,b,c` flag to the `cargo publish` command.", 394 | "type": [ 395 | "array", 396 | "null" 397 | ], 398 | "items": { 399 | "type": "string" 400 | } 401 | }, 402 | "publish_no_verify": { 403 | "title": "Publish No Verify", 404 | "description": "If `true`, add the `--no-verify` flag to the `cargo publish` command.", 405 | "type": [ 406 | "boolean", 407 | "null" 408 | ] 409 | }, 410 | "release": { 411 | "title": "Release", 412 | "description": "Used to toggle off the update/release process for a workspace or package.", 413 | "type": [ 414 | "boolean", 415 | "null" 416 | ] 417 | }, 418 | "semver_check": { 419 | "title": "Semver Check", 420 | "description": "Controls when to run cargo-semver-checks. If unspecified, run cargo-semver-checks if the package is a library.", 421 | "type": [ 422 | "boolean", 423 | "null" 424 | ] 425 | }, 426 | "version_group": { 427 | "title": "Version group", 428 | "description": "The name of a group of packages that needs to have the same version.", 429 | "type": [ 430 | "string", 431 | "null" 432 | ] 433 | } 434 | } 435 | }, 436 | "ReleaseType": { 437 | "oneOf": [ 438 | { 439 | "title": "Prod", 440 | "description": "Will mark the release as ready for production.", 441 | "type": "string", 442 | "enum": [ 443 | "prod" 444 | ] 445 | }, 446 | { 447 | "title": "Pre", 448 | "description": "Will mark the release as not ready for production. I.e. as pre-release.", 449 | "type": "string", 450 | "enum": [ 451 | "pre" 452 | ] 453 | }, 454 | { 455 | "title": "Auto", 456 | "description": "Will mark the release as not ready for production in case there is a semver pre-release in the tag e.g. v1.0.0-rc1. Otherwise, will mark the release as ready for production.", 457 | "type": "string", 458 | "enum": [ 459 | "auto" 460 | ] 461 | } 462 | ] 463 | }, 464 | "Sorting": { 465 | "type": "string", 466 | "enum": [ 467 | "oldest", 468 | "newest" 469 | ] 470 | }, 471 | "TextProcessor": { 472 | "description": "Used for modifying commit messages.", 473 | "type": "object", 474 | "required": [ 475 | "pattern" 476 | ], 477 | "properties": { 478 | "pattern": { 479 | "description": "Regex for matching a text to replace.", 480 | "type": "string" 481 | }, 482 | "replace": { 483 | "description": "Replacement text.", 484 | "type": [ 485 | "string", 486 | "null" 487 | ] 488 | }, 489 | "replace_command": { 490 | "description": "Command that will be run for replacing the commit message.", 491 | "type": [ 492 | "string", 493 | "null" 494 | ] 495 | } 496 | } 497 | }, 498 | "Workspace": { 499 | "description": "Config at the `[workspace]` level.", 500 | "type": "object", 501 | "properties": { 502 | "allow_dirty": { 503 | "title": "Allow Dirty", 504 | "description": "- If `true`, allow dirty working directories to be updated. The uncommitted changes will be part of the update. - If `false` or [`Option::None`], the command will fail if the working directory is dirty.", 505 | "type": [ 506 | "boolean", 507 | "null" 508 | ] 509 | }, 510 | "changelog_config": { 511 | "title": "Changelog Config", 512 | "description": "Path to the git cliff configuration file. Defaults to the `keep a changelog` configuration.", 513 | "type": [ 514 | "string", 515 | "null" 516 | ] 517 | }, 518 | "changelog_path": { 519 | "title": "Changelog Path", 520 | "description": "Normally the changelog is placed in the same directory of the Cargo.toml file. The user can provide a custom path here. `changelog_path` is propagated to the commands: `update`, `release-pr` and `release`.", 521 | "type": [ 522 | "string", 523 | "null" 524 | ] 525 | }, 526 | "changelog_update": { 527 | "title": "Changelog Update", 528 | "description": "Whether to create/update changelog or not. If unspecified, the changelog is updated.", 529 | "type": [ 530 | "boolean", 531 | "null" 532 | ] 533 | }, 534 | "dependencies_update": { 535 | "title": "Dependencies Update", 536 | "description": "- If `true`, update all the dependencies in the Cargo.lock file by running `cargo update`. - If `false` or [`Option::None`], only update the workspace packages by running `cargo update --workspace`.", 537 | "type": [ 538 | "boolean", 539 | "null" 540 | ] 541 | }, 542 | "features_always_increment_minor": { 543 | "title": "Features Always Increment Minor Version", 544 | "description": "- If `true`, feature commits will always bump the minor version, even in 0.x releases. - If `false` (default), feature commits will only bump the minor version starting with 1.x releases.", 545 | "type": [ 546 | "boolean", 547 | "null" 548 | ] 549 | }, 550 | "git_release_body": { 551 | "title": "Git Release Body", 552 | "description": "Tera template of the git release body created by release-plz.", 553 | "type": [ 554 | "string", 555 | "null" 556 | ] 557 | }, 558 | "git_release_draft": { 559 | "title": "Git Release Draft", 560 | "description": "If true, will not auto-publish the release.", 561 | "type": [ 562 | "boolean", 563 | "null" 564 | ] 565 | }, 566 | "git_release_enable": { 567 | "title": "Git Release Enable", 568 | "description": "Publish the GitHub/Gitea release for the created git tag. Enabled by default.", 569 | "type": [ 570 | "boolean", 571 | "null" 572 | ] 573 | }, 574 | "git_release_latest": { 575 | "title": "Git Release Latest", 576 | "description": "If true, will set the git release as latest.", 577 | "type": [ 578 | "boolean", 579 | "null" 580 | ] 581 | }, 582 | "git_release_name": { 583 | "title": "Git Release Name", 584 | "description": "Tera template of the git release name created by release-plz.", 585 | "type": [ 586 | "string", 587 | "null" 588 | ] 589 | }, 590 | "git_release_type": { 591 | "title": "Git Release Type", 592 | "description": "Whether to mark the created release as not ready for production.", 593 | "anyOf": [ 594 | { 595 | "$ref": "#/definitions/ReleaseType" 596 | }, 597 | { 598 | "type": "null" 599 | } 600 | ] 601 | }, 602 | "git_tag_enable": { 603 | "title": "Git Tag Enable", 604 | "description": "Publish the git tag for the new package version. Enabled by default.", 605 | "type": [ 606 | "boolean", 607 | "null" 608 | ] 609 | }, 610 | "git_tag_name": { 611 | "title": "Git Tag Name", 612 | "description": "Tera template of the git tag name created by release-plz.", 613 | "type": [ 614 | "string", 615 | "null" 616 | ] 617 | }, 618 | "pr_body": { 619 | "title": "PR Body", 620 | "description": "Tera template of the pull request's body created by release-plz.", 621 | "type": [ 622 | "string", 623 | "null" 624 | ] 625 | }, 626 | "pr_branch_prefix": { 627 | "title": "PR Branch Prefix", 628 | "description": "Prefix for the PR Branch", 629 | "type": [ 630 | "string", 631 | "null" 632 | ] 633 | }, 634 | "pr_draft": { 635 | "title": "PR Draft", 636 | "description": "If `true`, the created release PR will be marked as a draft.", 637 | "default": false, 638 | "type": "boolean" 639 | }, 640 | "pr_labels": { 641 | "title": "PR Labels", 642 | "description": "Labels to add to the release PR.", 643 | "default": [], 644 | "type": "array", 645 | "items": { 646 | "type": "string" 647 | } 648 | }, 649 | "pr_name": { 650 | "title": "PR Name", 651 | "description": "Tera template of the pull request's name created by release-plz.", 652 | "type": [ 653 | "string", 654 | "null" 655 | ] 656 | }, 657 | "publish": { 658 | "title": "Publish", 659 | "description": "If `false`, don't run `cargo publish`.", 660 | "type": [ 661 | "boolean", 662 | "null" 663 | ] 664 | }, 665 | "publish_all_features": { 666 | "title": "Publish All Features", 667 | "description": "If `true`, add the `--all-features` flag to the `cargo publish` command.", 668 | "type": [ 669 | "boolean", 670 | "null" 671 | ] 672 | }, 673 | "publish_allow_dirty": { 674 | "title": "Publish Allow Dirty", 675 | "description": "If `true`, add the `--allow-dirty` flag to the `cargo publish` command.", 676 | "type": [ 677 | "boolean", 678 | "null" 679 | ] 680 | }, 681 | "publish_features": { 682 | "title": "Publish Features", 683 | "description": "If `[\"a\", \"b\", \"c\"]`, add the `--features=a,b,c` flag to the `cargo publish` command.", 684 | "type": [ 685 | "array", 686 | "null" 687 | ], 688 | "items": { 689 | "type": "string" 690 | } 691 | }, 692 | "publish_no_verify": { 693 | "title": "Publish No Verify", 694 | "description": "If `true`, add the `--no-verify` flag to the `cargo publish` command.", 695 | "type": [ 696 | "boolean", 697 | "null" 698 | ] 699 | }, 700 | "publish_timeout": { 701 | "title": "Publish Timeout", 702 | "description": "Timeout for the publishing process", 703 | "type": [ 704 | "string", 705 | "null" 706 | ] 707 | }, 708 | "release": { 709 | "title": "Release", 710 | "description": "Used to toggle off the update/release process for a workspace or package.", 711 | "type": [ 712 | "boolean", 713 | "null" 714 | ] 715 | }, 716 | "release_always": { 717 | "title": "Release always", 718 | "description": "- If true, release-plz release will try to release your packages every time you run it (e.g. on every commit in the main branch). *(Default)*. - If false, `release-plz release` will try release your packages only when you merge the release pr. Use this if you want to commit your packages and publish them later. To determine if a pr is a release-pr, release-plz will check if the branch of the PR starts with `release-plz-`. So if you want to create a PR that should trigger a release (e.g. when you fix the CI), use this branch name format (e.g. `release-plz-fix-ci`).", 719 | "type": [ 720 | "boolean", 721 | "null" 722 | ] 723 | }, 724 | "release_commits": { 725 | "title": "Release Commits", 726 | "description": "Prepare release only if at least one commit respects this regex.", 727 | "type": [ 728 | "string", 729 | "null" 730 | ] 731 | }, 732 | "repo_url": { 733 | "title": "Repo URL", 734 | "description": "GitHub/Gitea repository url where your project is hosted. It is used to generate the changelog release link. It defaults to the url of the default remote.", 735 | "type": [ 736 | "string", 737 | "null" 738 | ], 739 | "format": "uri" 740 | }, 741 | "semver_check": { 742 | "title": "Semver Check", 743 | "description": "Controls when to run cargo-semver-checks. If unspecified, run cargo-semver-checks if the package is a library.", 744 | "type": [ 745 | "boolean", 746 | "null" 747 | ] 748 | } 749 | } 750 | } 751 | } 752 | } 753 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Awarnings", 4 | "Gollum", 5 | "quicktype", 6 | "rustfmt" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.5.0](https://github.com/tailcallhq/gh-workflow/compare/v0.4.1...v0.5.0) - 2024-11-28 9 | 10 | ### Other 11 | 12 | - fix workflow name 13 | 14 | ## [0.4.1](https://github.com/tailcallhq/rust-gh-workflow/compare/gh-workflow-v0.4.0...gh-workflow-v0.4.1) - 2024-11-16 15 | 16 | ### Fixed 17 | 18 | - error when `.github` dir does not exist ([#51](https://github.com/tailcallhq/rust-gh-workflow/pull/51)) 19 | 20 | ## [0.4.0](https://github.com/tailcallhq/rust-gh-workflow/compare/gh-workflow-v0.3.0...gh-workflow-v0.4.0) - 2024-11-13 21 | 22 | ### Other 23 | 24 | - lint fixes 25 | 26 | ## [0.3.0](https://github.com/tailcallhq/rust-gh-workflow/compare/gh-workflow-v0.2.1...gh-workflow-v0.3.0) - 2024-11-11 27 | 28 | ### Other 29 | 30 | - update workflow 31 | 32 | ## v0.2.0 (2024-11-11) 33 | 34 | 35 | 36 | 37 | 38 | ### Chore 39 | 40 | - update build.rs using `Generate` and custom name 41 | - impl `Step::working_directory` 42 | 43 | ### New Features 44 | 45 | - add `add_with` operator` on StepValue 46 | - add Setters for Event 47 | - Make generate public fixes #26 48 | 49 | ### Bug Fixes 50 | 51 | - `Job::runs_on` 52 | - add names to default Rust workflow 53 | - Improve type-safety for Step 54 | 55 | ### Refactor 56 | 57 | - many many changes 58 | 59 | ### Commit Statistics 60 | 61 | 62 | 63 | - 1 commit contributed to the release. 64 | - 1 commit was understood as [conventional](https://www.conventionalcommits.org). 65 | - 0 issues like '(#ID)' were seen in commit messages 66 | 67 | ### Commit Details 68 | 69 | 70 | 71 |
view details 72 | 73 | * **Uncategorized** 74 | - Improve type-safety for Step ([`790765a`](https://github.com/tailcallhq/rust-gh-workflow/commit/790765acf303891b491a37ee7d07413debd90f67)) 75 |
76 | 77 | ## v0.1.2 (2024-11-08) 78 | 79 | 80 | 81 | ### Chore 82 | 83 | - lint fixes 84 | 85 | ### New Features 86 | 87 | - add `Workflow::setup_rust()` helper 88 | 89 | ### Bug Fixes 90 | 91 | - add wasm to possible targets 92 | 93 | ### Commit Statistics 94 | 95 | 96 | 97 | - 4 commits contributed to the release. 98 | - 3 commits were understood as [conventional](https://www.conventionalcommits.org). 99 | - 0 issues like '(#ID)' were seen in commit messages 100 | 101 | ### Commit Details 102 | 103 | 104 | 105 |
view details 106 | 107 | * **Uncategorized** 108 | - Release gh-workflow v0.1.2 ([`e71bec6`](https://github.com/tailcallhq/rust-gh-workflow/commit/e71bec6475696c55f7db6cb399fe1572bde5d017)) 109 | - Lint fixes ([`9230d1f`](https://github.com/tailcallhq/rust-gh-workflow/commit/9230d1f1dc00b87038ec17f39deca77c9be6ffa6)) 110 | - Add `Workflow::setup_rust()` helper ([`5a3f184`](https://github.com/tailcallhq/rust-gh-workflow/commit/5a3f18477a2bddc662d3f100e7dd6cbf002cdd2f)) 111 | - Add wasm to possible targets ([`f74da3d`](https://github.com/tailcallhq/rust-gh-workflow/commit/f74da3df992ee7c10564679c10b695e5b96a85c6)) 112 |
113 | 114 | ## v0.1.1 (2024-11-08) 115 | 116 | ### Bug Fixes 117 | 118 | - add readme file 119 | 120 | ### Commit Statistics 121 | 122 | 123 | 124 | - 4 commits contributed to the release. 125 | - 1 commit was understood as [conventional](https://www.conventionalcommits.org). 126 | - 0 issues like '(#ID)' were seen in commit messages 127 | 128 | ### Commit Details 129 | 130 | 131 | 132 |
view details 133 | 134 | * **Uncategorized** 135 | - Release gh-workflow v0.1.1 ([`4dfbd11`](https://github.com/tailcallhq/rust-gh-workflow/commit/4dfbd11bb17f22245601c7a1d36d701328e8e646)) 136 | - Release gh-workflow v0.1.1 ([`c58445e`](https://github.com/tailcallhq/rust-gh-workflow/commit/c58445effa55bca60e2283205feba758365efe51)) 137 | - Release gh-workflow v0.1.1 ([`677a89e`](https://github.com/tailcallhq/rust-gh-workflow/commit/677a89e38a74410db14c7546499a1ce818befd96)) 138 | - Add readme file ([`55d81b0`](https://github.com/tailcallhq/rust-gh-workflow/commit/55d81b06bf673774e9559915cbe1cb49ccf0a6c5)) 139 |
140 | 141 | ## v0.1.0 (2024-11-08) 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | ### Chore 154 | 155 | - update license 156 | - add license 157 | - update build 158 | - accept path `AsRef` 159 | - add warning in generated config 160 | - autogen CI 161 | - update folder structure 162 | 163 | ### Chore 164 | 165 | - update license 166 | 167 | ### Chore 168 | 169 | - add changelog 170 | 171 | ### New Features 172 | 173 | - validate on CI 174 | - add events 175 | 176 | ### Bug Fixes 177 | 178 | - tests 179 | * refactor: move tests from `workflow.rs` to tests dir 180 | * remove unused deps 181 | * move fixtures dir 182 | * chore: rename files 183 | * chore: import updates 184 | * chore: update workflows 185 | * chore: update workflow 186 | * fix err on workflow-bench.yml 187 | * partial 188 | * partially fix tests 189 | * fix tests 190 | * drop unused struct 191 | * revert unwanted change in `needs` 192 | * revert unwanted change in `needs` 193 | * drop `build_matrix.yml` 194 | * revert 195 | 196 | ### Commit Statistics 197 | 198 | 199 | 200 | - 18 commits contributed to the release. 201 | - 12 commits were understood as [conventional](https://www.conventionalcommits.org). 202 | - 4 unique issues were worked on: [#13](https://github.com/tailcallhq/rust-gh-workflow/issues/13), [#14](https://github.com/tailcallhq/rust-gh-workflow/issues/14), [#19](https://github.com/tailcallhq/rust-gh-workflow/issues/19), [#21](https://github.com/tailcallhq/rust-gh-workflow/issues/21) 203 | 204 | ### Commit Details 205 | 206 | 207 | 208 |
view details 209 | 210 | * **[#13](https://github.com/tailcallhq/rust-gh-workflow/issues/13)** 211 | - Tests ([`e91cd89`](https://github.com/tailcallhq/rust-gh-workflow/commit/e91cd8944cfa9cf758889c4157f12bc01758fee1)) 212 | * **[#14](https://github.com/tailcallhq/rust-gh-workflow/issues/14)** 213 | - Autogen CI ([`dccdb36`](https://github.com/tailcallhq/rust-gh-workflow/commit/dccdb3612602559e3c39e83ae3894086e06c5a5d)) 214 | * **[#19](https://github.com/tailcallhq/rust-gh-workflow/issues/19)** 215 | - Accept path `AsRef` ([`55f52de`](https://github.com/tailcallhq/rust-gh-workflow/commit/55f52de53065d626aaea8007651251a9a86acc98)) 216 | * **[#21](https://github.com/tailcallhq/rust-gh-workflow/issues/21)** 217 | - Validate on CI ([`8f3615d`](https://github.com/tailcallhq/rust-gh-workflow/commit/8f3615d7d3e6b71946b09c68a4ec8dc7cb3418a7)) 218 | * **Uncategorized** 219 | - Release gh-workflow v0.1.0 ([`374d65c`](https://github.com/tailcallhq/rust-gh-workflow/commit/374d65c8c638b50efe5724d44f2bc1b409ab5a56)) 220 | - Update license ([`17a92e4`](https://github.com/tailcallhq/rust-gh-workflow/commit/17a92e4d66226b5b22feadce6b2b79326be328d1)) 221 | - Release gh-workflow v0.1.0 ([`3d5543b`](https://github.com/tailcallhq/rust-gh-workflow/commit/3d5543b427685752770a75ce3d078f7b38f7a5f2)) 222 | - Release gh-workflow v0.1.0 ([`4602f22`](https://github.com/tailcallhq/rust-gh-workflow/commit/4602f22c2437cad2467ee083402b0eb7f29ab045)) 223 | - Add changelog ([`cbf51ab`](https://github.com/tailcallhq/rust-gh-workflow/commit/cbf51abc82da429539cff463aebb83f941b62922)) 224 | - Update license ([`7f8b45f`](https://github.com/tailcallhq/rust-gh-workflow/commit/7f8b45f3f2065fc953da5fd4447183d0bcb94e38)) 225 | - Update manifest ([`770b3e3`](https://github.com/tailcallhq/rust-gh-workflow/commit/770b3e33773db936b91a416fb3ac26c809b2ad14)) 226 | - Add license ([`d73d9fa`](https://github.com/tailcallhq/rust-gh-workflow/commit/d73d9faf093e15fc9d91a9318ca84f6113a310b0)) 227 | - Update build ([`1a004fc`](https://github.com/tailcallhq/rust-gh-workflow/commit/1a004fc27cb6c44a6b76d5ca20b50dbfc90b4efe)) 228 | - Update event ([`a0bf957`](https://github.com/tailcallhq/rust-gh-workflow/commit/a0bf95769e66eed96cc9b5b81c51bcf38e1b49fc)) 229 | - Drop commented code ([`fe53956`](https://github.com/tailcallhq/rust-gh-workflow/commit/fe539566c65313f19f8773fea6cf78aa49cb7e65)) 230 | - Add events ([`d770fc8`](https://github.com/tailcallhq/rust-gh-workflow/commit/d770fc8bec19a8dd6e7b680b8d61819383b50498)) 231 | - Add warning in generated config ([`9054532`](https://github.com/tailcallhq/rust-gh-workflow/commit/90545329d44378175e1bbabe5595868720961dad)) 232 | - Update folder structure ([`5e43aa1`](https://github.com/tailcallhq/rust-gh-workflow/commit/5e43aa120b9f37227bc0d1e9d2c3c840a652319e)) 233 |
234 | 235 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "async-trait" 7 | version = "0.1.88" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 10 | dependencies = [ 11 | "proc-macro2", 12 | "quote", 13 | "syn 2.0.101", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.4.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 21 | 22 | [[package]] 23 | name = "console" 24 | version = "0.15.11" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" 27 | dependencies = [ 28 | "encode_unicode", 29 | "libc", 30 | "once_cell", 31 | "windows-sys", 32 | ] 33 | 34 | [[package]] 35 | name = "darling" 36 | version = "0.20.11" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 39 | dependencies = [ 40 | "darling_core", 41 | "darling_macro", 42 | ] 43 | 44 | [[package]] 45 | name = "darling_core" 46 | version = "0.20.11" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 49 | dependencies = [ 50 | "fnv", 51 | "ident_case", 52 | "proc-macro2", 53 | "quote", 54 | "strsim", 55 | "syn 2.0.101", 56 | ] 57 | 58 | [[package]] 59 | name = "darling_macro" 60 | version = "0.20.11" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 63 | dependencies = [ 64 | "darling_core", 65 | "quote", 66 | "syn 2.0.101", 67 | ] 68 | 69 | [[package]] 70 | name = "derive_more" 71 | version = "2.0.1" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" 74 | dependencies = [ 75 | "derive_more-impl", 76 | ] 77 | 78 | [[package]] 79 | name = "derive_more-impl" 80 | version = "2.0.1" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" 83 | dependencies = [ 84 | "proc-macro2", 85 | "quote", 86 | "syn 2.0.101", 87 | ] 88 | 89 | [[package]] 90 | name = "derive_setters" 91 | version = "0.1.7" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "d9c848e86c87e5cc305313041c5677d4d95d60baa71cf95e5f6ea2554bb629ff" 94 | dependencies = [ 95 | "darling", 96 | "proc-macro2", 97 | "quote", 98 | "syn 2.0.101", 99 | ] 100 | 101 | [[package]] 102 | name = "diff" 103 | version = "0.1.13" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 106 | 107 | [[package]] 108 | name = "encode_unicode" 109 | version = "1.0.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 112 | 113 | [[package]] 114 | name = "equivalent" 115 | version = "1.0.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 118 | 119 | [[package]] 120 | name = "fnv" 121 | version = "1.0.7" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 124 | 125 | [[package]] 126 | name = "gh-workflow" 127 | version = "0.6.0" 128 | dependencies = [ 129 | "async-trait", 130 | "derive_more", 131 | "derive_setters", 132 | "gh-workflow-macros", 133 | "indexmap", 134 | "insta", 135 | "merge", 136 | "pretty_assertions", 137 | "serde", 138 | "serde_json", 139 | "serde_yaml", 140 | "strum_macros", 141 | ] 142 | 143 | [[package]] 144 | name = "gh-workflow-macros" 145 | version = "0.6.0" 146 | dependencies = [ 147 | "heck", 148 | "quote", 149 | "syn 2.0.101", 150 | ] 151 | 152 | [[package]] 153 | name = "gh-workflow-tailcall" 154 | version = "0.5.2" 155 | dependencies = [ 156 | "derive_setters", 157 | "gh-workflow", 158 | "heck", 159 | "insta", 160 | "pretty_assertions", 161 | ] 162 | 163 | [[package]] 164 | name = "hashbrown" 165 | version = "0.15.3" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 168 | 169 | [[package]] 170 | name = "heck" 171 | version = "0.5.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 174 | 175 | [[package]] 176 | name = "ident_case" 177 | version = "1.0.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 180 | 181 | [[package]] 182 | name = "indexmap" 183 | version = "2.9.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 186 | dependencies = [ 187 | "equivalent", 188 | "hashbrown", 189 | "serde", 190 | ] 191 | 192 | [[package]] 193 | name = "insta" 194 | version = "1.43.1" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" 197 | dependencies = [ 198 | "console", 199 | "once_cell", 200 | "similar", 201 | ] 202 | 203 | [[package]] 204 | name = "itoa" 205 | version = "1.0.15" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 208 | 209 | [[package]] 210 | name = "libc" 211 | version = "0.2.172" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 214 | 215 | [[package]] 216 | name = "memchr" 217 | version = "2.7.4" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 220 | 221 | [[package]] 222 | name = "merge" 223 | version = "0.1.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9" 226 | dependencies = [ 227 | "merge_derive", 228 | "num-traits", 229 | ] 230 | 231 | [[package]] 232 | name = "merge_derive" 233 | version = "0.1.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07" 236 | dependencies = [ 237 | "proc-macro-error", 238 | "proc-macro2", 239 | "quote", 240 | "syn 1.0.109", 241 | ] 242 | 243 | [[package]] 244 | name = "num-traits" 245 | version = "0.2.19" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 248 | dependencies = [ 249 | "autocfg", 250 | ] 251 | 252 | [[package]] 253 | name = "once_cell" 254 | version = "1.21.3" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 257 | 258 | [[package]] 259 | name = "pretty_assertions" 260 | version = "1.4.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" 263 | dependencies = [ 264 | "diff", 265 | "yansi", 266 | ] 267 | 268 | [[package]] 269 | name = "proc-macro-error" 270 | version = "1.0.4" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 273 | dependencies = [ 274 | "proc-macro-error-attr", 275 | "proc-macro2", 276 | "quote", 277 | "syn 1.0.109", 278 | "version_check", 279 | ] 280 | 281 | [[package]] 282 | name = "proc-macro-error-attr" 283 | version = "1.0.4" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 286 | dependencies = [ 287 | "proc-macro2", 288 | "quote", 289 | "version_check", 290 | ] 291 | 292 | [[package]] 293 | name = "proc-macro2" 294 | version = "1.0.95" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 297 | dependencies = [ 298 | "unicode-ident", 299 | ] 300 | 301 | [[package]] 302 | name = "quote" 303 | version = "1.0.40" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 306 | dependencies = [ 307 | "proc-macro2", 308 | ] 309 | 310 | [[package]] 311 | name = "rustversion" 312 | version = "1.0.20" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 315 | 316 | [[package]] 317 | name = "ryu" 318 | version = "1.0.20" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 321 | 322 | [[package]] 323 | name = "serde" 324 | version = "1.0.219" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 327 | dependencies = [ 328 | "serde_derive", 329 | ] 330 | 331 | [[package]] 332 | name = "serde_derive" 333 | version = "1.0.219" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 336 | dependencies = [ 337 | "proc-macro2", 338 | "quote", 339 | "syn 2.0.101", 340 | ] 341 | 342 | [[package]] 343 | name = "serde_json" 344 | version = "1.0.140" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 347 | dependencies = [ 348 | "itoa", 349 | "memchr", 350 | "ryu", 351 | "serde", 352 | ] 353 | 354 | [[package]] 355 | name = "serde_yaml" 356 | version = "0.9.34+deprecated" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 359 | dependencies = [ 360 | "indexmap", 361 | "itoa", 362 | "ryu", 363 | "serde", 364 | "unsafe-libyaml", 365 | ] 366 | 367 | [[package]] 368 | name = "similar" 369 | version = "2.7.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" 372 | 373 | [[package]] 374 | name = "strsim" 375 | version = "0.11.1" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 378 | 379 | [[package]] 380 | name = "strum_macros" 381 | version = "0.27.1" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" 384 | dependencies = [ 385 | "heck", 386 | "proc-macro2", 387 | "quote", 388 | "rustversion", 389 | "syn 2.0.101", 390 | ] 391 | 392 | [[package]] 393 | name = "syn" 394 | version = "1.0.109" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 397 | dependencies = [ 398 | "proc-macro2", 399 | "quote", 400 | "unicode-ident", 401 | ] 402 | 403 | [[package]] 404 | name = "syn" 405 | version = "2.0.101" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 408 | dependencies = [ 409 | "proc-macro2", 410 | "quote", 411 | "unicode-ident", 412 | ] 413 | 414 | [[package]] 415 | name = "unicode-ident" 416 | version = "1.0.18" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 419 | 420 | [[package]] 421 | name = "unsafe-libyaml" 422 | version = "0.2.11" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 425 | 426 | [[package]] 427 | name = "version_check" 428 | version = "0.9.5" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 431 | 432 | [[package]] 433 | name = "windows-sys" 434 | version = "0.59.0" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 437 | dependencies = [ 438 | "windows-targets", 439 | ] 440 | 441 | [[package]] 442 | name = "windows-targets" 443 | version = "0.52.6" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 446 | dependencies = [ 447 | "windows_aarch64_gnullvm", 448 | "windows_aarch64_msvc", 449 | "windows_i686_gnu", 450 | "windows_i686_gnullvm", 451 | "windows_i686_msvc", 452 | "windows_x86_64_gnu", 453 | "windows_x86_64_gnullvm", 454 | "windows_x86_64_msvc", 455 | ] 456 | 457 | [[package]] 458 | name = "windows_aarch64_gnullvm" 459 | version = "0.52.6" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 462 | 463 | [[package]] 464 | name = "windows_aarch64_msvc" 465 | version = "0.52.6" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 468 | 469 | [[package]] 470 | name = "windows_i686_gnu" 471 | version = "0.52.6" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 474 | 475 | [[package]] 476 | name = "windows_i686_gnullvm" 477 | version = "0.52.6" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 480 | 481 | [[package]] 482 | name = "windows_i686_msvc" 483 | version = "0.52.6" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 486 | 487 | [[package]] 488 | name = "windows_x86_64_gnu" 489 | version = "0.52.6" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 492 | 493 | [[package]] 494 | name = "windows_x86_64_gnullvm" 495 | version = "0.52.6" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 498 | 499 | [[package]] 500 | name = "windows_x86_64_msvc" 501 | version = "0.52.6" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 504 | 505 | [[package]] 506 | name = "yansi" 507 | version = "1.0.1" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 510 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["crates/*"] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 2023 Tailcall 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🦀 Rust GitHub Actions Workflow 🚀 2 | 3 | [![Rust](https://img.shields.io/badge/Language-Rust-blue?style=flat-square)](https://www.rust-lang.org) 4 | [![Build Status](https://github.com/tailcallhq/rust-gh-workflow/actions/workflows/ci.yml/badge.svg?style=flat-square)](https://github.com/tailcallhq/rust-gh-workflow/actions) 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-green?style=flat-square)](https://opensource.org/licenses/MIT) 6 | [![Crates.io](https://img.shields.io/crates/v/gh-workflow?style=flat-square)](https://crates.io/crates/gh-workflow) 7 | [![Contributors](https://img.shields.io/github/contributors/tailcallhq/rust-gh-workflow?style=flat-square)](https://github.com/tailcallhq/rust-gh-workflow/graphs/contributors) 8 | [![GitHub forks](https://img.shields.io/github/forks/tailcallhq/rust-gh-workflow?style=flat-square)](https://github.com/tailcallhq/rust-gh-workflow/network/members) 9 | [![Stars](https://img.shields.io/github/stars/tailcallhq/rust-gh-workflow?style=flat-square)](https://github.com/tailcallhq/rust-gh-workflow/stargazers) 10 | [![Issues](https://img.shields.io/github/issues/tailcallhq/rust-gh-workflow?style=flat-square)](https://github.com/tailcallhq/rust-gh-workflow/issues) 11 | 12 | ## 🧑‍💻 What is Rust GitHub Workflows? 13 | 14 | **Rust GitHub Workflows** is a library that allows developers to write GitHub Actions in Rust. It empowers you to automate, manage, and improve your CI/CD pipelines in a type-safe manner. 15 | 16 | GitHub Actions is powerful, but writing workflows can sometimes feel repetitive, tricky and frustrating with no strict type-checking. That's where **Rust GitHub Workflows** steps in! 🦾 17 | 18 | - 🔥 **Rust-Powered**: Leverage the type-safety of Rust for writing workflows. 19 | - 📦 **Crate-friendly**: Build reusable workflows and publish them as crates. 20 | 21 | ## 📦 Installation 22 | 23 | To use **Rust GitHub Workflows** in your project, add it to your `Cargo.toml`: 24 | 25 | ```toml 26 | [build-dependencies] 27 | gh-workflow = "*" # Add the latest version 28 | ``` 29 | 30 | Then you can start creating GitHub Actions in your [tests/ci.rs](https://github.com/tailcallhq/rust-gh-workflow/blob/main/tests/ci.rs). 31 | 32 | ## 👷 Usage 33 | 34 | - Simply add a `tests/ci.rs` file to your project's tests directory. 35 | - Add the following code to generate the GitHub Actions workflow: 36 | 37 | ```rust 38 | use gh_workflows::*; 39 | 40 | #[test] 41 | fn main() { 42 | // Create a basic workflow 43 | let workflow = Workflow::setup_rust(); 44 | 45 | // Generate the ci.yml 46 | workflow.generate().unwrap(); 47 | } 48 | ``` 49 | 50 | To view a fully functional example, check out the [tests/ci.rs](https://github.com/tailcallhq/gh-workflow/blob/main/crates/gh-workflow-tailcall/tests/ci.rs) of this project. 51 | 52 | - Run `cargo test` to generate the GitHub Actions workflow. 53 | 54 | ## 💡 Why Rust? 55 | 56 | Rust provides the perfect combination of speed, safety, and flexibility, making it an ideal choice for writing GitHub Actions. With Rust, you get strong typing, memory safety, and the ability to reuse existing code, which can make your automation scripts more robust and maintainable. 57 | 58 | ## 📄 License 59 | 60 | This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details. 61 | 62 | ## 🙌 Contributors 63 | 64 | A big thank you to all the contributors who helped make this project a success! 🙏 65 | 66 | [![Contributors](https://contrib.rocks/image?repo=tailcallhq/rust-gh-workflow)](https://github.com/tailcallhq/rust-gh-workflow/graphs/contributors) 67 | 68 | ## 🌟 Show Your Support 69 | 70 | If you like this project, please consider giving it a ⭐ on [GitHub](https://github.com/tailcallhq/rust-gh-workflow) and share it with the community! 71 | 72 | ## 🔗 Inspiration 73 | 74 | This project was inspired by the following repositories: 75 | 76 | - [sbt/sbt-github-actions](https://github.com/sbt/sbt-github-actions) 77 | - [emmanuelnk/github-actions-workflow-ts](https://github.com/emmanuelnk/github-actions-workflow-ts) 78 | 79 | --- 80 | 81 | Happy automating with Rust! 🦀❤️ 82 | -------------------------------------------------------------------------------- /crates/gh-workflow-macros/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | -------------------------------------------------------------------------------- /crates/gh-workflow-macros/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 = "gh-workflow-macros" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "heck", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "heck" 16 | version = "0.5.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 19 | 20 | [[package]] 21 | name = "proc-macro2" 22 | version = "1.0.92" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 25 | dependencies = [ 26 | "unicode-ident", 27 | ] 28 | 29 | [[package]] 30 | name = "quote" 31 | version = "1.0.37" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 34 | dependencies = [ 35 | "proc-macro2", 36 | ] 37 | 38 | [[package]] 39 | name = "syn" 40 | version = "2.0.89" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" 43 | dependencies = [ 44 | "proc-macro2", 45 | "quote", 46 | "unicode-ident", 47 | ] 48 | 49 | [[package]] 50 | name = "unicode-ident" 51 | version = "1.0.14" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 54 | -------------------------------------------------------------------------------- /crates/gh-workflow-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gh-workflow-macros" 3 | version = "0.6.0" 4 | edition = "2021" 5 | 6 | description = "macros for gh-workflow" 7 | license = "Apache-2.0" 8 | documentation = "https://docs.rs/gh-workflow" 9 | homepage = "https://github.com/tailcallhq/gh-workflow" 10 | repository = "https://github.com/tailcallhq/gh-workflow" 11 | keywords = ["github", "actions", "workflow", "generator"] 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = "2.0" 18 | quote = "1.0" 19 | heck = "0.5.0" 20 | -------------------------------------------------------------------------------- /crates/gh-workflow-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use heck::ToSnakeCase; 2 | use proc_macro::TokenStream; 3 | use quote::quote; 4 | use syn::{parse_macro_input, Data, DeriveInput, Fields}; 5 | 6 | #[proc_macro_derive(Context)] 7 | pub fn derive_expr(input: TokenStream) -> TokenStream { 8 | let input = parse_macro_input!(input as DeriveInput); 9 | let struct_name = input.ident; 10 | let ctor_name = struct_name.to_string().to_snake_case(); 11 | let ctor_id = syn::Ident::new(&ctor_name, struct_name.span()); 12 | 13 | // Ensure it's a struct and get its fields 14 | let fields = if let Data::Struct(data_struct) = input.data { 15 | if let Fields::Named(fields) = data_struct.fields { 16 | fields 17 | } else { 18 | panic!("#[derive(Context)] only supports structs with named fields") 19 | } 20 | } else { 21 | panic!("#[derive(Context)] can only be used with structs"); 22 | }; 23 | 24 | // Generate methods for each field 25 | let methods = fields.named.iter().map(|field| { 26 | let field_name = &field.ident; 27 | let field_type = &field.ty; 28 | let field_name_str = field_name.as_ref().unwrap().to_string(); 29 | quote! { 30 | pub fn #field_name(&self) -> Context<#field_type> { 31 | self.select::<#field_type>(#field_name_str) 32 | } 33 | } 34 | }); 35 | 36 | // Generate the output code 37 | let expanded = quote! { 38 | impl Context<#struct_name> { 39 | #(#methods)* 40 | 41 | pub fn #ctor_id() -> Self { 42 | Context::::new().select(stringify!(#ctor_name)) 43 | } 44 | } 45 | }; 46 | 47 | // eprintln!("Generated code:\n{}", expanded); 48 | 49 | TokenStream::from(expanded) 50 | } 51 | -------------------------------------------------------------------------------- /crates/gh-workflow-tailcall/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.5.2](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.5.1...gh-workflow-tailcall-v0.5.2) - 2025-05-06 11 | 12 | ### Added 13 | 14 | - *(event)* add tags filter to Push struct for event handling ([#157](https://github.com/tailcallhq/gh-workflow/pull/157)) 15 | 16 | ## [0.5.1](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.5.0...gh-workflow-tailcall-v0.5.1) - 2025-01-29 17 | 18 | ### Other 19 | 20 | - updated the following local packages: gh-workflow 21 | 22 | ## [0.5.0](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.4.1...gh-workflow-tailcall-v0.5.0) - 2025-01-19 23 | 24 | ### Fixed 25 | 26 | - correct cargo install command in CI workflow and standard workflow 27 | 28 | ## [0.4.1](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.4.0...gh-workflow-tailcall-v0.4.1) - 2025-01-13 29 | 30 | ### Added 31 | 32 | - add caching support for Cargo toolchain in CI workflows 33 | 34 | ## [0.4.0](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.3.0...gh-workflow-tailcall-v0.4.0) - 2025-01-13 35 | 36 | ### Fixed 37 | 38 | - update autofix-ci/action version in workflow files 39 | 40 | ## [0.3.0](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.2.1...gh-workflow-tailcall-v0.3.0) - 2025-01-13 41 | 42 | ### Added 43 | 44 | - add setup steps to Workflow for job initialization 45 | 46 | ## [0.2.1](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.2.0...gh-workflow-tailcall-v0.2.1) - 2024-12-29 47 | 48 | ### Fixed 49 | 50 | - *(deps)* update rust crate serde_json to v1.0.134 (#111) 51 | 52 | ## [0.2.0](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.1.4...gh-workflow-tailcall-v0.2.0) - 2024-12-11 53 | 54 | ### Other 55 | 56 | - unset release_always 57 | 58 | ## [0.1.4](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.1.3...gh-workflow-tailcall-v0.1.4) - 2024-12-05 59 | 60 | ### Other 61 | 62 | - updated the following local packages: gh-workflow 63 | 64 | ## [0.1.3](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.1.2...gh-workflow-tailcall-v0.1.3) - 2024-12-02 65 | 66 | ### Other 67 | 68 | - updated the following local packages: gh-workflow 69 | 70 | ## [0.1.2](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.1.1...gh-workflow-tailcall-v0.1.2) - 2024-11-29 71 | 72 | ### Other 73 | 74 | - updated the following local packages: gh-workflow 75 | 76 | ## [0.1.1](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-tailcall-v0.1.0...gh-workflow-tailcall-v0.1.1) - 2024-11-29 77 | 78 | ### Other 79 | 80 | - use gh-workflow-tailcall in the test 81 | -------------------------------------------------------------------------------- /crates/gh-workflow-tailcall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gh-workflow-tailcall" 3 | version = "0.5.2" 4 | edition = "2021" 5 | 6 | 7 | description = "macros for gh-workflow" 8 | license = "Apache-2.0" 9 | documentation = "https://docs.rs/gh-workflow" 10 | homepage = "https://github.com/tailcallhq/gh-workflow" 11 | repository = "https://github.com/tailcallhq/gh-workflow" 12 | keywords = ["github", "actions", "workflow", "generator"] 13 | 14 | [dependencies] 15 | derive_setters = { version = "0.1.6" } 16 | gh-workflow = { path = "../gh-workflow", version = "0.6.0" } 17 | heck = "0.5.0" 18 | 19 | [dev-dependencies] 20 | insta = "1.40.0" 21 | pretty_assertions = "1.4.1" 22 | -------------------------------------------------------------------------------- /crates/gh-workflow-tailcall/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod standard; 2 | 3 | pub use gh_workflow::*; 4 | pub use standard::*; 5 | -------------------------------------------------------------------------------- /crates/gh-workflow-tailcall/src/standard.rs: -------------------------------------------------------------------------------- 1 | //! StandardWorkflow is designed to be used for most Rust projects that are 2 | //! built at Tailcall. Though gh-workflow makes it much easier to write 3 | //! workflows you still need to constantly keep referring to the Github 4 | //! documentation to write your own workflows. This module saves all that time 5 | //! by using feature flags to enable or disable features that you want in your 6 | //! workflow. Based on the features enabled or disabled a workflow is generated. 7 | 8 | use ctx::Context; 9 | use derive_setters::Setters; 10 | use generate::Generate; 11 | use gh_workflow::error::Result; 12 | use gh_workflow::{Workflow as GHWorkflow, *}; 13 | use heck::ToTitleCase; 14 | use release_plz::{Command, Release}; 15 | use toolchain::Toolchain; 16 | 17 | /// Defines the test runner to use for running tests 18 | #[derive(Debug, Clone, Default)] 19 | pub enum TestRunner { 20 | /// Uses the default cargo test runner 21 | Cargo, 22 | 23 | /// Uses cargo-nextest for running tests 24 | #[default] 25 | Nextest, 26 | } 27 | 28 | #[derive(Debug, Clone, Setters)] 29 | pub struct StandardWorkflow { 30 | /// When enabled, a release job is added to the workflow. 31 | /// *IMPORTANT:* Ensure `secrets.CARGO_REGISTRY_TOKEN` is set for your 32 | /// github action. 33 | pub auto_release: bool, 34 | 35 | /// Name of the workflow. 36 | pub name: String, 37 | 38 | /// When enabled, a benchmark job is added to the workflow. 39 | pub benchmarks: bool, 40 | 41 | /// Auto-fixes the code after 42 | pub auto_fix: bool, 43 | 44 | /// Steps to be executed before the checkout step 45 | pub setup: Vec>, 46 | 47 | /// The test runner to use for running tests 48 | pub test_runner: TestRunner, 49 | } 50 | 51 | impl Default for StandardWorkflow { 52 | fn default() -> Self { 53 | Self { 54 | auto_release: false, 55 | name: "ci".into(), 56 | benchmarks: false, 57 | auto_fix: false, 58 | setup: Vec::new(), 59 | test_runner: TestRunner::default(), 60 | } 61 | } 62 | } 63 | 64 | impl StandardWorkflow { 65 | /// Initialize a job with common configuration including: 66 | /// - Permissions 67 | /// - Setup steps 68 | /// - Checkout step 69 | /// 70 | /// This reduces duplication across different job types. 71 | fn init_job(&self, name: impl ToString) -> Job { 72 | let mut job = Job::new(name).permissions(Permissions::default().contents(Level::Read)); 73 | 74 | // Add setup steps in reverse order to maintain the correct sequence 75 | for step in self.setup.iter().rev() { 76 | job = job.add_step(step.clone()); 77 | } 78 | 79 | job.add_step(Step::checkout()) 80 | } 81 | 82 | /// Add a setup step to be executed before the checkout step. 83 | /// 84 | /// # Example 85 | /// ```ignore 86 | /// use gh_workflow_tailcall::*; 87 | /// let workflow = StandardWorkflow::default() 88 | /// .add_setup(Step::run("git config --global core.autocrlf false") 89 | /// .name("Configure git")); 90 | /// ``` 91 | pub fn add_setup>>(mut self, step: S) -> Self { 92 | self.setup.push(step.into()); 93 | self 94 | } 95 | } 96 | 97 | impl StandardWorkflow { 98 | /// Generates and tests the workflow file. 99 | pub fn generate(self) -> Result<()> { 100 | self.to_ci_workflow().generate()?; 101 | Generate::new(self.to_autofix_workflow()) 102 | .name("autofix.yml") 103 | .generate()?; 104 | Ok(()) 105 | } 106 | 107 | /// Converts the workflow into a Github workflow. 108 | fn to_autofix_workflow(&self) -> GHWorkflow { 109 | // NOTE: The workflow name needs to by `autofix.ci` 110 | GHWorkflow::new("autofix.ci") 111 | .add_env(self.workflow_flags()) 112 | .on(self.workflow_event()) 113 | .add_job("lint", self.lint_job(true)) 114 | } 115 | 116 | /// Converts the workflow into a Github workflow. 117 | pub fn to_ci_workflow(&self) -> GHWorkflow { 118 | GHWorkflow::new(self.name.clone()) 119 | .add_env(self.workflow_flags()) 120 | .on(self.workflow_event()) 121 | .add_job("build", self.test_job()) 122 | .add_job("lint", self.lint_job(false)) 123 | .add_job_when( 124 | self.auto_release, 125 | "release", 126 | self.release_job(Command::Release), 127 | ) 128 | .add_job_when( 129 | self.auto_release, 130 | "release-pr", 131 | self.release_job(Command::ReleasePR), 132 | ) 133 | } 134 | 135 | fn release_job(&self, cmd: Command) -> Job { 136 | self.init_job(cmd.to_string().to_title_case()) 137 | .concurrency( 138 | Concurrency::new(Expression::new("release-${{github.ref}}")) 139 | .cancel_in_progress(false), 140 | ) 141 | .cond(self.workflow_cond()) 142 | .add_needs(self.test_job()) 143 | .add_needs(self.lint_job(false)) 144 | .add_env(Env::github()) 145 | .add_env(Env::new( 146 | "CARGO_REGISTRY_TOKEN", 147 | "${{ secrets.CARGO_REGISTRY_TOKEN }}", 148 | )) 149 | .permissions(self.write_permissions()) 150 | .add_step(Release::default().command(cmd)) 151 | } 152 | 153 | fn lint_job(&self, auto_fix: bool) -> Job { 154 | let job = self.init_job(if auto_fix { "Lint Fix" } else { "Lint" }); 155 | 156 | let job = if auto_fix { 157 | job.concurrency( 158 | Concurrency::new(Expression::new("autofix-${{github.ref}}")) 159 | .cancel_in_progress(false), 160 | ) 161 | } else { 162 | job 163 | }; 164 | 165 | job.add_step( 166 | Toolchain::default() 167 | .add_nightly() 168 | .add_clippy() 169 | .add_fmt() 170 | .cache(true) 171 | .cache_directories(vec![ 172 | "~/.cargo/registry".into(), 173 | "~/.cargo/git".into(), 174 | "target".into(), 175 | ]), 176 | ) 177 | .add_step( 178 | Cargo::new("fmt") 179 | .name("Cargo Fmt") 180 | .nightly() 181 | .add_args("--all") 182 | .add_args_when(!auto_fix, "--check"), 183 | ) 184 | .add_step( 185 | Cargo::new("clippy") 186 | .name("Cargo Clippy") 187 | .nightly() 188 | .add_args_when(auto_fix, "--fix") 189 | .add_args_when(auto_fix, "--allow-dirty") 190 | .add_args("--all-features --workspace -- -D warnings"), 191 | ) 192 | .add_step_when( 193 | auto_fix, 194 | Step::uses( 195 | "autofix-ci", 196 | "action", 197 | "551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef", 198 | ), 199 | ) 200 | } 201 | 202 | /// Creates the "Build and Test" job for the workflow. 203 | fn test_job(&self) -> Job { 204 | self.init_job("Build and Test") 205 | .add_step(Toolchain::default().add_stable()) 206 | .add_step_when( 207 | matches!(self.test_runner, TestRunner::Nextest), 208 | Cargo::new("install") 209 | .args("cargo-nextest --locked") 210 | .name("Install nextest"), 211 | ) 212 | .add_step(match self.test_runner { 213 | TestRunner::Cargo => Cargo::new("test") 214 | .args("--all-features --workspace") 215 | .name("Cargo Test"), 216 | TestRunner::Nextest => Cargo::new("nextest") 217 | .args("run --all-features --workspace") 218 | .name("Cargo Nextest"), 219 | }) 220 | .add_step_when( 221 | self.benchmarks, 222 | Cargo::new("bench").args("--workspace").name("Cargo Bench"), 223 | ) 224 | } 225 | 226 | fn write_permissions(&self) -> Permissions { 227 | Permissions::default() 228 | .pull_requests(Level::Write) 229 | .packages(Level::Write) 230 | .contents(Level::Write) 231 | } 232 | 233 | fn workflow_cond(&self) -> Context { 234 | let is_main = Context::github().ref_().eq("refs/heads/main".into()); 235 | let is_push = Context::github().event_name().eq("push".into()); 236 | 237 | is_main.and(is_push) 238 | } 239 | 240 | fn workflow_event(&self) -> Event { 241 | Event::default() 242 | .push(Push::default().add_branch("main").add_tag("v*")) 243 | .pull_request( 244 | PullRequest::default() 245 | .add_type(PullRequestType::Opened) 246 | .add_type(PullRequestType::Synchronize) 247 | .add_type(PullRequestType::Reopened) 248 | .add_branch("main"), 249 | ) 250 | } 251 | 252 | fn workflow_flags(&self) -> RustFlags { 253 | RustFlags::deny("warnings") 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /crates/gh-workflow-tailcall/tests/ci.rs: -------------------------------------------------------------------------------- 1 | use gh_workflow_tailcall::StandardWorkflow; 2 | 3 | #[test] 4 | fn generate() { 5 | StandardWorkflow::default() 6 | .auto_release(true) 7 | .auto_fix(true) 8 | .generate() 9 | .unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /crates/gh-workflow/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.6.0](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.10...gh-workflow-v0.6.0) - 2025-05-06 11 | 12 | ### Added 13 | 14 | - *(event)* add tags filter to Push struct for event handling ([#157](https://github.com/tailcallhq/gh-workflow/pull/157)) 15 | 16 | ## [0.5.10](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.9...gh-workflow-v0.5.10) - 2025-01-29 17 | 18 | ### Added 19 | 20 | - add get method to Jobs struct for retrieving jobs by key (#134) 21 | 22 | ## [0.5.9](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.8...gh-workflow-v0.5.9) - 2025-01-19 23 | 24 | ### Fixed 25 | 26 | - correct cargo install command in CI workflow and standard workflow 27 | 28 | ## [0.5.8](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.7...gh-workflow-v0.5.8) - 2025-01-13 29 | 30 | ### Added 31 | 32 | - add caching support for Cargo toolchain in CI workflows 33 | 34 | ## [0.5.7](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.6...gh-workflow-v0.5.7) - 2024-12-29 35 | 36 | ### Fixed 37 | 38 | - *(deps)* update rust crate serde_json to v1.0.134 (#111) 39 | 40 | ## [0.5.6](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.5...gh-workflow-v0.5.6) - 2024-12-11 41 | 42 | ### Other 43 | 44 | - unset release_always 45 | 46 | ## [0.5.5](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.4...gh-workflow-v0.5.5) - 2024-12-05 47 | 48 | ### Other 49 | 50 | - add readme 51 | 52 | ## [0.5.4](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.3...gh-workflow-v0.5.4) - 2024-12-02 53 | 54 | ### Fixed 55 | 56 | - jobs dependency id generator ([#94](https://github.com/tailcallhq/gh-workflow/pull/94)) 57 | 58 | ## [0.5.3](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.2...gh-workflow-v0.5.3) - 2024-11-29 59 | 60 | ### Fixed 61 | 62 | - drop `v` prefix from `uses` API 63 | 64 | ## [0.5.2](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.1...gh-workflow-v0.5.2) - 2024-11-29 65 | 66 | ### Other 67 | 68 | - use gh-workflow-tailcall in the test 69 | 70 | ## [0.5.1](https://github.com/tailcallhq/gh-workflow/compare/gh-workflow-v0.5.0...gh-workflow-v0.5.1) - 2024-11-29 71 | 72 | ### Other 73 | 74 | - *(gh-workflow-macros)* release v0.5.0 ([#86](https://github.com/tailcallhq/gh-workflow/pull/86)) 75 | 76 | ## [0.5.0](https://github.com/tailcallhq/gh-workflow/compare/v0.4.1...v0.5.0) - 2024-11-28 77 | 78 | ### Other 79 | 80 | - reset release flags 81 | -------------------------------------------------------------------------------- /crates/gh-workflow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gh-workflow" 3 | version = "0.6.0" 4 | edition = "2021" 5 | 6 | description = "A type-safe GitHub Actions workflow generator" 7 | license = "Apache-2.0" 8 | documentation = "https://docs.rs/gh-workflow" 9 | homepage = "https://github.com/tailcallhq/gh-workflow" 10 | repository = "https://github.com/tailcallhq/gh-workflow" 11 | keywords = ["github", "actions", "workflow", "generator"] 12 | readme = "../../README.md" 13 | 14 | [dependencies] 15 | async-trait = "0.1.83" 16 | derive_more = { version = "2.0.0", features = ["from", "deref", "deref_mut"] } 17 | derive_setters = "0.1.6" 18 | indexmap = { version = "2.6.0", features = ["serde"] } 19 | merge = "0.1.0" 20 | serde = { version = "1.0.210", features = ["derive"] } 21 | serde_json = { version = "1.0.128" } 22 | serde_yaml = "0.9.34" 23 | strum_macros = "0.27.0" 24 | gh-workflow-macros = { path = "../gh-workflow-macros", version = "0.6.0" } 25 | 26 | [dev-dependencies] 27 | insta = "1.40.0" 28 | pretty_assertions = "1.4.1" 29 | -------------------------------------------------------------------------------- /crates/gh-workflow/src/cargo.rs: -------------------------------------------------------------------------------- 1 | use derive_setters::Setters; 2 | 3 | use crate::toolchain::Version; 4 | use crate::{Run, Step}; 5 | 6 | #[derive(Clone, Setters)] 7 | #[setters(strip_option, into)] 8 | pub struct Cargo { 9 | /// The command to be executed for eg: fmt, clippy, build, test, etc. 10 | pub command: String, 11 | 12 | /// The unique identifier of the Step. 13 | pub id: Option, 14 | 15 | /// Name of the Step 16 | pub name: Option, 17 | 18 | /// Toolchain to be used for example `+nightly`. 19 | pub toolchain: Option, 20 | 21 | /// Arguments to be passed to the cargo command. 22 | #[setters(skip)] 23 | pub args: Vec, 24 | } 25 | 26 | impl Cargo { 27 | /// Creates a new `Cargo` instance with the specified command. 28 | pub fn new(cmd: T) -> Cargo { 29 | Cargo { 30 | command: cmd.to_string(), 31 | id: Default::default(), 32 | name: Default::default(), 33 | toolchain: Default::default(), 34 | args: Default::default(), 35 | } 36 | } 37 | 38 | /// Sets the toolchain to nightly. 39 | pub fn nightly(mut self) -> Self { 40 | self.toolchain = Some(Version::Nightly); 41 | self 42 | } 43 | 44 | /// Sets the arguments for the cargo command. If arguments are already set, 45 | /// it will be overwritten. 46 | pub fn args(mut self, args: T) -> Self { 47 | self.args = vec![args.to_string()]; 48 | self 49 | } 50 | 51 | /// Adds additional arguments to the cargo command. 52 | pub fn add_args(mut self, args: T) -> Self { 53 | self.args.extend( 54 | args.to_string() 55 | .split_whitespace() 56 | .map(|s| s.to_string()) 57 | .collect::>(), 58 | ); 59 | self 60 | } 61 | 62 | /// Adds the arguments to the cargo command when a condition is met. 63 | pub fn add_args_when(self, when: bool, args: T) -> Self { 64 | if when { 65 | self.add_args(args) 66 | } else { 67 | self 68 | } 69 | } 70 | } 71 | 72 | impl From for Step { 73 | fn from(value: Cargo) -> Self { 74 | let mut command = vec!["cargo".to_string()]; 75 | 76 | if let Some(toolchain) = value.toolchain { 77 | command.push(format!("+{toolchain}")); 78 | } 79 | 80 | command.push(value.command); 81 | 82 | // Extend the command with non-empty arguments 83 | command.extend( 84 | value 85 | .args 86 | .into_iter() 87 | .map(|arg| arg.trim().to_string()) 88 | .filter(|arg| !arg.is_empty()), 89 | ); 90 | 91 | let mut step = Step::run(command.join(" ")); 92 | 93 | if let Some(id) = value.id { 94 | step = step.id(id); 95 | } 96 | 97 | if let Some(name) = value.name { 98 | step = step.name(name); 99 | } 100 | 101 | step 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /crates/gh-workflow/src/comment.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------- 2 | # ------------------------------- WARNING --------------------------- 3 | # ------------------------------------------------------------------- 4 | # 5 | # This file was automatically generated by gh-workflows using the 6 | # gh-workflow-gen bin. You should add and commit this file to your 7 | # git repository. **DO NOT EDIT THIS FILE BY HAND!** Any manual changes 8 | # will be lost if the file is regenerated. 9 | # 10 | # To make modifications, update your `build.rs` configuration to adjust 11 | # the workflow description as needed, then regenerate this file to apply 12 | # those changes. 13 | # 14 | # ------------------------------------------------------------------- 15 | # ----------------------------- END WARNING ------------------------- 16 | # ------------------------------------------------------------------- 17 | -------------------------------------------------------------------------------- /crates/gh-workflow/src/ctx.rs: -------------------------------------------------------------------------------- 1 | //! A type-safe implementation of workflow context: 2 | 3 | use std::fmt; 4 | use std::marker::PhantomData; 5 | use std::rc::Rc; 6 | 7 | use gh_workflow_macros::Context; 8 | 9 | use crate::Expression; 10 | 11 | #[derive(Clone)] 12 | pub struct Context { 13 | marker: PhantomData, 14 | step: Step, 15 | } 16 | 17 | #[derive(Default, Clone)] 18 | enum Step { 19 | #[default] 20 | Root, 21 | Select { 22 | name: Rc, 23 | object: Box, 24 | }, 25 | Eq { 26 | left: Box, 27 | right: Box, 28 | }, 29 | And { 30 | left: Box, 31 | right: Box, 32 | }, 33 | Or { 34 | left: Box, 35 | right: Box, 36 | }, 37 | Literal(String), 38 | Concat { 39 | left: Box, 40 | right: Box, 41 | }, 42 | } 43 | 44 | impl Context { 45 | fn new() -> Self { 46 | Context { marker: PhantomData, step: Step::Root } 47 | } 48 | 49 | fn select(&self, path: impl Into) -> Context { 50 | Context { 51 | marker: PhantomData, 52 | step: Step::Select { 53 | name: Rc::new(path.into()), 54 | object: Box::new(self.step.clone()), 55 | }, 56 | } 57 | } 58 | 59 | pub fn eq(&self, other: Context) -> Context { 60 | Context { 61 | marker: Default::default(), 62 | step: Step::Eq { 63 | left: Box::new(self.step.clone()), 64 | right: Box::new(other.step.clone()), 65 | }, 66 | } 67 | } 68 | 69 | pub fn and(&self, other: Context) -> Context { 70 | Context { 71 | marker: Default::default(), 72 | step: Step::And { 73 | left: Box::new(self.step.clone()), 74 | right: Box::new(other.step.clone()), 75 | }, 76 | } 77 | } 78 | 79 | pub fn or(&self, other: Context) -> Context { 80 | Context { 81 | marker: Default::default(), 82 | step: Step::Or { 83 | left: Box::new(self.step.clone()), 84 | right: Box::new(other.step.clone()), 85 | }, 86 | } 87 | } 88 | } 89 | 90 | impl Context { 91 | pub fn concat(&self, other: Context) -> Context { 92 | Context { 93 | marker: Default::default(), 94 | step: Step::Concat { 95 | left: Box::new(self.step.clone()), 96 | right: Box::new(other.step), 97 | }, 98 | } 99 | } 100 | } 101 | 102 | #[allow(unused)] 103 | #[derive(Context)] 104 | pub struct Github { 105 | /// The name of the action currently running, or the id of a step. 106 | action: String, 107 | /// The path where an action is located. This property is only supported in 108 | /// composite actions. 109 | action_path: String, 110 | /// For a step executing an action, this is the ref of the action being 111 | /// executed. 112 | action_ref: String, 113 | /// For a step executing an action, this is the owner and repository name of 114 | /// the action. 115 | action_repository: String, 116 | /// For a composite action, the current result of the composite action. 117 | action_status: String, 118 | /// The username of the user that triggered the initial workflow run. 119 | actor: String, 120 | /// The account ID of the person or app that triggered the initial workflow 121 | /// run. 122 | actor_id: String, 123 | /// The URL of the GitHub REST API. 124 | api_url: String, 125 | /// The base_ref or target branch of the pull request in a workflow run. 126 | base_ref: String, 127 | /// Path on the runner to the file that sets environment variables from 128 | /// workflow commands. 129 | env: String, 130 | /// The full event webhook payload. 131 | event: serde_json::Value, 132 | /// The name of the event that triggered the workflow run. 133 | event_name: String, 134 | /// The path to the file on the runner that contains the full event webhook 135 | /// payload. 136 | event_path: String, 137 | /// The URL of the GitHub GraphQL API. 138 | graphql_url: String, 139 | /// The head_ref or source branch of the pull request in a workflow run. 140 | head_ref: String, 141 | /// The job id of the current job. 142 | job: String, 143 | /// The path of the repository. 144 | path: String, 145 | /// The short ref name of the branch or tag that triggered the workflow run. 146 | ref_name: String, 147 | /// true if branch protections are configured for the ref that triggered the 148 | /// workflow run. 149 | ref_protected: bool, 150 | /// The type of ref that triggered the workflow run. Valid values are branch 151 | /// or tag. 152 | ref_type: String, 153 | /// The owner and repository name. 154 | repository: String, 155 | /// The ID of the repository. 156 | repository_id: String, 157 | /// The repository owner's username. 158 | repository_owner: String, 159 | /// The repository owner's account ID. 160 | repository_owner_id: String, 161 | /// The Git URL to the repository. 162 | repository_url: String, 163 | /// The number of days that workflow run logs and artifacts are kept. 164 | retention_days: String, 165 | /// A unique number for each workflow run within a repository. 166 | run_id: String, 167 | /// A unique number for each run of a particular workflow in a repository. 168 | run_number: String, 169 | /// A unique number for each attempt of a particular workflow run in a 170 | /// repository. 171 | run_attempt: String, 172 | /// The source of a secret used in a workflow. 173 | secret_source: String, 174 | /// The URL of the GitHub server. 175 | server_url: String, 176 | /// The commit SHA that triggered the workflow. 177 | sha: String, 178 | /// A token to authenticate on behalf of the GitHub App installed on your 179 | /// repository. 180 | token: String, 181 | /// The username of the user that initiated the workflow run. 182 | triggering_actor: String, 183 | /// The name of the workflow. 184 | workflow: String, 185 | /// The ref path to the workflow. 186 | workflow_ref: String, 187 | /// The commit SHA for the workflow file. 188 | workflow_sha: String, 189 | /// The default working directory on the runner for steps. 190 | workspace: String, 191 | } 192 | 193 | impl Context { 194 | pub fn ref_(&self) -> Context { 195 | self.select("ref") 196 | } 197 | } 198 | 199 | impl fmt::Display for Step { 200 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 201 | match self { 202 | Step::Root => write!(f, ""), 203 | Step::Select { name, object } => { 204 | if matches!(**object, Step::Root) { 205 | write!(f, "{name}") 206 | } else { 207 | write!(f, "{object}.{name}") 208 | } 209 | } 210 | Step::Eq { left, right } => { 211 | write!(f, "{left} == {right}") 212 | } 213 | Step::And { left, right } => { 214 | write!(f, "{left} && {right}") 215 | } 216 | Step::Or { left, right } => { 217 | write!(f, "{left} || {right}") 218 | } 219 | Step::Literal(value) => { 220 | write!(f, "'{value}'") 221 | } 222 | Step::Concat { left, right } => { 223 | write!(f, "{left}{right}") 224 | } 225 | } 226 | } 227 | } 228 | 229 | impl fmt::Display for Context { 230 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 231 | write!(f, "${{{{ {} }}}}", self.step.to_string().replace('"', "")) 232 | } 233 | } 234 | 235 | impl From> for Expression { 236 | fn from(value: Context) -> Self { 237 | Expression::new(value.to_string()) 238 | } 239 | } 240 | 241 | impl> From for Context { 242 | fn from(value: T) -> Self { 243 | Context { 244 | marker: Default::default(), 245 | step: Step::Literal(value.into()), 246 | } 247 | } 248 | } 249 | 250 | #[allow(unused)] 251 | #[derive(Context)] 252 | /// The job context contains information about the currently running job. 253 | pub struct Job { 254 | /// A unique number for each container in a job. This property is only 255 | /// available if the job uses a container. 256 | container: Container, 257 | 258 | /// The services configured for a job. This property is only available if 259 | /// the job uses service containers. 260 | services: Services, 261 | 262 | /// The status of the current job. 263 | status: JobStatus, 264 | } 265 | 266 | /// The status of a job execution 267 | #[derive(Clone)] 268 | pub enum JobStatus { 269 | /// The job completed successfully 270 | Success, 271 | /// The job failed 272 | Failure, 273 | /// The job was cancelled 274 | Cancelled, 275 | } 276 | 277 | #[derive(Context)] 278 | #[allow(unused)] 279 | /// Container information for a job. This is only available if the job runs in a 280 | /// container. 281 | pub struct Container { 282 | /// The ID of the container 283 | id: String, 284 | /// The container network 285 | network: String, 286 | } 287 | 288 | #[derive(Context)] 289 | 290 | /// Services configured for a job. This is only available if the job uses 291 | /// service containers. 292 | pub struct Services {} 293 | 294 | #[cfg(test)] 295 | mod test { 296 | use pretty_assertions::assert_eq; 297 | 298 | use super::*; 299 | 300 | #[test] 301 | fn test_expr() { 302 | let github = Context::github(); // Expr 303 | 304 | assert_eq!(github.to_string(), "${{ github }}"); 305 | 306 | let action = github.action(); // Expr 307 | assert_eq!(action.to_string(), "${{ github.action }}"); 308 | 309 | let action_path = github.action_path(); // Expr 310 | assert_eq!(action_path.to_string(), "${{ github.action_path }}"); 311 | } 312 | 313 | #[test] 314 | fn test_expr_eq() { 315 | let github = Context::github(); 316 | let action = github.action(); 317 | let action_path = github.action_path(); 318 | 319 | let expr = action.eq(action_path); 320 | 321 | assert_eq!( 322 | expr.to_string(), 323 | "${{ github.action == github.action_path }}" 324 | ); 325 | } 326 | 327 | #[test] 328 | fn test_expr_and() { 329 | let push = Context::github().event_name().eq("push".into()); 330 | let main = Context::github().ref_().eq("ref/heads/main".into()); 331 | let expr = push.and(main); 332 | 333 | assert_eq!( 334 | expr.to_string(), 335 | "${{ github.event_name == 'push' && github.ref == 'ref/heads/main' }}" 336 | ) 337 | } 338 | 339 | #[test] 340 | fn test_expr_or() { 341 | let github = Context::github(); 342 | let action = github.action(); 343 | let action_path = github.action_path(); 344 | let action_ref = github.action_ref(); 345 | 346 | let expr = action.eq(action_path).or(action.eq(action_ref)); 347 | 348 | assert_eq!( 349 | expr.to_string(), 350 | "${{ github.action == github.action_path || github.action == github.action_ref }}" 351 | ); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /crates/gh-workflow/src/error.rs: -------------------------------------------------------------------------------- 1 | //! This module defines the `Error` enum and the `Result` type alias used 2 | //! throughout the crate. 3 | 4 | #[derive(Debug, derive_more::From)] 5 | pub enum Error { 6 | IO(std::io::Error), 7 | Yaml(serde_yaml::Error), 8 | GitHubWorkflowMismatch, 9 | JobIdAlreadyExists(String), 10 | UTF8(std::string::FromUtf8Error), 11 | OutdatedWorkflow, 12 | MissingWorkflowFile(std::path::PathBuf), 13 | } 14 | 15 | pub type Result = std::result::Result; 16 | -------------------------------------------------------------------------------- /crates/gh-workflow/src/event.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_update)] 2 | 3 | use std::collections::HashMap; 4 | 5 | use derive_setters::Setters; 6 | use merge::Merge; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use crate::is_default; 10 | 11 | /// Represents all possible webhook events that can trigger a workflow 12 | /// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows 13 | #[derive(Default, Debug, Clone, Deserialize, Serialize, Merge, Setters, PartialEq, Eq)] 14 | #[setters(strip_option, into)] 15 | pub struct Event { 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub branch_protection_rule: Option, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub check_run: Option, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | pub check_suite: Option, 22 | #[serde(skip_serializing_if = "Option::is_none")] 23 | pub create: Option, 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub delete: Option, 26 | #[serde(skip_serializing_if = "Option::is_none")] 27 | pub deployment: Option, 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub deployment_status: Option, 30 | #[serde(skip_serializing_if = "Option::is_none")] 31 | pub discussion: Option, 32 | #[serde(skip_serializing_if = "Option::is_none")] 33 | pub discussion_comment: Option, 34 | #[serde(skip_serializing_if = "Option::is_none")] 35 | pub fork: Option, 36 | #[serde(skip_serializing_if = "Option::is_none")] 37 | pub gollum: Option, 38 | #[serde(skip_serializing_if = "Option::is_none")] 39 | pub issue_comment: Option, 40 | #[serde(skip_serializing_if = "Option::is_none")] 41 | pub issues: Option, 42 | #[serde(skip_serializing_if = "Option::is_none")] 43 | pub label: Option { 350 | /// The value of the step. 351 | value: StepValue, 352 | #[serde(skip)] 353 | marker: std::marker::PhantomData, 354 | } 355 | 356 | impl From> for StepValue { 357 | /// Converts a `Step` into a `StepValue`. 358 | fn from(step: Step) -> Self { 359 | step.value 360 | } 361 | } 362 | 363 | impl From> for StepValue { 364 | /// Converts a `Step` into a `StepValue`. 365 | fn from(step: Step) -> Self { 366 | step.value 367 | } 368 | } 369 | 370 | /// Represents a step that uses an action. 371 | #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] 372 | pub struct Use; 373 | 374 | /// Represents a step that runs a command. 375 | #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] 376 | pub struct Run; 377 | 378 | /// A trait to convert `Step` and `Step` to `StepValue`. 379 | pub trait StepType: Sized + private::Sealed { 380 | /// Converts a step to its value representation. 381 | fn to_value(s: Step) -> StepValue; 382 | } 383 | 384 | impl private::Sealed for Run {} 385 | impl private::Sealed for Use {} 386 | 387 | impl StepType for Run { 388 | /// Converts a `Step` to `StepValue`. 389 | fn to_value(s: Step) -> StepValue { 390 | s.into() 391 | } 392 | } 393 | 394 | impl StepType for Use { 395 | /// Converts a `Step` to `StepValue`. 396 | fn to_value(s: Step) -> StepValue { 397 | s.into() 398 | } 399 | } 400 | 401 | /// Represents environment variables in the workflow. 402 | #[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] 403 | #[serde(transparent)] 404 | pub struct Env(IndexMap); 405 | 406 | impl From> for Env { 407 | /// Converts an `IndexMap` into an `Env`. 408 | fn from(value: IndexMap) -> Self { 409 | Env(value) 410 | } 411 | } 412 | 413 | impl Env { 414 | /// Sets the `GITHUB_TOKEN` environment variable. 415 | pub fn github() -> Self { 416 | let mut map = IndexMap::new(); 417 | map.insert( 418 | "GITHUB_TOKEN".to_string(), 419 | Value::from("${{ secrets.GITHUB_TOKEN }}"), 420 | ); 421 | Env(map) 422 | } 423 | 424 | /// Creates a new `Env` with a specified key-value pair. 425 | pub fn new>(key: K, value: V) -> Self { 426 | Env::default().add(key, value) 427 | } 428 | 429 | /// Adds an environment variable to the `Env`. 430 | pub fn add>(mut self, key: T1, value: T2) -> Self { 431 | self.0.insert(key.to_string(), value.into()); 432 | self 433 | } 434 | } 435 | 436 | /// Represents input parameters for a step. 437 | #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] 438 | #[serde(transparent)] 439 | pub struct Input(#[serde(skip_serializing_if = "IndexMap::is_empty")] IndexMap); 440 | 441 | impl From> for Input { 442 | /// Converts an `IndexMap` into an `Input`. 443 | fn from(value: IndexMap) -> Self { 444 | Input(value) 445 | } 446 | } 447 | 448 | impl Merge for Input { 449 | /// Merges another `Input` into this one. 450 | fn merge(&mut self, other: Self) { 451 | self.0.extend(other.0); 452 | } 453 | } 454 | 455 | impl Input { 456 | /// Adds a new input parameter to the `Input`. 457 | pub fn add>(mut self, key: S, value: V) -> Self { 458 | self.0.insert(key.to_string(), value.into()); 459 | self 460 | } 461 | 462 | /// Checks if the `Input` is empty. 463 | pub fn is_empty(&self) -> bool { 464 | self.0.is_empty() 465 | } 466 | } 467 | 468 | /// Represents a step value in the workflow. 469 | #[allow(clippy::duplicated_attributes)] 470 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 471 | #[serde(rename_all = "kebab-case")] 472 | #[setters( 473 | strip_option, 474 | into, 475 | generate_delegates(ty = "Step", field = "value"), 476 | generate_delegates(ty = "Step", field = "value") 477 | )] 478 | pub struct StepValue { 479 | /// The ID of the step. 480 | #[serde(skip_serializing_if = "Option::is_none")] 481 | pub id: Option, 482 | 483 | /// The name of the step. 484 | #[serde(skip_serializing_if = "Option::is_none")] 485 | pub name: Option, 486 | 487 | /// The condition under which the step runs. 488 | #[serde(skip_serializing_if = "Option::is_none", rename = "if")] 489 | pub if_condition: Option, 490 | 491 | /// The action to use in the step. 492 | #[serde(skip_serializing_if = "Option::is_none")] 493 | #[setters(skip)] 494 | pub uses: Option, 495 | 496 | /// Input parameters for the step. 497 | #[serde(skip_serializing_if = "Option::is_none")] 498 | pub with: Option, 499 | 500 | /// The command to run in the step. 501 | #[serde(skip_serializing_if = "Option::is_none")] 502 | #[setters(skip)] 503 | pub run: Option, 504 | 505 | /// Environment variables for the step. 506 | #[serde(skip_serializing_if = "Option::is_none")] 507 | pub env: Option, 508 | 509 | /// The timeout for the step in minutes. 510 | #[serde(skip_serializing_if = "Option::is_none")] 511 | pub timeout_minutes: Option, 512 | 513 | /// Whether to continue on error. 514 | #[serde(skip_serializing_if = "Option::is_none")] 515 | pub continue_on_error: Option, 516 | 517 | /// The working directory for the step. 518 | #[serde(skip_serializing_if = "Option::is_none")] 519 | pub working_directory: Option, 520 | 521 | /// The retry strategy for the step. 522 | #[serde(skip_serializing_if = "Option::is_none")] 523 | pub retry: Option, 524 | 525 | /// Artifacts produced by the step. 526 | #[serde(skip_serializing_if = "Option::is_none")] 527 | pub artifacts: Option, 528 | } 529 | 530 | impl StepValue { 531 | /// Creates a new `StepValue` that runs the provided shell command. 532 | pub fn run(cmd: T) -> Self { 533 | StepValue { run: Some(cmd.to_string()), ..Default::default() } 534 | } 535 | 536 | /// Creates a new `StepValue` that uses an action. 537 | pub fn uses( 538 | owner: Owner, 539 | repo: Repo, 540 | version: Version, 541 | ) -> Self { 542 | StepValue { 543 | uses: Some(format!( 544 | "{}/{}@{}", 545 | owner.to_string(), 546 | repo.to_string(), 547 | version.to_string() 548 | )), 549 | ..Default::default() 550 | } 551 | } 552 | } 553 | 554 | /// Represents a step in the workflow. 555 | impl Step { 556 | /// Adds an environment variable to the step. 557 | pub fn add_env>(mut self, new_env: R) -> Self { 558 | let mut env = self.value.env.unwrap_or_default(); 559 | 560 | env.0.extend(new_env.into().0); 561 | self.value.env = Some(env); 562 | self 563 | } 564 | } 565 | 566 | /// Represents a step that runs a command. 567 | impl Step { 568 | /// Creates a new `Step` that runs the provided shell command. 569 | pub fn run(cmd: T) -> Self { 570 | Step { value: StepValue::run(cmd), marker: Default::default() } 571 | } 572 | } 573 | 574 | /// Represents a step that uses an action. 575 | impl Step { 576 | /// Creates a new `Step` that uses an action. 577 | pub fn uses( 578 | owner: Owner, 579 | repo: Repo, 580 | version: Version, 581 | ) -> Self { 582 | Step { 583 | value: StepValue::uses(owner, repo, version), 584 | marker: Default::default(), 585 | } 586 | } 587 | 588 | /// Creates a step pointing to the default GitHub's Checkout Action. 589 | pub fn checkout() -> Step { 590 | Step::uses("actions", "checkout", "v4").name("Checkout Code") 591 | } 592 | 593 | /// Adds a new input to the step. 594 | pub fn add_with>(mut self, new_with: I) -> Self { 595 | let mut with = self.value.with.unwrap_or_default(); 596 | with.merge(new_with.into()); 597 | if with.0.is_empty() { 598 | self.value.with = None; 599 | } else { 600 | self.value.with = Some(with); 601 | } 602 | 603 | self 604 | } 605 | 606 | /// Adds a new input to the step when a condition is met. 607 | pub fn add_with_when>(self, cond: bool, new_with: I) -> Self { 608 | if cond { 609 | self.add_with(new_with) 610 | } else { 611 | self 612 | } 613 | } 614 | } 615 | 616 | /// Represents a key-value pair for inputs. 617 | impl From<(S1, S2)> for Input { 618 | /// Converts a tuple into an `Input`. 619 | fn from(value: (S1, S2)) -> Self { 620 | let mut index_map: IndexMap = IndexMap::new(); 621 | index_map.insert(value.0.to_string(), Value::String(value.1.to_string())); 622 | Input(index_map) 623 | } 624 | } 625 | 626 | /// Represents environment variables as key-value pairs. 627 | impl From<(S1, S2)> for Env { 628 | /// Converts a tuple into an `Env`. 629 | fn from(value: (S1, S2)) -> Self { 630 | let mut index_map: IndexMap = IndexMap::new(); 631 | index_map.insert(value.0.to_string(), Value::String(value.1.to_string())); 632 | Env(index_map) 633 | } 634 | } 635 | 636 | /// Represents the runner environment for jobs. 637 | #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 638 | #[serde(rename_all = "kebab-case")] 639 | pub enum Runner { 640 | #[default] 641 | Linux, 642 | MacOS, 643 | Windows, 644 | Custom(String), 645 | } 646 | 647 | /// Represents a container configuration for jobs. 648 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 649 | #[serde(rename_all = "kebab-case")] 650 | #[setters(strip_option, into)] 651 | pub struct Container { 652 | /// The image to use for the container. 653 | pub image: String, 654 | 655 | /// Credentials for accessing the container. 656 | #[serde(skip_serializing_if = "Option::is_none")] 657 | pub credentials: Option, 658 | 659 | /// Environment variables for the container. 660 | #[serde(skip_serializing_if = "Option::is_none")] 661 | pub env: Option, 662 | 663 | /// Ports to expose from the container. 664 | #[serde(skip_serializing_if = "Option::is_none")] 665 | pub ports: Option>, 666 | 667 | /// Volumes to mount in the container. 668 | #[serde(skip_serializing_if = "Option::is_none")] 669 | pub volumes: Option>, 670 | 671 | /// Additional options for the container. 672 | #[serde(skip_serializing_if = "Option::is_none")] 673 | pub options: Option, 674 | 675 | /// Hostname for the container. 676 | #[serde(skip_serializing_if = "Option::is_none")] 677 | pub hostname: Option, 678 | } 679 | 680 | /// Represents credentials for accessing a container. 681 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 682 | #[serde(rename_all = "kebab-case")] 683 | #[setters(strip_option, into)] 684 | pub struct Credentials { 685 | /// The username for authentication. 686 | pub username: String, 687 | 688 | /// The password for authentication. 689 | pub password: String, 690 | } 691 | 692 | /// Represents a network port. 693 | #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] 694 | #[serde(rename_all = "kebab-case")] 695 | pub enum Port { 696 | /// A port specified by its number. 697 | Number(u16), 698 | 699 | /// A port specified by its name. 700 | Name(String), 701 | } 702 | 703 | /// Represents a volume configuration for containers. 704 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 705 | #[serde(rename_all = "kebab-case")] 706 | #[setters(strip_option, into)] 707 | pub struct Volume { 708 | /// The source path of the volume. 709 | pub source: String, 710 | 711 | /// The destination path of the volume. 712 | pub destination: String, 713 | } 714 | 715 | impl Volume { 716 | /// Creates a new `Volume` from a string representation. 717 | pub fn new(volume_str: &str) -> Option { 718 | let parts: Vec<&str> = volume_str.split(':').collect(); 719 | if parts.len() == 2 { 720 | Some(Volume { 721 | source: parts[0].to_string(), 722 | destination: parts[1].to_string(), 723 | }) 724 | } else { 725 | None 726 | } 727 | } 728 | } 729 | 730 | /// Represents concurrency settings for workflows. 731 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 732 | #[serde(rename_all = "kebab-case")] 733 | #[setters(strip_option, into)] 734 | pub struct Concurrency { 735 | /// The group name for concurrency. 736 | pub group: String, 737 | 738 | /// Whether to cancel in-progress jobs. 739 | #[serde(skip_serializing_if = "Option::is_none")] 740 | pub cancel_in_progress: Option, 741 | 742 | /// The limit on concurrent jobs. 743 | #[serde(skip_serializing_if = "Option::is_none")] 744 | pub limit: Option, 745 | } 746 | 747 | impl Concurrency { 748 | pub fn new(group: impl Into) -> Self { 749 | let expr: Expression = group.into(); 750 | Self { group: expr.0, ..Default::default() } 751 | } 752 | } 753 | 754 | /// Represents permissions for the `GITHUB_TOKEN`. 755 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 756 | #[serde(rename_all = "kebab-case")] 757 | #[setters(strip_option, into)] 758 | pub struct Permissions { 759 | /// Permissions for actions. 760 | #[serde(skip_serializing_if = "Option::is_none")] 761 | pub actions: Option, 762 | 763 | /// Permissions for repository contents. 764 | #[serde(skip_serializing_if = "Option::is_none")] 765 | pub contents: Option, 766 | 767 | /// Permissions for issues. 768 | #[serde(skip_serializing_if = "Option::is_none")] 769 | pub issues: Option, 770 | 771 | /// Permissions for pull requests. 772 | #[serde(skip_serializing_if = "Option::is_none")] 773 | pub pull_requests: Option, 774 | 775 | /// Permissions for deployments. 776 | #[serde(skip_serializing_if = "Option::is_none")] 777 | pub deployments: Option, 778 | 779 | /// Permissions for checks. 780 | #[serde(skip_serializing_if = "Option::is_none")] 781 | pub checks: Option, 782 | 783 | /// Permissions for statuses. 784 | #[serde(skip_serializing_if = "Option::is_none")] 785 | pub statuses: Option, 786 | 787 | /// Permissions for packages. 788 | #[serde(skip_serializing_if = "Option::is_none")] 789 | pub packages: Option, 790 | 791 | /// Permissions for pages. 792 | #[serde(skip_serializing_if = "Option::is_none")] 793 | pub pages: Option, 794 | 795 | /// Permissions for ID tokens. 796 | #[serde(skip_serializing_if = "Option::is_none")] 797 | pub id_token: Option, 798 | } 799 | 800 | /// Represents the level of permissions. 801 | #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 802 | #[serde(rename_all = "kebab-case")] 803 | pub enum Level { 804 | Read, 805 | Write, 806 | #[default] 807 | None, 808 | } 809 | 810 | /// Represents the strategy for running jobs. 811 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 812 | #[serde(rename_all = "kebab-case")] 813 | #[setters(strip_option, into)] 814 | pub struct Strategy { 815 | /// The matrix for job execution. 816 | #[serde(skip_serializing_if = "Option::is_none")] 817 | pub matrix: Option, 818 | 819 | /// Whether to fail fast on errors. 820 | #[serde(skip_serializing_if = "Option::is_none")] 821 | pub fail_fast: Option, 822 | 823 | /// The maximum number of jobs to run in parallel. 824 | #[serde(skip_serializing_if = "Option::is_none")] 825 | pub max_parallel: Option, 826 | } 827 | 828 | /// Represents an environment for jobs. 829 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 830 | #[serde(rename_all = "kebab-case")] 831 | #[setters(strip_option, into)] 832 | pub struct Environment { 833 | /// The name of the environment. 834 | pub name: String, 835 | 836 | /// The URL associated with the environment. 837 | #[serde(skip_serializing_if = "Option::is_none")] 838 | pub url: Option, 839 | } 840 | 841 | /// Represents default settings for jobs. 842 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 843 | #[serde(rename_all = "kebab-case")] 844 | #[setters(strip_option, into)] 845 | pub struct Defaults { 846 | /// Default settings for running jobs. 847 | #[serde(skip_serializing_if = "Option::is_none")] 848 | pub run: Option, 849 | 850 | /// Default retry settings for jobs. 851 | #[serde(skip_serializing_if = "Option::is_none")] 852 | pub retry: Option, 853 | 854 | /// Default concurrency settings for jobs. 855 | #[serde(skip_serializing_if = "Option::is_none")] 856 | pub concurrency: Option, 857 | } 858 | 859 | /// Represents default settings for running commands. 860 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 861 | #[serde(rename_all = "kebab-case")] 862 | #[setters(strip_option, into)] 863 | pub struct RunDefaults { 864 | /// The shell to use for running commands. 865 | #[serde(skip_serializing_if = "Option::is_none")] 866 | pub shell: Option, 867 | 868 | /// The working directory for running commands. 869 | #[serde(skip_serializing_if = "Option::is_none")] 870 | pub working_directory: Option, 871 | } 872 | 873 | /// Represents default settings for retries. 874 | #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 875 | #[serde(rename_all = "kebab-case")] 876 | pub struct RetryDefaults { 877 | /// The maximum number of retry attempts. 878 | #[serde(skip_serializing_if = "Option::is_none")] 879 | pub max_attempts: Option, 880 | } 881 | 882 | /// Represents an expression used in conditions. 883 | #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 884 | pub struct Expression(String); 885 | 886 | impl Expression { 887 | /// Creates a new `Expression` from a string. 888 | pub fn new(expr: T) -> Self { 889 | Self(expr.to_string()) 890 | } 891 | } 892 | 893 | /// Represents a secret required for the workflow. 894 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 895 | #[serde(rename_all = "kebab-case")] 896 | #[setters(strip_option, into)] 897 | pub struct Secret { 898 | /// Indicates if the secret is required. 899 | pub required: bool, 900 | 901 | /// A description of the secret. 902 | #[serde(skip_serializing_if = "Option::is_none")] 903 | pub description: Option, 904 | } 905 | 906 | /// Represents a strategy for retrying jobs. 907 | #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 908 | #[serde(rename_all = "kebab-case")] 909 | pub struct RetryStrategy { 910 | /// The maximum number of retry attempts. 911 | #[serde(skip_serializing_if = "Option::is_none")] 912 | pub max_attempts: Option, 913 | } 914 | 915 | /// Represents artifacts produced by jobs. 916 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 917 | #[serde(rename_all = "kebab-case")] 918 | #[setters(strip_option, into)] 919 | pub struct Artifacts { 920 | /// Artifacts to upload after the job. 921 | #[serde(skip_serializing_if = "Option::is_none")] 922 | pub upload: Option>, 923 | 924 | /// Artifacts to download before the job. 925 | #[serde(skip_serializing_if = "Option::is_none")] 926 | pub download: Option>, 927 | } 928 | 929 | /// Represents an artifact produced by a job. 930 | #[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] 931 | #[serde(rename_all = "kebab-case")] 932 | #[setters(strip_option, into)] 933 | pub struct Artifact { 934 | /// The name of the artifact. 935 | pub name: String, 936 | 937 | /// The path to the artifact. 938 | pub path: String, 939 | 940 | /// The number of days to retain the artifact. 941 | #[serde(skip_serializing_if = "Option::is_none")] 942 | pub retention_days: Option, 943 | } 944 | -------------------------------------------------------------------------------- /crates/gh-workflow/tests/fixtures/workflow-bench.yml: -------------------------------------------------------------------------------- 1 | name: "Run benchmark" 2 | on: 3 | pull_request_target: 4 | types: [assigned, opened, synchronize, reopened, edited] 5 | push: 6 | branches: 7 | - main 8 | permissions: 9 | contents: write 10 | issues: write 11 | pull-requests: write 12 | packages: write 13 | jobs: 14 | build: 15 | if: github.event.head_commit.message != 'Update performance results in README.md' 16 | runs-on: benchmarking-runner 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | strategy: 20 | matrix: 21 | service: 22 | [ 23 | apollo_server, 24 | caliban, 25 | netflix_dgs, 26 | gqlgen, 27 | tailcall, 28 | async_graphql, 29 | hasura, 30 | graphql_jit, 31 | ] 32 | steps: 33 | - name: Checkout (GitHub) 34 | uses: actions/checkout@v4 35 | with: 36 | token: ${{ secrets.GITHUB_TOKEN }} 37 | ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} 38 | - name: Login to GitHub Container Registry 39 | uses: docker/login-action@v2 40 | with: 41 | registry: ghcr.io 42 | username: ${{ github.repository_owner }} 43 | password: ${{ secrets.GITHUB_TOKEN }} 44 | - name: Build devcontainer and run setup and benchmark 45 | uses: devcontainers/ci@v0.3 46 | with: 47 | imageName: ghcr.io/tailcallhq/graphql-benchmark 48 | push: always 49 | runCmd: | 50 | bash ./graphql/${{ matrix.service }}/setup.sh 51 | bash run_benchmarks.sh ${{ matrix.service }} 52 | 53 | - name: List benchmark files 54 | run: | 55 | ls -la bench*.txt || echo "No matching files found" 56 | 57 | - name: Upload benchmark results 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: benchmark-results 61 | path: bench*.txt 62 | 63 | analyze: 64 | needs: [build] 65 | runs-on: benchmarking-runner 66 | steps: 67 | - name: Checkout (GitHub) 68 | uses: actions/checkout@v4 69 | 70 | - name: Download all benchmark results 71 | uses: actions/download-artifact@v3 72 | with: 73 | name: benchmark-results 74 | path: . 75 | 76 | - name: List downloaded artifacts 77 | run: ls -la bench*.txt || echo "No matching files found" 78 | 79 | - name: Analyze results 80 | run: | 81 | bash run_analyze_script.sh 82 | 83 | - name: Print benchmark results 84 | run: cat ./results.md 85 | 86 | - name: Comment benchmark results on PR 87 | if: github.event_name == 'pull_request_target' 88 | uses: peter-evans/commit-comment@v3 89 | with: 90 | sha: ${{ github.event.pull_request.head.sha }} 91 | body-path: "results.md" 92 | reactions: eyes 93 | 94 | - name: Commit and push changes (on main branch) 95 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 96 | uses: stefanzweifel/git-auto-commit-action@v5 97 | with: 98 | branch: main 99 | commit_author: Author 100 | commit_message: "[ci skip] update performance results in README.md" 101 | -------------------------------------------------------------------------------- /crates/gh-workflow/tests/fixtures/workflow-ci.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: [main] 9 | types: [opened, reopened, synchronize] 10 | permissions: 11 | contents: read 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | # TODO: cancel in progress is not ok for a release since it can be cancelled in a middle of updates 16 | # and we can end up with partial release. 17 | # but for pull-requests cancelling previously running jobs could be beneficial 18 | # cancel-in-progress: true 19 | 20 | jobs: 21 | setup_build_matrix: 22 | name: Outputs matrix used for cross compilation 23 | uses: ./.github/workflows/build_matrix.yml 24 | 25 | check_if_build: 26 | name: Check if Build 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 1000 33 | - id: check 34 | run: | 35 | chmod +x .github/scripts/check_if_build.sh 36 | .github/scripts/check_if_build.sh 37 | outputs: 38 | check_if_build: ${{ steps.check.outputs.check_if_build }} 39 | 40 | test_aws_build: 41 | name: Test AWS Lambda Build 42 | if: github.event_name == 'pull_request' 43 | runs-on: ubuntu-latest 44 | defaults: 45 | run: 46 | working-directory: ./tailcall-aws-lambda 47 | 48 | steps: 49 | - uses: actions/checkout@v4 50 | 51 | - name: Install Rust 52 | uses: actions-rust-lang/setup-rust-toolchain@v1 53 | 54 | - name: Install Python 55 | uses: actions/setup-python@v5 56 | with: 57 | python-version: "3.12" 58 | 59 | - name: Install cargo-lambda 60 | run: pip install cargo-lambda 61 | 62 | - name: Build 63 | run: cargo lambda build 64 | 65 | test_wasm: 66 | name: Run Tests (WASM) 67 | runs-on: ubuntu-latest 68 | defaults: 69 | run: 70 | working-directory: ./tailcall-wasm 71 | steps: 72 | - uses: actions/checkout@v4 73 | - name: Install Rust 74 | uses: actions-rust-lang/setup-rust-toolchain@v1 75 | with: 76 | target: wasm32-unknown-unknown 77 | 78 | - name: Install Wasm Pack 79 | run: cargo install wasm-bindgen-cli --vers "0.2.92" 80 | 81 | - name: Test WASM 82 | run: | 83 | cargo install wasm-pack 84 | wasm-pack test --node 85 | 86 | test_cf: 87 | name: Run Tests (Cloudflare) 88 | runs-on: ubuntu-latest 89 | defaults: 90 | run: 91 | working-directory: ./tailcall-cloudflare 92 | 93 | steps: 94 | - uses: actions/checkout@v4 95 | 96 | - name: Install Rust 97 | uses: actions-rust-lang/setup-rust-toolchain@v1 98 | with: 99 | target: wasm32-unknown-unknown 100 | 101 | - name: Install Node.js 102 | uses: actions/setup-node@v4 103 | with: 104 | node-version: "20.11.0" 105 | 106 | - name: Package Install 107 | run: npm install 108 | 109 | - name: Test CF 110 | run: npm test 111 | 112 | test: 113 | name: Run Tests on ${{ matrix.build }} 114 | runs-on: ${{ matrix.os || 'ubuntu-latest' }} 115 | needs: [setup_build_matrix] 116 | strategy: 117 | fail-fast: false 118 | matrix: ${{ fromJson(needs.setup_build_matrix.outputs.matrix) }} 119 | 120 | steps: 121 | - uses: actions/checkout@v4 122 | 123 | - name: Install Node.js 124 | uses: actions/setup-node@v4 125 | with: 126 | node-version: "20.11.0" 127 | - name: Install Prettier 128 | run: npm i -g prettier 129 | 130 | - name: Install Rust Toolchain 131 | uses: actions-rust-lang/setup-rust-toolchain@v1 132 | 133 | - name: Install Cross compilation toolchain 134 | uses: taiki-e/setup-cross-toolchain-action@v1 135 | with: 136 | target: ${{ matrix.target }} 137 | 138 | - name: Cache NASM 139 | if: runner.os == 'Windows' 140 | uses: actions/cache@v4 141 | with: 142 | path: | 143 | nasm-2.16.02 144 | nasm.zip 145 | key: ${{ runner.os }}-nasm-${{ matrix.build }}-v2 146 | restore-keys: | 147 | ${{ runner.os }}-nasm-${{ matrix.build }}- 148 | 149 | - name: Install dependencies on Windows 150 | if: runner.os == 'Windows' 151 | run: | 152 | if (Test-Path nasm-2.16.02) { 153 | echo "Using cached NASM" 154 | } else { 155 | $nasmUrl = 'https://www.nasm.us/pub/nasm/releasebuilds/2.16.02/win64/nasm-2.16.02-win64.zip' 156 | $nasmZip = 'nasm.zip' 157 | Invoke-WebRequest -Uri $nasmUrl -OutFile $nasmZip 158 | Expand-Archive -Path $nasmZip -DestinationPath '.' 159 | } 160 | echo "$(Resolve-Path nasm-2.16.02)" >> $env:GITHUB_PATH 161 | 162 | - uses: taiki-e/install-action@cargo-llvm-cov 163 | 164 | - name: Run Cargo Test 165 | if: matrix.test != 'false' 166 | # TODO: run llvm-cov only for single build since other builds are not sent to codecov anyway 167 | run: cargo llvm-cov --workspace ${{ matrix.features }} --lcov --target ${{ matrix.target }} --output-path lcov.info 168 | 169 | - name: Upload Coverage to Codecov 170 | if: matrix.build == 'darwin-arm64' 171 | uses: Wandalen/wretry.action@v3 172 | with: 173 | action: codecov/codecov-action@v4 174 | attempt_limit: 3 175 | attempt_delay: 10000 176 | with: | 177 | token: ${{ secrets.CODECOV_TOKEN }} 178 | files: lcov.info 179 | fail_ci_if_error: true 180 | 181 | check-examples: 182 | name: Check Examples 183 | runs-on: ubuntu-latest 184 | steps: 185 | - uses: actions/checkout@v4 186 | - name: Set up Rust 187 | uses: actions-rs/toolchain@v1 188 | with: 189 | toolchain: stable 190 | profile: minimal 191 | override: true 192 | 193 | - name: Install Rust 194 | uses: actions-rust-lang/setup-rust-toolchain@v1 195 | 196 | - name: Build Project 197 | run: cargo build 198 | 199 | - name: Check all examples 200 | run: ./examples/lint.sh 201 | 202 | draft_release: 203 | name: Draft Release 204 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 205 | runs-on: ubuntu-latest 206 | permissions: 207 | contents: write 208 | pull-requests: write 209 | steps: 210 | - name: Checkout Current Branch (Fast) 211 | uses: actions/checkout@v4 212 | 213 | - id: create_release 214 | uses: release-drafter/release-drafter@v6 215 | env: 216 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 217 | with: 218 | config-name: release-drafter.yml 219 | 220 | - name: Set Output for Later Jobs 221 | id: set_output 222 | run: | 223 | echo "create_release_name=${{ steps.create_release.outputs.name }}" >> $GITHUB_OUTPUT 224 | echo "create_release_id=${{ steps.create_release.outputs.id }}" >> $GITHUB_OUTPUT 225 | outputs: 226 | create_release_name: ${{ steps.set_output.outputs.create_release_name }} 227 | create_release_id: ${{ steps.set_output.outputs.create_release_id }} 228 | 229 | # TODO: move to separate file to separate responsibilities 230 | release: 231 | name: Release 232 | needs: 233 | [ 234 | setup_build_matrix, 235 | test, 236 | draft_release, 237 | check_if_build, 238 | test_cf, 239 | test_wasm, 240 | ] 241 | # TODO: put a condition to separate job that other will depend on to remove duplication? 242 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && (needs.check_if_build.outputs.check_if_build == 'true') 243 | runs-on: ${{ matrix.os || 'ubuntu-latest' }} 244 | strategy: 245 | fail-fast: false 246 | matrix: ${{ fromJson(needs.setup_build_matrix.outputs.matrix) }} 247 | permissions: 248 | contents: write 249 | pull-requests: write 250 | env: 251 | GITHUB_TOKEN: ${{secrets.GITHUBTOKEN}} 252 | GA_API_SECRET: ${{secrets.GA_API_SECRET}} 253 | GA_MEASUREMENT_ID: ${{secrets.GA_MEASUREMENT_ID}} 254 | POSTHOG_API_SECRET: ${{secrets.POSTHOG_API_SECRET}} 255 | APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} 256 | 257 | steps: 258 | - name: Checkout Current Branch (Fast) 259 | uses: actions/checkout@v4 260 | 261 | - name: Install Rust Toolchain 262 | uses: actions-rust-lang/setup-rust-toolchain@v1 263 | with: 264 | target: ${{ matrix.target }} 265 | 266 | - name: Build 267 | env: 268 | APP_VERSION: ${{ needs.draft_release.outputs.create_release_name}} 269 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 270 | uses: ClementTsang/cargo-action@v0.0.6 271 | with: 272 | use-cross: ${{ matrix.cross }} 273 | command: build 274 | args: ${{matrix.features}} --release --target ${{ matrix.target }} 275 | 276 | - name: Install Node.js 277 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 278 | uses: actions/setup-node@v4 279 | with: 280 | node-version: 20.11.0 281 | registry-url: https://registry.npmjs.org 282 | 283 | - name: Install dependencies 284 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 285 | run: | 286 | cd npm 287 | npm install 288 | 289 | - name: Run generate.js script 290 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 291 | run: | 292 | cd npm 293 | npm run gen -- --target ${{matrix.target}} --version ${{ env.APP_VERSION }} --build ${{matrix.build}} --ext ${{ matrix.ext || '' }} --libc ${{ matrix.libc }} 294 | - name: Setup .npmrc file to publish to npm 295 | run: echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc 296 | 297 | - name: NPM Publish 298 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 299 | uses: JS-DevTools/npm-publish@main 300 | with: 301 | token: ${{ secrets.NPM_TOKEN }} 302 | package: npm/@tailcallhq/core-${{matrix.build}} 303 | access: public 304 | 305 | - name: Rename Binary with Target Name 306 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 307 | run: | 308 | pwd 309 | cp target/${{ matrix.target }}/release/tailcall${{ matrix.ext }} target/${{ matrix.target }}/release/tailcall-${{ matrix.target }}${{ matrix.ext }} 310 | 311 | - name: Upload ${{ matrix.target }} Binary 312 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 313 | uses: xresloader/upload-to-github-release@v1 314 | with: 315 | release_id: ${{ needs.draft_release.outputs.create_release_id }} 316 | file: target/${{ matrix.target }}/release/tailcall-${{ matrix.target }}${{ matrix.ext }} 317 | overwrite: true 318 | release_lambda: 319 | name: Release (AWS Lambda) 320 | needs: [test, draft_release, check_if_build, test_cf] 321 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && (needs.check_if_build.outputs.check_if_build == 'true') 322 | runs-on: ubuntu-latest 323 | permissions: 324 | contents: write 325 | pull-requests: write 326 | env: 327 | GITHUB_TOKEN: ${{secrets.GITHUBTOKEN}} 328 | APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} 329 | 330 | steps: 331 | - name: Checkout Current Branch (Fast) 332 | uses: actions/checkout@v4 333 | 334 | - name: Install Correct Toolchain 335 | uses: actions-rust-lang/setup-rust-toolchain@v1 336 | 337 | - name: Install Python 338 | uses: actions/setup-python@v5 339 | with: 340 | python-version: "3.12" 341 | 342 | - name: Install cargo-lambda 343 | run: pip install cargo-lambda 344 | 345 | - name: Build 346 | env: 347 | APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} 348 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 349 | run: cargo lambda build -p tailcall-aws-lambda --release --target x86_64-unknown-linux-musl 350 | 351 | - name: Rename Binary with Target Name 352 | run: | 353 | pwd 354 | cp target/lambda/tailcall-aws-lambda/bootstrap target/lambda/tailcall-aws-lambda/tailcall-aws-lambda-bootstrap 355 | 356 | - name: Upload AWS Lambda Bootstrap Binary 357 | uses: xresloader/upload-to-github-release@v1 358 | with: 359 | release_id: ${{ needs.draft_release.outputs.create_release_id }} 360 | file: target/lambda/tailcall-aws-lambda/tailcall-aws-lambda-bootstrap 361 | overwrite: true 362 | semantic_release: 363 | name: Semantic Release 364 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 365 | needs: [draft_release, release, release_lambda] 366 | runs-on: ubuntu-latest 367 | permissions: 368 | contents: write 369 | pull-requests: write 370 | env: 371 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 372 | APP_VERSION: ${{needs.draft_release.outputs.create_release_name }} 373 | steps: 374 | - name: Publish Release 375 | uses: test-room-7/action-publish-release-drafts@v0 376 | env: 377 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 378 | with: 379 | github-token: ${{ secrets.GITHUB_TOKEN }} 380 | tag-name: ${{needs.draft_release.outputs.create_release_name }} 381 | 382 | publish_npm_root: 383 | name: Publish NPM main package 384 | needs: [draft_release, release] 385 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 386 | runs-on: ubuntu-latest 387 | steps: 388 | - name: Checkout Current Branch (Fast) 389 | uses: actions/checkout@v4 390 | - name: Install Node 391 | uses: actions/setup-node@v4 392 | with: 393 | node-version: 20.11.0 394 | registry-url: https://registry.npmjs.org 395 | - name: Install Rust 396 | uses: actions-rust-lang/setup-rust-toolchain@v1 397 | with: 398 | target: wasm32-unknown-unknown 399 | - name: Setup .npmrc file to publish to npm 400 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc 401 | - name: Install dependencies 402 | run: | 403 | cd npm 404 | npm install 405 | - name: Run generate-root.js script 406 | env: 407 | APP_VERSION: ${{needs.draft_release.outputs.create_release_name }} 408 | run: | 409 | cd npm 410 | npm run gen-root -- --version ${{ env.APP_VERSION }} --name @tailcallhq/tailcall 411 | - name: Setup .npmrc file to publish to npm 412 | run: echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc 413 | - name: Publish packages 414 | uses: JS-DevTools/npm-publish@main 415 | with: 416 | token: ${{ secrets.NPM_TOKEN }} 417 | package: npm/@tailcallhq/tailcall 418 | access: public 419 | 420 | env: 421 | APP_VERSION: ${{needs.draft_release.outputs.create_release_name }} 422 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 423 | 424 | build-and-push-image: 425 | env: 426 | REGISTRY: ghcr.io 427 | IMAGE_NAME: ${{ github.repository }}/tc-server 428 | APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} # Ensure APP_VERSION is set correctly 429 | needs: [draft_release, release] 430 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 431 | runs-on: ubuntu-latest 432 | # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. 433 | permissions: 434 | contents: read 435 | packages: write 436 | # 437 | steps: 438 | - name: Checkout Repository 439 | uses: actions/checkout@v4 440 | # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. 441 | - name: Log in to the Container Registry 442 | uses: docker/login-action@v3 443 | with: 444 | registry: ${{ env.REGISTRY }} 445 | username: ${{ github.actor }} 446 | password: ${{ secrets.GITHUBTOKEN }} 447 | # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. 448 | - name: Extract Metadata (tags, labels) for Docker 449 | id: meta 450 | uses: docker/metadata-action@v5 451 | with: 452 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 453 | tags: | 454 | type=raw,value=${{ env.APP_VERSION }} 455 | type=raw,value=latest,enable=${{ endsWith(env.APP_VERSION, '-SNAPSHOT') == false }} 456 | # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. 457 | # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. 458 | # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. 459 | - name: Build and Push the Docker Image 460 | uses: docker/build-push-action@v6 461 | with: 462 | context: . 463 | push: true 464 | tags: ${{ steps.meta.outputs.tags }} 465 | labels: ${{ steps.meta.outputs.labels }} 466 | 467 | homebrew-release: 468 | name: Homebrew Release 469 | needs: [draft_release, release, semantic_release] 470 | if: (startsWith(github.event.head_commit.message, 'feat') || startsWith(github.event.head_commit.message, 'fix')) && (github.event_name == 'push' && github.ref == 'refs/heads/main') 471 | permissions: 472 | contents: write 473 | pull-requests: write 474 | runs-on: ubuntu-latest 475 | steps: 476 | - uses: actions/checkout@v4 477 | with: 478 | repository: tailcallhq/homebrew-tailcall 479 | ref: main 480 | token: ${{ secrets.HOMEBREW_ACCESS }} 481 | 482 | - name: Update Homebrew Formula 483 | run: ./update-formula.sh ${{needs.draft_release.outputs.create_release_name }} 484 | -------------------------------------------------------------------------------- /crates/gh-workflow/tests/fixtures/workflow-demo.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Demo 2 | run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | jobs: 8 | Explore-GitHub-Actions: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." 12 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" 13 | - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." 14 | - name: Check out repository code 15 | uses: actions/checkout@v4 16 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." 17 | - run: echo "🖥️ The workflow is now ready to test your code on the runner." 18 | - name: List files in the repository 19 | run: | 20 | ls ${{ github.workspace }} 21 | - run: echo "🍏 This job's status is ${{ job.status }}." 22 | -------------------------------------------------------------------------------- /crates/gh-workflow/tests/fixtures/workflow-rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build and Test 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, windows-latest, macos-latest] 16 | rust: [stable, beta, nightly] 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | - name: Set up Rust 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: ${{ matrix.rust }} 24 | override: true 25 | - name: Install Dependencies 26 | run: sudo apt-get update && sudo apt-get install -y pkg-config libssl-dev 27 | - name: Cache Cargo registry 28 | uses: actions/cache@v3 29 | with: 30 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 31 | path: ~/.cargo/registry 32 | restore-keys: | 33 | ${{ runner.os }}-cargo-registry- 34 | - name: Cache Cargo build 35 | uses: actions/cache@v3 36 | with: 37 | path: target 38 | key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} 39 | restore-keys: | 40 | ${{ runner.os }}-cargo-build- 41 | - name: Build project 42 | run: cargo build --verbose 43 | - name: Run tests 44 | run: cargo test --verbose 45 | - name: Run clippy (optional) 46 | run: cargo clippy --all-targets --all-features -- -D warnings 47 | - name: Run fmt check (optional) 48 | run: cargo fmt -- --check 49 | -------------------------------------------------------------------------------- /crates/gh-workflow/tests/test-workflow.rs: -------------------------------------------------------------------------------- 1 | use gh_workflow::Workflow; 2 | use pretty_assertions::assert_eq; 3 | use serde_json::Value; 4 | 5 | fn split(content: &str) -> (Value, Value) { 6 | let parsed = Workflow::parse(content).unwrap(); 7 | let actual = serde_yaml::from_str::(&parsed.to_string().unwrap()).unwrap(); 8 | let expected = serde_yaml::from_str::(content).unwrap(); 9 | 10 | (actual, expected) 11 | } 12 | 13 | #[test] 14 | fn test_workflow_bench() { 15 | let (actual, expected) = split(include_str!("./fixtures/workflow-bench.yml")); 16 | assert_eq!(actual, expected); 17 | } 18 | 19 | #[test] 20 | fn test_workflow_ci() { 21 | let (actual, expected) = split(include_str!("./fixtures/workflow-ci.yml")); 22 | assert_eq!(actual, expected); 23 | } 24 | 25 | #[test] 26 | fn test_workflow_demo() { 27 | let (actual, expected) = split(include_str!("./fixtures/workflow-demo.yml")); 28 | assert_eq!(actual, expected); 29 | } 30 | 31 | #[test] 32 | fn test_workflow_rust() { 33 | let (actual, expected) = split(include_str!("./fixtures/workflow-rust.yml")); 34 | assert_eq!(actual, expected); 35 | } 36 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "automerge": true, 5 | "automergeType": "pr", 6 | "automergeStrategy": "squash", 7 | "platformAutomerge": true, 8 | "packageRules": [ 9 | { 10 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 11 | "automerge": true 12 | } 13 | ] 14 | } --------------------------------------------------------------------------------