{"id":24806491,"url":"https://github.com/op5dev/tf-via-pr","last_synced_at":"2025-04-04T12:05:50.336Z","repository":{"id":65875434,"uuid":"598365601","full_name":"OP5dev/TF-via-PR","owner":"OP5dev","description":"Plan and apply Terraform/OpenTofu via PR automation, using best practices for secure and scalable IaC workflows.","archived":false,"fork":false,"pushed_at":"2025-03-30T20:47:19.000Z","size":32988,"stargazers_count":189,"open_issues_count":0,"forks_count":23,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-03T15:18:03.175Z","etag":null,"topics":["automation","aws","cicd-pipeline","devops","github-actions","infrastructure-as-code","opentofu","platform-engineering","terraform"],"latest_commit_sha":null,"homepage":"https://OP5.dev/s/tf-via-pr","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OP5dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["rdhar","op5dev"]}},"created_at":"2023-02-07T00:30:15.000Z","updated_at":"2025-03-31T12:23:19.000Z","dependencies_parsed_at":"2023-07-22T18:15:23.226Z","dependency_job_id":"8ec440e9-7944-49d0-b76a-06d060e47088","html_url":"https://github.com/OP5dev/TF-via-PR","commit_stats":null,"previous_names":["devsectop/tf-via-pr","devsectop/tf-via-pr-comments","op5dev/tf-via-pr"],"tags_count":69,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OP5dev%2FTF-via-PR","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OP5dev%2FTF-via-PR/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OP5dev%2FTF-via-PR/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OP5dev%2FTF-via-PR/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OP5dev","download_url":"https://codeload.github.com/OP5dev/TF-via-PR/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247174407,"owners_count":20896076,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","aws","cicd-pipeline","devops","github-actions","infrastructure-as-code","opentofu","platform-engineering","terraform"],"created_at":"2025-01-30T08:28:41.948Z","updated_at":"2025-04-04T12:05:50.318Z","avatar_url":"https://github.com/OP5dev.png","language":"Shell","readme":"[![Terraform Compatible](https://img.shields.io/badge/Terraform-Compatible-844FBA?logo=terraform\u0026logoColor=white)](https://github.com/hashicorp/setup-terraform \"Terraform Compatible.\")\n[![OpenTofu Compatible](https://img.shields.io/badge/OpenTofu-Compatible-FFDA18?logo=opentofu\u0026logoColor=white)](https://github.com/opentofu/setup-opentofu \"OpenTofu Compatible.\")\n*\n[![GitHub license](https://img.shields.io/github/license/op5dev/tf-via-pr?logo=apache\u0026label=License)](LICENSE \"Apache License 2.0.\")\n[![GitHub release tag](https://img.shields.io/github/v/release/op5dev/tf-via-pr?logo=semanticrelease\u0026label=Release)](https://github.com/op5dev/tf-via-pr/releases \"View all releases.\")\n*\n[![GitHub repository stargazers](https://img.shields.io/github/stars/op5dev/tf-via-pr)](https://github.com/op5dev/tf-via-pr \"Become a stargazer.\")\n\n# Terraform/OpenTofu via Pull Request (TF-via-PR)\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003e\n      \u003ch3\u003eWhat does it do?\u003c/h3\u003e\n    \u003c/th\u003e\n    \u003cth\u003e\n      \u003ch3\u003eWho is it for?\u003c/h3\u003e\n    \u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cul\u003e\n        \u003cli\u003ePlan and apply changes with CLI arguments and \u003cstrong\u003eencrypted plan file\u003c/strong\u003e to avoid configuration drift.\u003c/li\u003e\n        \u003cli\u003eOutline diff within up-to-date \u003cstrong\u003ePR comment\u003c/strong\u003e and matrix-friendly workflow summary, complete with log.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cul\u003e\n        \u003cli\u003eDevOps and Platform engineers wanting to empower their teams to \u003cstrong\u003eself-service\u003c/strong\u003e scalably.\u003c/li\u003e\n        \u003cli\u003eMaintainers looking to \u003cstrong\u003esecure\u003c/strong\u003e their pipeline without the overhead of containers or VMs.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\n### View: [Usage Examples](#usage-examples) · [Inputs](#inputs) · [Outputs](#outputs) · [Security](#security) · [Changelog](#changelog) · [License](#license)\n\n[![PR comment of plan output with \"Diff of changes\" section expanded.](/.github/assets/comment.png)](https://raw.githubusercontent.com/op5dev/tf-via-pr/refs/heads/main/.github/assets/comment.png \"View full-size image.\")\n\n\u003c/br\u003e\n\n## Usage Examples\n\n### How to get started?\n\n```yaml\non:\n  pull_request:\n  push:\n    branches: [main]\n\njobs:\n  provision:\n    runs-on: ubuntu-latest\n\n    permissions:\n      actions: read        # Required to identify workflow run.\n      checks: write        # Required to add status summary.\n      contents: read       # Required to checkout repository.\n      pull-requests: write # Required to add comment and label.\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: hashicorp/setup-terraform@v3\n        with:\n          terraform_wrapper: false\n\n      # Run plan by default, or apply on merge.\n      - uses: op5dev/tf-via-pr@v13\n        with:\n          working-directory: path/to/directory\n          command: ${{ github.event_name == 'push' \u0026\u0026 'apply' || 'plan' }}\n          arg-lock: ${{ github.event_name == 'push' }}\n          arg-backend-config: env/dev.tfbackend\n          arg-var-file: env/dev.tfvars\n          arg-workspace: dev-use1\n          plan-encrypt: ${{ secrets.PASSPHRASE }}\n```\n\n\u003e [!TIP]\n\u003e\n\u003e - All supported arguments (e.g., `-backend-config`, `-destroy`, `-parallelism`, etc.) are [listed below](#arguments).\n\u003e - Environment variables can be passed in for cloud platform authentication (e.g., [configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials \"Configuring AWS credentials for use in GitHub Actions.\") for short-lived credentials via OIDC).\n\u003e - Recommend setting `terraform_wrapper`/`tofu_wrapper` to `false` in order to output the [detailed exit code](https://developer.hashicorp.com/terraform/cli/commands/plan#detailed-exitcode) for better error handling.\n\n\u003c/br\u003e\n\n### Where to find more examples?\n\nThe following workflows showcase common use cases, while a comprehensive list of inputs is [documented](#inputs) below.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/pr_push_auth.yaml\"\u003e#1 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003epull_request\u003c/code\u003e (plan) and \u003ccode\u003epush\u003c/code\u003e (apply) events with Terraform, AWS \u003cstrong\u003eauthentication\u003c/strong\u003e and \u003cstrong\u003ecache\u003c/strong\u003e.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/pr_merge_matrix.yaml\"\u003e#2 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003epull_request\u003c/code\u003e (plan) and \u003ccode\u003emerge_group\u003c/code\u003e (apply) events with OpenTofu in \u003cstrong\u003ematrix\u003c/strong\u003e strategy.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/pr_push_lint.yaml\"\u003e#3 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003epull_request\u003c/code\u003e (plan) and \u003ccode\u003epush\u003c/code\u003e (apply) events with \u003cstrong\u003efmt/validate checks\u003c/strong\u003e and TFLint.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/pr_push_stages.yaml\"\u003e#4 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003epull_request\u003c/code\u003e (plan) and \u003ccode\u003epush\u003c/code\u003e (apply) events with \u003cstrong\u003econditional jobs\u003c/strong\u003e based on plan file.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/pr_manual_label.yaml\"\u003e#5 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003elabeled\u003c/code\u003e and \u003ccode\u003eworkflow_dispatch\u003c/code\u003e \u003cstrong\u003emanual\u003c/strong\u003e events on GitHub Enterprise (GHE) \u003cstrong\u003eself-hosted runner\u003c/strong\u003e.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ch4\u003e\u003ca href=\"/.github/examples/schedule_refresh.yaml\"\u003e#6 example ⤴\u003c/a\u003e\u003c/h4\u003e\n      \u003cp\u003eRuns on \u003ccode\u003eschedule\u003c/code\u003e \u003cstrong\u003ecron\u003c/strong\u003e event with \u003ccode\u003e-refresh-only\u003c/code\u003e to open an issue on \u003cstrong\u003econfiguration drift\u003c/strong\u003e.\u003c/p\u003e\n      \u003c/br\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\n### How does encryption work?\n\nBefore the workflow uploads the plan file as an artifact, it can be encrypted-at-rest with a passphrase using `plan-encrypt` input to prevent exposure of sensitive data (e.g., `${{ secrets.PASSPHRASE }}`). This is done with [OpenSSL](https://docs.openssl.org/master/man1/openssl-enc/ \"OpenSSL encryption documentation.\")'s symmetric stream counter mode ([256 bit AES in CTR](https://docs.openssl.org/3.3/man1/openssl-enc/#supported-ciphers:~:text=192/256%20bit-,AES%20in%20CTR%20mode,-aes%2D%5B128%7C192)) encryption with salt and pbkdf2.\n\nIn order to decrypt the plan file locally, use the following commands after downloading the artifact (adding a whitespace before `openssl` to prevent recording the command in shell history):\n\n```fish\nunzip \u003ctfplan.zip\u003e\nopenssl enc -d -aes-256-ctr -pbkdf2 -salt \\\n  -in   tfplan.encrypted \\\n  -out  tfplan.decrypted \\\n  -pass pass:\"\u003cpassphrase\u003e\"\n\u003ctf.tool\u003e show tfplan.decrypted\n```\n\n\u003c/br\u003e\n\n## Inputs\n\nAll supported CLI argument inputs are [listed below](#arguments) with accompanying options, while workflow configuration inputs are listed here.\n\n### Configuration\n\n| Type     | Name                | Description                                                                                                                               |\n| -------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| CLI      | `working-directory` | Specify the working directory of TF code, alias of `arg-chdir`.\u003c/br\u003eExample: `path/to/directory`                                          |\n| CLI      | `command`           | Command to run between: `plan` or `apply`.\u003csup\u003e1\u003c/sup\u003e\u003c/br\u003eExample: `plan`                                                                |\n| CLI      | `tool`              | Provisioning tool to use between: `terraform` or `tofu`.\u003c/br\u003eDefault: `terraform`                                                         |\n| CLI      | `plan-file`         | Supply existing plan file path instead of the auto-generated one.\u003c/br\u003eExample: `path/to/file.tfplan`                                      |\n| Check    | `format`            | Check format of TF code.\u003c/br\u003eDefault: `false`                                                                                             |\n| Check    | `validate`          | Check validation of TF code.\u003c/br\u003eDefault: `false`                                                                                         |\n| Check    | `plan-parity`       | Replace plan file if it matches a newly-generated one to prevent stale apply.\u003csup\u003e2\u003c/sup\u003e\u003c/br\u003eDefault: `false`                            |\n| Security | `plan-encrypt`      | Encrypt plan file artifact with the given input.\u003csup\u003e3\u003c/sup\u003e\u003c/br\u003eExample: `${{ secrets.PASSPHRASE }}`                                     |\n| Security | `preserve-plan`     | Preserve plan file \"tfplan\" in the given working directory after workflow execution.\u003c/br\u003eDefault: `false`                                 |\n| Security | `upload-plan`       | Upload plan file as GitHub workflow artifact.\u003c/br\u003eDefault: `true`                                                                         |\n| Security | `retention-days`    | Duration after which plan file artifact will expire in days.\u003c/br\u003eExample: `90`                                                            |\n| Security | `token`             | Specify a GitHub token.\u003c/br\u003eDefault: `${{ github.token }}`                                                                                |\n| UI       | `label-pr`          | Add a PR label with the command input (e.g., `tf:plan`).\u003c/br\u003eDefault: `true`                                                              |\n| UI       | `comment-pr`        | Add a PR comment: `always`, `on-change`, or `never`.\u003csup\u003e4\u003c/sup\u003e\u003c/br\u003eDefault: `always`                                                    |\n| UI       | `comment-method`    | PR comment by: `update` existing comment or `recreate` and delete previous one.\u003csup\u003e5\u003c/sup\u003e\u003c/br\u003eDefault: `update`                         |\n| UI       | `tag-actor`         | Tag the workflow triggering actor: `always`, `on-change`, or `never`.\u003csup\u003e4\u003c/sup\u003e\u003c/br\u003eDefault: `always`                                   |\n| UI       | `hide-args`         | Hide comma-separated list of CLI arguments from the command input.\u003csup\u003e6\u003c/sup\u003e\u003c/br\u003eDefault: `detailed-exitcode,parallelism,lock,out,var=` |\n| UI       | `show-args`         | Show comma-separated list of CLI arguments in the command input.\u003csup\u003e6\u003c/sup\u003e\u003c/br\u003eDefault: `workspace`                                     |\n\n\u003c/br\u003e\n\n1. Both `command: plan` and `command: apply` include: `init`, `fmt` (with `format: true`), `validate` (with `validate: true`), and `workspace` (with `arg-workspace`) commands rolled into it automatically.\u003c/br\u003e\n  To separately run checks and/or generate outputs only, `command: init` can be used.\u003c/br\u003e\u003c/br\u003e\n1. Originally intended for `merge_group` event trigger, `plan-parity: true` input helps to prevent stale apply within a series of workflow runs when merging multiple PRs.\u003c/br\u003e\u003c/br\u003e\n1. The secret string input for `plan-encrypt` can be of any length, as long as it's consistent between encryption (plan) and decryption (apply).\u003c/br\u003e\u003c/br\u003e\n1. The `on-change` option is true when the exit code of the last TF command is non-zero (ensure `terraform_wrapper`/`tofu_wrapper` is set to `false`).\u003c/br\u003e\u003c/br\u003e\n1. The default behavior of `comment-method` is to update the existing PR comment with the latest plan/apply output, making it easy to track changes over time through the comment's revision history.\u003c/br\u003e\u003c/br\u003e\n  [![PR comment revision history comparing plan and apply outputs.](/.github/assets/revisions.png)](https://raw.githubusercontent.com/op5dev/tf-via-pr/refs/heads/main/.github/assets/revisions.png \"View full-size image.\")\u003c/br\u003e\u003c/br\u003e\n1. It can be desirable to hide certain arguments from the last run command input to prevent exposure in the PR comment (e.g., sensitive `arg-var` values). Conversely, it can be desirable to show other arguments even if they are not in last run command input (e.g., `arg-workspace` or `arg-backend-config` selection).\n\n\u003c/br\u003e\n\n### Arguments\n\n\u003e [!NOTE]\n\u003e\n\u003e - Arguments are passed to the appropriate TF command(s) automatically, whether that's `fmt`, `init`, `validate`, `plan`, or `apply`.\u003c/br\u003e\n\u003e - For repeated arguments like `arg-var`, `arg-var-file`, `arg-backend-config`, `arg-replace` and `arg-target`, use commas to separate multiple values (e.g., `arg-var: key1=value1,key2=value2`).\n\n\u003c/br\u003e\n\nApplicable to respective \"plan\" and \"apply\" `command` inputs (\"init\" included).\n\n| Name                      | CLI Argument                             |\n| ------------------------- | ---------------------------------------- |\n| `arg-auto-approve`        | `-auto-approve`                          |\n| `arg-backend-config`      | `-backend-config`                        |\n| `arg-backend`             | `-backend`                               |\n| `arg-backup`              | `-backup`                                |\n| `arg-chdir`               | `-chdir`\u003c/br\u003eAlias: `working-directory`  |\n| `arg-compact-warnings`    | `-compact-warnings`                      |\n| `arg-concise`             | `-concise`                               |\n| `arg-destroy`             | `-destroy`                               |\n| `arg-detailed-exitcode`   | `-detailed-exitcode`\u003c/br\u003eDefault: `true` |\n| `arg-force-copy`          | `-force-copy`                            |\n| `arg-from-module`         | `-from-module`                           |\n| `arg-generate-config-out` | `-generate-config-out`                   |\n| `arg-get`                 | `-get`                                   |\n| `arg-lock-timeout`        | `-lock-timeout`                          |\n| `arg-lock`                | `-lock`                                  |\n| `arg-lockfile`            | `-lockfile`                              |\n| `arg-migrate-state`       | `-migrate-state`                         |\n| `arg-parallelism`         | `-parallelism`                           |\n| `arg-plugin-dir`          | `-plugin-dir`                            |\n| `arg-reconfigure`         | `-reconfigure`                           |\n| `arg-refresh-only`        | `-refresh-only`                          |\n| `arg-refresh`             | `-refresh`                               |\n| `arg-replace`             | `-replace`                               |\n| `arg-state-out`           | `-state-out`                             |\n| `arg-state`               | `-state`                                 |\n| `arg-target`              | `-target`                                |\n| `arg-upgrade`             | `-upgrade`                               |\n| `arg-var-file`            | `-var-file`                              |\n| `arg-var`                 | `-var`                                   |\n| `arg-workspace`           | `-workspace`\u003c/br\u003eAlias: `TF_WORKSPACE`   |\n\n\u003c/br\u003e\n\nApplicable only when `format: true`.\n\n| Name            | CLI Argument                     |\n| --------------- | -------------------------------- |\n| `arg-check`     | `-check`\u003c/br\u003eDefault: `true`     |\n| `arg-diff`      | `-diff`\u003c/br\u003eDefault: `true`      |\n| `arg-list`      | `-list`                          |\n| `arg-recursive` | `-recursive`\u003c/br\u003eDefault: `true` |\n| `arg-write`     | `-write`                         |\n\n\u003c/br\u003e\n\nApplicable only when `validate: true`.\n\n| Name                 | CLI Argument      |\n| -------------------- | ----------------- |\n| `arg-no-tests`       | `-no-tests`       |\n| `arg-test-directory` | `-test-directory` |\n\n\u003c/br\u003e\n\n## Outputs\n\n| Type     | Name           | Description                                   |\n| -------- | -------------- | --------------------------------------------- |\n| Artifact | `plan-id`      | ID of the plan file artifact.                 |\n| Artifact | `plan-url`     | URL of the plan file artifact.                |\n| CLI      | `command`      | Input of the last TF command.                 |\n| CLI      | `diff`         | Diff of changes, if present (truncated).      |\n| CLI      | `exitcode`     | Exit code of the last TF command.             |\n| CLI      | `result`       | Result of the last TF command (truncated).    |\n| CLI      | `summary`      | Summary of the last TF command.               |\n| Workflow | `check-id`     | ID of the check run.                          |\n| Workflow | `comment-body` | Body of the PR comment.                       |\n| Workflow | `comment-id`   | ID of the PR comment.                         |\n| Workflow | `job-id`       | ID of the workflow job.                       |\n| Workflow | `run-url`      | URL of the workflow run.                      |\n| Workflow | `identifier`   | Unique name of the workflow run and artifact. |\n\n\u003c/br\u003e\n\n## Security\n\nView [security policy and reporting instructions](SECURITY.md).\n\n\u003e [!TIP]\n\u003e\n\u003e Pin your workflow version to a specific release tag or SHA to harden your CI/CD pipeline security against supply chain attacks.\n\n\u003c/br\u003e\n\n## Changelog\n\nView [all notable changes](https://github.com/op5dev/tf-via-pr/releases \"Releases.\") to this project in [Keep a Changelog](https://keepachangelog.com \"Keep a Changelog.\") format, which adheres to [Semantic Versioning](https://semver.org \"Semantic Versioning.\").\n\n\u003e [!TIP]\n\u003e\n\u003e All forms of **contribution are very welcome** and deeply appreciated for fostering open-source projects.\n\u003e\n\u003e - [Create a PR](https://github.com/op5dev/tf-via-pr/pulls \"Create a pull request.\") to contribute changes you'd like to see.\n\u003e - [Raise an issue](https://github.com/op5dev/tf-via-pr/issues \"Raise an issue.\") to propose changes or report unexpected behavior.\n\u003e - [Open a discussion](https://github.com/op5dev/tf-via-pr/discussions \"Open a discussion.\") to discuss broader topics or questions.\n\u003e - [Become a stargazer](https://github.com/op5dev/tf-via-pr/stargazers \"Become a stargazer.\") if you find this project useful.\n\n\u003c/br\u003e\n\n### To-Do\n\n- Handling of inputs which contain space(s) (e.g., `working-directory: path to/directory`).\n- Handling of comma-separated inputs which contain comma(s) (e.g., `arg-var: token=1,2,3`)—use `TF_CLI_ARGS` [workaround](https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_cli_args-and-tf_cli_args_name).\n\n\u003c/br\u003e\n\n## License\n\n- This project is licensed under the permissive [Apache License 2.0](LICENSE \"Apache License 2.0.\").\n- All works herein are my own, shared of my own volition, and [contributors](https://github.com/op5dev/tf-via-pr/graphs/contributors \"Contributors.\").\n- Copyright 2016-present [Rishav Dhar](https://github.com/rdhar \"Rishav Dhar's GitHub profile.\") — All wrongs reserved.\n","funding_links":["https://github.com/sponsors/rdhar","https://github.com/sponsors/op5dev"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fop5dev%2Ftf-via-pr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fop5dev%2Ftf-via-pr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fop5dev%2Ftf-via-pr/lists"}