{"id":25874647,"url":"https://github.com/lyft/atlantis","last_synced_at":"2026-03-15T08:06:03.379Z","repository":{"id":38443525,"uuid":"300001859","full_name":"lyft/atlantis","owner":"lyft","description":"Terraform automation for GitHub PRs (private fork of runatlantis/atlantis)","archived":false,"fork":false,"pushed_at":"2025-09-25T17:23:20.000Z","size":46287,"stargazers_count":52,"open_issues_count":2,"forks_count":6,"subscribers_count":37,"default_branch":"main","last_synced_at":"2025-11-22T15:03:56.335Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"runatlantis/atlantis","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lyft.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-09-30T17:39:02.000Z","updated_at":"2025-11-05T16:44:26.000Z","dependencies_parsed_at":"2025-09-24T22:25:00.042Z","dependency_job_id":null,"html_url":"https://github.com/lyft/atlantis","commit_stats":null,"previous_names":[],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/lyft/atlantis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyft%2Fatlantis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyft%2Fatlantis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyft%2Fatlantis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyft%2Fatlantis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lyft","download_url":"https://codeload.github.com/lyft/atlantis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyft%2Fatlantis/sbom","scorecard":{"id":607172,"data":{"date":"2025-08-11","repo":{"name":"github.com/lyft/atlantis","commit":"de7afd722526966bd3d721a315f4b18d631406b5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/lint.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Warn: no topLevel permission defined: .github/workflows/update_parent.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/lint.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/lint.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/lint.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/update_parent.yaml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/update_parent.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update_parent.yaml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/update_parent.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update_parent.yaml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/lyft/atlantis/update_parent.yaml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:2","Warn: containerImage not pinned by hash: Dockerfile.dev:1: pin your Docker image by updating ghcr.io/runatlantis/atlantis:v0.19.7 to ghcr.io/runatlantis/atlantis:v0.19.7@sha256:7fbcdf7574d9652d2e2769c1cc8fb8deac1d38e0e499ec8e4182140de4743eeb","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":0,"reason":"41 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0635","Warn: Project is vulnerable to: GO-2022-0646","Warn: Project is vulnerable to: GO-2022-1178 / GHSA-h4q8-96p6-jcgr","Warn: Project is vulnerable to: GO-2020-0017 / GHSA-w73w-5m7g-f7qc","Warn: Project is vulnerable to: GO-2022-0752 / GHSA-wxj3-qwv4-cvfm","Warn: Project is vulnerable to: GO-2022-0636 / GHSA-8w94-cf6g-c8mg","Warn: Project is vulnerable to: GO-2022-0630 / GHSA-5qgp-p5jc-w2rm","Warn: Project is vulnerable to: GO-2022-0705 / GHSA-qmmc-jppf-32wv","Warn: Project is vulnerable to: GO-2022-0640 / GHSA-997c-fj8j-rq5h","Warn: Project is vulnerable to: GO-2022-0751 / GHSA-vj3f-3286-r4pf","Warn: Project is vulnerable to: GO-2022-0649 / GHSA-g7v2-2qxx-wjrw","Warn: Project is vulnerable to: GO-2022-0708 / GHSA-v4h8-794j-g8mm","Warn: Project is vulnerable to: GHSA-qrqr-3x5j-2xw9","Warn: Project is vulnerable to: GHSA-j249-ghv5-7mxv","Warn: Project is vulnerable to: GHSA-6hwg-w5jg-9c6x","Warn: Project is vulnerable to: GO-2024-2521","Warn: Project is vulnerable to: GO-2024-2500 / GHSA-3fwx-pjgw-3558","Warn: Project is vulnerable to: GO-2024-2913 / GHSA-v994-f8vw-g7j4","Warn: Project is vulnerable to: GO-2024-2914 / GHSA-xmmx-7jpf-fx42","Warn: Project is vulnerable to: GO-2022-0390 / GHSA-2mm7-x5h6-5pvq","Warn: Project is vulnerable to: GO-2022-0985 / GHSA-rc4r-wh2q-q6c4","Warn: Project is vulnerable to: GO-2022-1107 / GHSA-vp35-85q5-9f25","Warn: Project is vulnerable to: GHSA-jq35-85cj-fj4p","Warn: Project is vulnerable to: GHSA-mq39-4gv4-mvpx","Warn: Project is vulnerable to: GO-2024-2512 / GHSA-xw73-rw38-6vjc","Warn: Project is vulnerable to: GO-2025-3829 / GHSA-4vq8-7jfc-9cvp","Warn: Project is vulnerable to: GO-2024-3250 / GHSA-29wx-vh33-7x7r","Warn: Project is vulnerable to: GO-2025-3553 / GHSA-mh63-6h87-95cp","Warn: Project is vulnerable to: GO-2024-2800 / GHSA-q64h-39hv-4cf7","Warn: Project is vulnerable to: GO-2024-2948 / GHSA-xfhp-jf8p-mh5w","Warn: Project is vulnerable to: GO-2024-2947 / GHSA-v6v8-xj6m-xwqh","Warn: Project is vulnerable to: GO-2025-3462 / GHSA-q9w6-cwj4-gf4p","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2025-3488 / GHSA-6v2p-p543-phr9","Warn: Project is vulnerable to: GO-2023-2153 / GHSA-m425-mq94-257g / GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2024-2611 / GHSA-8r3f-844c-mc37"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T01:52:32.197Z","repository_id":38443525,"created_at":"2025-08-21T01:52:32.197Z","updated_at":"2025-08-21T01:52:32.197Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30538268,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-15T07:17:37.589Z","status":"ssl_error","status_checked_at":"2026-03-15T07:17:31.738Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2025-03-02T09:28:36.658Z","updated_at":"2026-03-15T08:06:03.367Z","avatar_url":"https://github.com/lyft.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Atlantis\n\nThis was forked from [runatlantis/atlantis](https://github.com/runatlantis/atlantis) at [v0.17.3](https://github.com/runatlantis/atlantis/releases/tag/v0.17.3)\n\nSince then this version has diverged significantly from upstream and was therefore detached.\n\n:warning: This repo is still still contains a lot of code from upstream which is slowly being phased out as we test our implementation in production.  It is not ready for general consumption.\n\n## What's different?\n\n**Functional Differences**\n* Applies automatically occur AFTER merging a PR (In PR applies can be enabled for certain repos but this shouldn't be used primarily)\n* Applies are queued for a given root as commits are merged in\n* No PR locks; concurrent plans can be run against the same root\n\n**Non Functional Differences**\n* 2 microservices: Gateway event proxy + Worker\n* Improved reliability and availability since you can have n number of proxies or workers or both.\n* [Temporal](https://docs.temporal.io/) is used heavily to maintain execution state, provide host-level routing and provide fault tolerance to our workflows.\n\n## Architecture\n\nMost of the new code can be found in `server/neptune`. Neptune is the codename for our rebuild of Atlantis.  Outside of that package is mostly old code which we are removing as we deprecate upstream/legacy workers. \n\nCurrently, Atlantis can operate in 3 modes based on the configuration passed in:\n* Gateway\n* Legacy Worker (This is currently being deprecated as we cutover fully to the Temporal worker within Lyft)\n* Temporal Worker\n\nIn order for Neptune to work correctly, all of these must exist.  \n\n## Gateway\nReceives webhook events from github and acts on them accordingly.  Gateway is stateless however each request does spin up a go routine that clones a repository to disk.  This is the primary bottleneck here.\n\n### Events\n\n#### Push\n* Clone repository on disk and fetch root configuration.\n* Get the latest diff and determine if any roots have changed.\n* Start/signal the deploy temporal workflow for each root that requires processing.\n\n#### Pull\n* Clone repository on disk and fetch root configuration.\n* Get the latest diff of the entire PR and determine if any roots have changed.\n* Proxy event to legacy worker if any roots require processing.\n\n#### Check Run\n* Determine which action is being triggered.  We have custom buttons that are thrown up on the check run depending on the situation.  As of now there are 2 types of events we are looking for:  `unlocked` and `plan_review`.  When we receive both these events, we signal our deploy workflow and terraform workflow respectively.\n* Listens for the GH provided `Re-run failed checkruns` button selection and signals our deploy workflow that we are attempting to add the previously failing revision back into the deploy queue for a rerun attempt. This is similar to the check suite event described below, but the key difference here is this request only reruns attempts where a checkrun has failed. \n\n#### Check Suite\n* Listens for GH provided `Re-run all checkruns` button selection events and signals our deploy workflows that we are attempting add all off the modified roots within a revision back into their respective deploy queues for a rerun attempt, regardless of success or failure status. Note that we only support rerun attempts if a revision meets the following criteria:\n  - check run request comes from a revision on the default branch of a repo (force applies are not allowed)\n  - revision is identical to the latest persisted deployment attempt for a specific root\n## Legacy Worker\nResponsible for speculative planning and policy checking within the PR.  This code is relatively untouched from upstream atlantis and should eventually be nuked in favor of Temporal workflows. \n\n## Temporal Worker\nResponsible for running 3 primary processes:\n* HTTP Server for viewing realtime logs\n* Terraform Task Queue Worker for running Terraform Workflow tasks\n* Deploy Task Queue Worker for running Deploy Workflow tasks\n\n## Temporal Workflows\n\n### Deploy\n\nDeploy workflows are run on the granularity of a single repository root.  It follows the ID pattern below:\n```\n\u003cOWNER/REPO\u003e||\u003cROOT_NAME\u003e\n```\n\nThe following is a high level diagram of how this workflow is structured:\n\n```\n                                           ┌─────────────┐\n                                           │             │\n                                           │  deployment │\n                                           │    store    │\n┌─────────────────┐                        │             │\n│     select      │                        └────▲───┬────┘\n│                 │                             │   │\n│                 │                             │   │\n│                 │                             │   │\n├┬───────────────┬┤                        ┌────┴───▼──────┐\n││revision signal││    ┌──────────────┐    │               │\n││   channel     │┼────►priority queue├────► queue worker  │\n│┼───────────────┼│    └──────────────┘    │               │\n│┼───────────────┼│                        └──────┬────────┘\n││ timeout timer ││                               │\n│┼───────────────┼│                               │\n└─────────────────┘                       ┌───────▼─────────┐\n                                          │     select      │\n                                          │                 │\n                                          │                 │\n                                          │                 │\n                                          ├┬───────────────┬┤\n                                          ││ queue::CanPop │┼───────────────┐\n                                          ││               ││               │\n                                          │┼───────────────┼│      ┌────────▼─────────┐\n                                          │┼───────────────┼│      │      select      │\n                                          ││ unlock signal ││      │                  │         ┌─────────┐\n                                          │┼───────────────┼│      │                  │         │ Github  │\n                                          └─────────────────┘      │                  │         └▲────────┘\n                                                                   ├┬────────────────┬┤          │\n                                                                   ││ state change   ││          │\n                                                                   ││ signal channel │┼──────────┴───┐\n                                                                   │┼────────────────┼│              │\n                                                                   │┼────────────────┼│              │\n                                                                   ││ child workflow ││           ┌──▼──┐\n                                                                   │┼────────────────┼│           │ SNS │\n                                                                   └──────────────────┘           └─────┘\n```\n\nThe deploy workflow is responsible for a few things:\n* Receiving revisions to deploy from gateway.\n* Queueing up successive terraform workflows.\n* Updating github check run state based on the in-progress terraform workflow\n\nIn order to receive revisions our main workflow thread listens to a dedicated channel.  If we haven't received a new revision in 60 minutes and our queue is empty, we instigate a shutdown of the workflow in its entirety.\n\n#### Queue\n\nThe queue is modeled as a priority queue where manually triggered deployments always happen before merge triggered deployments.  This queue can be in a locked state if the last deployment that happened was triggered manually.  The queue lock applies only to deployments that have been triggered via merging and can be unlocked through the corresponding check run of an item that is blocked.  \n\nItems can only be popped of the queue if the queue is unlocked OR if the queue is locked but contains a manually triggered deployment.  \n\nBy default, a new workflow starts up in an unlocked state.\n\n#### Queue Worker\nUpon workflow startup, we start a queue worker go routine which is responsible for popping off items from our queue when it can, and listening for unlock signals from gateway.\n\nThe worker also maintains local state on the latest deployment that has happened. This is used for validating that new revisions intended for deploy are ahead of the latest deployed revisions.  Once each deployment is complete, the worker persists this information in the configured blob store.  The worker only fetches from this blob store on workflow startup and maintains the information locally for lifetime of its execution.\n\nA deploy consists of executing a terraform workflow.  The worker blocks on execution of this \"child\" workflow and listens for state changes via a dedicated signal channel.  Once the child is complete, we stop listening to this signal channel and move on.\n\n#### State Signal\n\nState changes are reflected in the github checks UI (ie. plan in progress, plan failed, apply in progress etc.).  A single check run is used to represent the deployment state.  The check run state is indicative of the completion state of the deployment and the details of the deployment itself are rendered in the check run details section.\n\nState changes for apply jobs specifically are sent to SNS for internal auditing purposes.\n\n### Terraform\n\nThe Terraform workflow runs on the granularity of a single deployment.  It's identifier is the deployment's identifier which is randomly generated in the Deploy Workflow.  Note: this means a single revision can be tied to multiple deployments.\n\nThe terraform workflow is stateful due to the fact that it keeps data on disk and references it throughout the workflow.  Cleanup of that data only happens when that workflow is complete.  In order to ensure this statefulness, the terraform workflow is aware of the worker it's running on and fetches this information as part of the first activity.  Each successive activity task takes place on the same task queue.  \n\nFollowing this:\n* The workflow clones a repository and stores it on disk\n* Generates a Job ID for the first set of Terraform Operations\n* Runs Terraform Init followed by Terraform Plan\n\nBefore and after this job, the workflow signals it's parent execution with a state object.  At this point, the workflow either blocks on a dedicated plan review channel, or proceeds to the apply under some criteria. Atm this is only if there are no changes in the plan.\n\nPlan review signals are received directly by this workflow from gateway which pulls the workflow ID from the check run's `External ID` field.  \n\nIf the plan is approved, the workflow proceeds with the apply, all the while updating the parent execution with the status, before exiting the workflow.\nIf the plan is rejected or times out (1 week timeout on plan reviews), the parent is notified and the workflow exits.\n\n#### Retries\n\nThe workflow itself has no retries configured.  All activities use the default retry policy except for Terraform Activities.  Terraform Activities throw up a `TerraformClientError` if there is an error from the binary itself.  This error is configured to be non-retryable since most of the time this is a user error.  \n\nFor Terraform Applies, timeouts are not retried. Timeouts can happen from exceeding the ScheduleToClose threshold or from lack of heartbeat for over a minute.  Instead of retrying the apply, which can have unpredictable results, we signal our parent that there has been a timeout and this is surfaced to the user.\n\n#### Heartbeats\n\nSince Terraform activities can run long, we send hearbeats at 5 second intervals. If 1 minute goes by without receiving a hearbeat, temporal will assume the worker node is down and the configured retry policy will be run.\n\n#### Job Logs\n\nTerraform operation logs are streamed to the local server process using go channels.  Once the operation is complete, the channel is closed and the receiving process persists the logs to the configured job store.  \n\n## Developing\n\n### Running Atlantis Locally\n* Clone the repo from https://github.com/runatlantis/atlantis/\n* Compile Atlantis:\n    ```\n    go install\n    ```\n* Run Atlantis:\n    ```\n    atlantis server --gh-user \u003cyour username\u003e --gh-token \u003cyour token\u003e --repo-allowlist \u003cyour repo\u003e --gh-webhook-secret \u003cyour webhook secret\u003e --log-level debug\n    ```\n    If you get an error like `command not found: atlantis`, ensure that `$GOPATH/bin` is in your `$PATH`.\n\n### Running Atlantis With Local Changes\n\nThe atlantis worker can't technically be run yet locally given it's dependency on sqs.\nHowever, Docker compose is set up to run a gateway, a temporal worker,\ntemporalite and ngrok all in the same network.\nNgrok allows us to expose localhost to the public internet in order to test github app integrations.\n\nThere is some setup that is required in order to have your containers running and receiving webhooks.\n\n1. Setup your own personal github organization and test github app.\n2. Install this app to a test repo within your organization with the following configuration.\n    * Repository permissions\n      * Checks - Read and Write\n      * Commit statuses - Read and Write\n      * Contents - Read and Write\n      * Issues - Read and Write\n      * Pull requests - Read and Write\n    * Organization permissions\n      * Members - Read-only\n    * Subscribe to events\n      * Create\n      * Issue comment\n      * Pull request\n      * Pull request review\n      * Push\n    * Webhook - This will be setup later when you start ngrok and get the webhook URL, until then fill out any value to get past the app create validation.\n3. Download the key file and save it to `~/.ssh` directory. Note: `~/.ssh` is mounted to allow for referencing any local ssh keys.\n\n4. Create the following files:\n`~/atlantis-gateway.env`\n```sh\nATLANTIS_PORT=4143\nATLANTIS_GH_APP_ID=\u003cFILL THIS IN\u003e\nATLANTIS_GH_APP_KEY_FILE=/.ssh/your-key-file.pem\nATLANTIS_GH_WEBHOOK_SECRET=\u003cFILL THIS IN\u003e\nATLANTIS_GH_APP_SLUG=\u003cFILL THIS IN\u003e\n\n# The github organization the feature flag repo resides\nATLANTIS_FF_OWNER=\u003cFILL THIS IN\u003e\n# Name of the feature flag repo\nATLANTIS_FF_REPO=\u003cFILL THIS IN\u003e\n# The path to the flags.yaml file\nATLANTIS_FF_PATH=\u003cFILL THIS IN\u003e\n\nATLANTIS_DATA_DIR=/tmp/\nATLANTIS_LYFT_MODE=gateway\nATLANTIS_REPO_CONFIG=/generated/repo-config.yaml\nATLANTIS_WRITE_GIT_CREDS=true\nATLANTIS_ENABLE_POLICY_CHECKS=true\nATLANTIS_ENABLE_DIFF_MARKDOWN_FORMAT=true\n\nATLANTIS_REPO_ALLOWLIST=\u003cFILL THIS IN\u003e\nALLOWED_REPOS=\u003cFILL THIS IN\u003e\n```\n\n`~/atlantis-temporalworker.env`\n```sh\nATLANTIS_PORT=4142\nATLANTIS_GH_APP_ID=\u003cFILL THIS IN\u003e\nATLANTIS_GH_APP_KEY_FILE=/.ssh/your-key-file.pem\nATLANTIS_GH_WEBHOOK_SECRET=\u003cFILL THIS IN\u003e\nATLANTIS_GH_APP_SLUG=\u003cFILL THIS IN\u003e\nATLANTIS_FF_OWNER=\u003cFILL THIS IN\u003e\nATLANTIS_FF_REPO=\u003cFILL THIS IN\u003e\nATLANTIS_FF_PATH=\u003cFILL THIS IN\u003e\nATLANTIS_DATA_DIR=/tmp/\nATLANTIS_LYFT_MODE=temporalworker\nATLANTIS_REPO_CONFIG=/generated/repo-config.yaml\nATLANTIS_WRITE_GIT_CREDS=true\nATLANTIS_ENABLE_POLICY_CHECKS=true\nATLANTIS_ENABLE_DIFF_MARKDOWN_FORMAT=true\nATLANTIS_REPO_ALLOWLIST=\u003cFILL THIS IN\u003e\nALLOWED_REPOS=\u003cFILL THIS IN\u003e\nATLANTIS_DEFAULT_TF_VERSION=1.2.5\n```\n\nOnce these steps are complete, everything should startup as normal. You just need to run:\n\n```\nmake build-service\ndocker-compose build\ndocker-compose up\n```\n\nIn order to have events routed to gateway, you'll need to visit `http://localhost:4040/` and copy the https url into your GitHub app.  \n\nIn order to see the temporal ui visit `http://localhost:8233/`.\n\n### Rebuilding\n\nIf the ngrok container is restarted, the url will change which is a hassle. Fortunately, when we make a code change, we can rebuild and restart the atlantis container easily without disrupting ngrok.\n\ne.g.\n\n```\nmake build-service\ndocker-compose up --detach --build\n```\n\n## Running Tests Locally:\n\n`make test`. If you want to run the integration tests that actually run real `terraform` commands, run `make test-all`.\n\n## Running Tests In Docker:\n```\ndocker run --rm -v $(pwd):/go/src/github.com/runatlantis/atlantis -w /go/src/github.com/runatlantis/atlantis ghcr.io/runatlantis/testing-env:latest make test\n```\n\nOr to run the integration tests\n```\ndocker run --rm -v $(pwd):/go/src/github.com/runatlantis/atlantis -w /go/src/github.com/runatlantis/atlantis ghcr.io/runatlantis/testing-env:latest make test-all\n```\n\n## Calling Your Local Atlantis From GitHub\n- Create a Github organization by following the steps in this [tutorial](https://docs.github.com/en/organizations/collaborating-with-groups-in-organizations/creating-a-new-organization-from-scratch).\n- In the homepage of your organization, navigate to the settings, developer settings and finally to Github Apps. \n- Click on new Github App. Set the homepage URL to https://www.atlantis.com and the setup the webhook URL as described in the steps below. \n- Under Repository Permissions, set it to following: \n```\n\t- Metadata: Read-Only \n\t- Pull Requests: Read and Write\n\t- Commit Statuses: Read and Write\n```\nSimilarly, subscribe for the following events: \n```\n\t- Pull Request\n\t- Issue Comment\n```\n- Create a test terraform repository in your GitHub.\n- Create a personal access token for Atlantis. See [Create a GitHub token](https://github.com/runatlantis/atlantis/tree/master/runatlantis.io/docs/access-credentials.md#generating-an-access-token).\n- Start Atlantis in server mode using that token:\n```\natlantis server --gh-user \u003cyour username\u003e --gh-token \u003cyour token\u003e --repo-allowlist \u003cyour repo\u003e --gh-webhook-secret \u003cyour webhook secret\u003e --log-level debug\n```\n- Download ngrok from https://ngrok.com/download. This will enable you to expose Atlantis running on your laptop to the internet so GitHub can call it.\n- When you've downloaded and extracted ngrok, run it on port `4141`:\n```\nngrok http 4141\n```\n- Create a Webhook in your repo and use the `https` url that `ngrok` printed out after running `ngrok http 4141`. Be sure to append `/events` so your webhook url looks something like `https://efce3bcd.ngrok.io/events`. See [Add GitHub Webhook](https://github.com/runatlantis/atlantis/blob/master/runatlantis.io/docs/configuring-webhooks.md#configuring-webhooks).\n- Create a pull request and type `atlantis help`. You should see the request in the `ngrok` and Atlantis logs and you should also see Atlantis comment back.\n\n## Code Style\n### Logging\n- `ctx.Log` should be available in most methods. If not, pass it down.\n- levels:\n    - debug is for developers of atlantis\n    - info is for users (expected that people run on info level)\n    - warn is for something that might be a problem but we're not sure\n    - error is for something that's definitely a problem\n- **ALWAYS** logs should be all lowercase (when printed, the first letter of each line will be automatically capitalized)\n- **ALWAYS** quote any string variables using %q in the fmt string, ex. `ctx.Log.Infof(\"cleaning clone dir %q\", dir)` =\u003e `Cleaning clone directory \"/tmp/atlantis/lkysow/atlantis-terraform-test/3\"`\n- **NEVER** use colons \"`:`\" in a log since that's used to separate error descriptions and causes\n  - if you need to have a break in your log, either use `-` or `,` ex. `failed to clean directory, continuing regardless`\n\n### Errors\n- **ALWAYS** use lowercase unless the word requires it\n- **ALWAYS** use `errors.Wrap(err, \"additional context...\")\"` instead of `fmt.Errorf(\"additional context: %s\", err)`\nbecause it is less likely to result in mistakes and gives us the ability to trace call stacks\n- **NEVER** use the words \"error occurred when...\", or \"failed to...\" or \"unable to...\", etc. Instead, describe what was occurring at\ntime of the error, ex. \"cloning repository\", \"creating AWS session\". This will prevent errors from looking like\n```\nError setting up workspace: failed to run git clone: could find git\n```\n\nand will instead look like\n```\nError: setting up workspace: running git clone: no executable \"git\"\n```\nThis is easier to read and more consistent\n\n### Testing\n- place tests under `{package under test}_test` to enforce testing the external interfaces\n- if you need to test internally i.e. access non-exported stuff, call the file `{file under test}_internal_test.go`\n- use our testing utility for easier-to-read assertions: `import . \"github.com/runatlantis/atlantis/testing\"` and then use `Assert()`, `Equals()` and `Ok()`\n\n### Mocks\nWe write our own mocks to reduce dependencies.  Most of the old code uses [pegomock](https://github.com/petergtz/pegomock) which is unmaintained.  We shouldn't use this for any new changes going forward.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyft%2Fatlantis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flyft%2Fatlantis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyft%2Fatlantis/lists"}