Merge branch 'main' into adr/project-locks
@@ -1,30 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
e2e:
|
||||
docker:
|
||||
- image: cimg/go:1.21 # If you update this, update it in the Makefile too
|
||||
environment:
|
||||
# This version of TF will be downloaded before Atlantis is started.
|
||||
# We do this instead of setting --default-tf-version because setting
|
||||
# that flag starts the download asynchronously so we'd have a race
|
||||
# condition.
|
||||
# renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
|
||||
TERRAFORM_VERSION: 1.6.5
|
||||
steps:
|
||||
- checkout
|
||||
- run: make build-service
|
||||
# We don't run e2e tests on fork PRs because they don't have access to the secret env vars.
|
||||
- run: if [ -z "${CIRCLE_PR_REPONAME}" ]; then ./scripts/e2e.sh; fi
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
branch:
|
||||
jobs:
|
||||
- e2e:
|
||||
context:
|
||||
- atlantis-e2e-tests
|
||||
filters:
|
||||
branches:
|
||||
# Ignore fork PRs since they don't have access to
|
||||
# the atlantis-e2e-tests context (and also doc PRs).
|
||||
ignore: /(pull\/\d+)|(docs\/.*)/
|
||||
@@ -1,5 +1,6 @@
|
||||
*
|
||||
!cmd/
|
||||
!scripts/download-release.sh
|
||||
!server/
|
||||
!testdrive/
|
||||
!main.go
|
||||
|
||||
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 3
|
||||
trim_trailing_whitespace = false
|
||||
11
.gitattributes
vendored
@@ -1,11 +1,2 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.go text
|
||||
*.json text
|
||||
*.yml text
|
||||
*.yaml text
|
||||
*.sh text
|
||||
*.tf text
|
||||
* text=auto eol=lf
|
||||
|
||||
11
.github/labeler.yml
vendored
@@ -14,7 +14,8 @@ docs:
|
||||
|
||||
github-actions:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '.github/**'
|
||||
- any-glob-to-any-file:
|
||||
- '.github/workflows/*.yml'
|
||||
|
||||
go:
|
||||
- changed-files:
|
||||
@@ -40,6 +41,10 @@ provider/gitlab:
|
||||
|
||||
website:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'runatlantis.io/.vuepress/**/*'
|
||||
- any-glob-to-any-file: 'runatlantis.io/.vitepress/**/*'
|
||||
- any-glob-to-any-file: 'package.json'
|
||||
- any-glob-to-any-file: 'yarn.lock'
|
||||
- any-glob-to-any-file: 'package-lock.json'
|
||||
|
||||
blog:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'runatlantis.io/blog/**'
|
||||
|
||||
5
.github/release.yml
vendored
@@ -5,6 +5,7 @@ changelog:
|
||||
- github-actions
|
||||
authors:
|
||||
- octocat
|
||||
- renovate[bot]
|
||||
categories:
|
||||
- title: Breaking Changes 🛠
|
||||
labels:
|
||||
@@ -15,9 +16,9 @@ changelog:
|
||||
- Semver-Minor
|
||||
- enhancement
|
||||
- feature
|
||||
- title: Bug fixes
|
||||
- title: Bug fixes 🐛
|
||||
labels:
|
||||
- bug
|
||||
- title: Other Changes
|
||||
- title: Other Changes 🔄
|
||||
labels:
|
||||
- "*"
|
||||
|
||||
163
.github/renovate.json5
vendored
@@ -1,108 +1,119 @@
|
||||
{
|
||||
extends: [
|
||||
"config:base",
|
||||
"schedule:daily",
|
||||
'config:best-practices',
|
||||
':separateMultipleMajorReleases',
|
||||
'schedule:daily',
|
||||
],
|
||||
commitMessageSuffix: " in {{packageFile}}",
|
||||
commitMessageSuffix: ' in {{packageFile}}',
|
||||
dependencyDashboardAutoclose: true,
|
||||
automerge: true,
|
||||
baseBranches: ["main", "/^release\-.*/"],
|
||||
platformAutomerge: true,
|
||||
labels: ["dependencies"],
|
||||
postUpdateOptions: [
|
||||
"gomodTidy",
|
||||
"gomodUpdateImportPaths",
|
||||
"yarnDedupeHighest",
|
||||
baseBranches: [
|
||||
'main',
|
||||
'/^release-.*/',
|
||||
],
|
||||
platformAutomerge: true,
|
||||
labels: [
|
||||
'dependencies',
|
||||
],
|
||||
postUpdateOptions: [
|
||||
'gomodTidy',
|
||||
'gomodUpdateImportPaths',
|
||||
'npmDedupe',
|
||||
],
|
||||
// needed so e2e tests do not stomp over each other
|
||||
prHourlyLimit: 1,
|
||||
lockFileMaintenance: {
|
||||
enabled: true,
|
||||
},
|
||||
minimumReleaseAge: '5 days',
|
||||
osvVulnerabilityAlerts: true,
|
||||
vulnerabilityAlerts: {
|
||||
enabled: true,
|
||||
labels: [
|
||||
"security",
|
||||
'security',
|
||||
],
|
||||
},
|
||||
packageRules: [
|
||||
// For vuepress
|
||||
{
|
||||
"matchPackageNames": ["vuepress", "@vuepress/client", "@vuepress/markdown", "@vuepress/utils"],
|
||||
"groupName": "vuepress",
|
||||
"allowedVersions": "!/pre.*$/",
|
||||
},
|
||||
// e2e test depends on testing/Dockefile testing-image which has conftest specific version.
|
||||
// to upgrade conftest versions, we need following PRs.
|
||||
// 1. update testing/Dockerfile conftest version
|
||||
// 2. update testing-env tag
|
||||
// 3. update e2e conftest version
|
||||
// This will allow conftest version updates in testing/Dockefile
|
||||
{
|
||||
matchPaths: ["testing/**"],
|
||||
matchPackagePatterns: ["conftest"],
|
||||
additionalBranchPrefix: "{{baseDir}}-",
|
||||
groupName: "conftest-testing",
|
||||
/*
|
||||
prBodyNotes: [
|
||||
":warning: Upgrade testing-env conftest and then upgrade other conftest versions for e2e :warning:",
|
||||
matchFileNames: [
|
||||
'package.json',
|
||||
],
|
||||
*/
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
ignorePaths: ["testing/**"],
|
||||
matchPackagePatterns: ["github-actions"],
|
||||
groupName: "github-",
|
||||
matchFileNames: [
|
||||
'testing/**',
|
||||
],
|
||||
additionalBranchPrefix: '{{packageFileDir}}-',
|
||||
groupName: 'conftest-testing',
|
||||
matchPackageNames: [
|
||||
'/conftest/',
|
||||
],
|
||||
},
|
||||
/*
|
||||
// This tag is currently latest so we can skip this check for now unless we need to pin it again.
|
||||
{
|
||||
// we need to upgrade testing-env on ci quickly
|
||||
matchPackageNames: ["ghcr.io/runatlantis/testing-env"],
|
||||
groupName: "testing-env-ci-test",
|
||||
schedule: ["every 1 hour after 00:00 and before 23:59 every day"],
|
||||
ignorePaths: [
|
||||
'testing/**',
|
||||
],
|
||||
groupName: 'github-',
|
||||
matchPackageNames: [
|
||||
'/github-actions/',
|
||||
],
|
||||
},
|
||||
*/
|
||||
{
|
||||
// use LTS node version for node docker image
|
||||
matchDatasources: ["docker"],
|
||||
matchPackageNames: ["node", "cimg/node"],
|
||||
versioning: "node",
|
||||
matchDatasources: [
|
||||
'docker',
|
||||
],
|
||||
matchPackageNames: [
|
||||
'node',
|
||||
'cimg/node',
|
||||
],
|
||||
versioning: 'node',
|
||||
},
|
||||
{
|
||||
matchPackageNames: [
|
||||
'go',
|
||||
'golang',
|
||||
],
|
||||
versioning: 'go',
|
||||
groupName: 'go',
|
||||
},
|
||||
],
|
||||
// https://docs.renovatebot.com/modules/manager/regex/
|
||||
regexManagers: [
|
||||
customManagers: [
|
||||
{
|
||||
fileMatch: ["(^|/)Dockerfile$", "(^|/)Dockerfile\\.[^/]*$"],
|
||||
matchStrings: [
|
||||
// example:
|
||||
// renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
|
||||
// ENV DEFAULT_TERRAFORM_VERSION=x.x.x
|
||||
"renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\sENV .*?_VERSION=(?<currentValue>.*)\\s",
|
||||
customType: 'regex',
|
||||
fileMatch: [
|
||||
'(^|/)Dockerfile$',
|
||||
'(^|/)Dockerfile\\.[^/]*$',
|
||||
],
|
||||
versioningTemplate: "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}",
|
||||
extractVersionTemplate: '^v(?<version>\\d+\\.\\d+\\.\\d+)',
|
||||
},
|
||||
{
|
||||
fileMatch: [".*go$"],
|
||||
matchStrings: [
|
||||
// example:
|
||||
// const ConftestVersion = "x.x.x" // renovate: datasource=github-releases depName=open-policy-agent/conftest
|
||||
"\\sconst .*Version = \"(?<currentValue>.*)\"\\s// renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s",
|
||||
],
|
||||
versioningTemplate: "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}",
|
||||
extractVersionTemplate: '^v(?<version>\\d+\\.\\d+\\.\\d+)',
|
||||
},
|
||||
{
|
||||
fileMatch: [".circleci/config.yml$"],
|
||||
matchStrings: [
|
||||
// example:
|
||||
// # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
|
||||
// TRRAFORM_VERSION: x.x.x
|
||||
"renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s.*?_VERSION: (?<currentValue>.*)\\s",
|
||||
// ENV DEFAULT_TERRAFORM_VERSION=x.x.x
|
||||
// # renovate: datasource=github-releases depName=open-policy-agent/conftest
|
||||
// ARG DEFAULT_CONFTEST_VERSION=x.x.x
|
||||
'renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s(ARG|ENV) .*?_VERSION=(?<currentValue>.*)\\s',
|
||||
],
|
||||
versioningTemplate: "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}",
|
||||
versioningTemplate: '{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}',
|
||||
extractVersionTemplate: '^v(?<version>\\d+\\.\\d+\\.\\d+)',
|
||||
},
|
||||
]
|
||||
{
|
||||
customType: 'regex',
|
||||
fileMatch: [
|
||||
'.*go$',
|
||||
],
|
||||
matchStrings: [
|
||||
'\\sconst .*Version = "(?<currentValue>.*)"\\s// renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s',
|
||||
],
|
||||
versioningTemplate: '{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}',
|
||||
extractVersionTemplate: '^v(?<version>\\d+\\.\\d+\\.\\d+)',
|
||||
},
|
||||
{
|
||||
customType: 'regex',
|
||||
fileMatch: [
|
||||
'^\\.github/workflows/[^/]+\\.ya?ml$',
|
||||
'Makefile$',
|
||||
],
|
||||
matchStrings: [
|
||||
'renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s.*?_VERSION: (?<currentValue>.*)\\s',
|
||||
],
|
||||
versioningTemplate: '{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}',
|
||||
extractVersionTemplate: '^v(?<version>\\d+\\.\\d+\\.\\d+)',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
79
.github/workflows/atlantis-image.yml
vendored
@@ -6,12 +6,16 @@ on:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
tags:
|
||||
- v*.*.* # stable release like, v0.19.2
|
||||
- v*.*.*-pre.* # pre release like, v0.19.0-pre.calendardate
|
||||
- v*.*.*
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -23,10 +27,10 @@ jobs:
|
||||
outputs:
|
||||
should-run-build: ${{ steps.changes.outputs.src == 'true' || startsWith(github.ref, 'refs/tags/') }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@@ -44,7 +48,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image_type: [alpine, debian]
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
# Set docker repo to either the fork or the main repo where the branch exists
|
||||
DOCKER_REPO: ghcr.io/${{ github.repository }}
|
||||
@@ -52,26 +56,26 @@ jobs:
|
||||
PUSH: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
# Lint the Dockerfile first before setting anything up
|
||||
- name: Lint Dockerfile
|
||||
uses: hadolint/hadolint-action@v3.1.0
|
||||
uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0
|
||||
with:
|
||||
dockerfile: "Dockerfile"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: arm64,arm
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
|
||||
# https://github.com/docker/build-push-action/issues/761#issuecomment-1575006515
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
image=moby/buildkit:v0.14.0
|
||||
|
||||
# release version is the name of the tag i.e. v0.10.0
|
||||
# release version also has the image type appended i.e. v0.10.0-alpine
|
||||
@@ -81,7 +85,7 @@ jobs:
|
||||
# if it's v0.10.0 and debian, it will do v0.10.0-debian, latest-debian
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5
|
||||
env:
|
||||
SUFFIX: ${{ format('-{0}', matrix.image_type) }}
|
||||
with:
|
||||
@@ -113,7 +117,7 @@ jobs:
|
||||
# Suffix is not used here since there's no way to disable it above
|
||||
|
||||
- name: Login to Packages Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -126,7 +130,7 @@ jobs:
|
||||
|
||||
- name: "Build ${{ env.PUSH == 'true' && 'and push' || '' }} ${{ env.DOCKER_REPO }} image"
|
||||
if: contains(fromJson('["push", "pull_request"]'), github.event_name)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -143,6 +147,51 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}
|
||||
|
||||
test:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-build == 'true'
|
||||
name: Test Image With Goss
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
image_type: [alpine, debian]
|
||||
env:
|
||||
# Set docker repo to either the fork or the main repo where the branch exists
|
||||
DOCKER_REPO: ghcr.io/${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
|
||||
# https://github.com/docker/build-push-action/issues/761#issuecomment-1575006515
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.14.0
|
||||
|
||||
- name: "Build and load into Docker"
|
||||
if: contains(fromJson('["push", "pull_request"]'), github.event_name)
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: |
|
||||
ATLANTIS_BASE_TAG_TYPE=${{ matrix.image_type }}
|
||||
push: false
|
||||
load: true
|
||||
tags: "${{ env.DOCKER_REPO }}:goss-test"
|
||||
target: ${{ matrix.image_type }}
|
||||
|
||||
- name: "Setup Goss"
|
||||
uses: e1himself/goss-installation-action@fbb6fb55d3e59c96045b2500eeb8ce0995d99ac1 # v1.2.1
|
||||
with:
|
||||
version: "v0.4.7"
|
||||
|
||||
- name: Execute Goss tests
|
||||
run: |
|
||||
dgoss run --rm ${{ env.DOCKER_REPO }}:goss-test bash -c 'while true; do sleep 1; done;'
|
||||
|
||||
skip-build:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-build == 'false'
|
||||
@@ -150,6 +199,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image_type: [alpine, debian]
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
26
.github/workflows/codeql.yml
vendored
@@ -30,15 +30,21 @@ on:
|
||||
schedule:
|
||||
- cron: '17 9 * * 5'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
permissions:
|
||||
contents: read # for dorny/paths-filter to fetch a list of changed files
|
||||
pull-requests: read # for dorny/paths-filter to read pull requests
|
||||
outputs:
|
||||
should-run-analyze: ${{ steps.changes.outputs.src == 'true' }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@@ -50,7 +56,7 @@ jobs:
|
||||
needs: [changes]
|
||||
name: Analyze
|
||||
if: github.event.pull_request.draft == false && needs.changes.outputs.should-run-analyze == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -67,11 +73,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -85,7 +91,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -98,7 +104,7 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -109,6 +115,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
7
.github/workflows/labeler.yml
vendored
@@ -8,12 +8,15 @@ on:
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
||||
|
||||
39
.github/workflows/lint.yml
vendored
@@ -9,21 +9,29 @@ on:
|
||||
- ready_for_review
|
||||
branches:
|
||||
- "main"
|
||||
- 'release-**'
|
||||
- "release-**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
# Required: allow read access to the content for analysis.
|
||||
contents: read
|
||||
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||
pull-requests: read
|
||||
# Optional: Allow write access to checks to allow the action to annotate code in the PR.
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
outputs:
|
||||
should-run-linting: ${{ steps.changes.outputs.go == 'true' }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@@ -37,18 +45,25 @@ jobs:
|
||||
needs: [changes]
|
||||
if: github.event.pull_request.draft == false && needs.changes.outputs.should-run-linting == 'true'
|
||||
name: Linting
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
uses: reviewdog/action-golangci-lint@v2
|
||||
with:
|
||||
tool_name: golangci-lint
|
||||
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
# need to setup go toolchain explicitly
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6
|
||||
with:
|
||||
# renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||
version: v1.60.1
|
||||
|
||||
skip-lint:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-linting == 'false'
|
||||
name: Linting
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
4
.github/workflows/pr-lint.yml
vendored
@@ -13,8 +13,8 @@ permissions:
|
||||
jobs:
|
||||
main:
|
||||
name: Validate PR title
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
- uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
33
.github/workflows/pr-size-labeler.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: pr-size
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
permissions:
|
||||
pull-requests: write # for codelytv/pr-size-labeler to add labels & comment on PRs
|
||||
runs-on: ubuntu-latest
|
||||
name: Label the PR size
|
||||
steps:
|
||||
- uses: codelytv/pr-size-labeler@c7a55a022747628b50f3eb5bf863b9e796b8f274 # v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
xs_label: 'size/xs'
|
||||
xs_max_size: '10'
|
||||
s_label: 'size/s'
|
||||
s_max_size: '200'
|
||||
m_label: 'size/m'
|
||||
m_max_size: '1000'
|
||||
l_label: 'size/l'
|
||||
l_max_size: '10000'
|
||||
xl_label: 'size/xl'
|
||||
fail_if_xl: 'false'
|
||||
message_if_xl: >
|
||||
This PR exceeds the recommended size of 1000 lines.
|
||||
Please make sure you are NOT addressing multiple issues with one PR.
|
||||
Note this PR might be rejected due to its size.
|
||||
github_api_url: 'https://api.github.com'
|
||||
files_to_ignore: ''
|
||||
45
.github/workflows/release.yml
vendored
@@ -3,32 +3,30 @@ name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*.*.* # stable release like, v0.19.2
|
||||
- v*.*.*-pre.* # pre release like, v0.19.0-pre.calendardate
|
||||
- v*.*.*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Run GoReleaser for stable release
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6
|
||||
if: (!contains(github.ref, 'pre'))
|
||||
with:
|
||||
version: v1.16.2
|
||||
# You can pass flags to goreleaser via GORELEASER_ARGS
|
||||
# --clean will save you deleting the dist dir
|
||||
args: release --clean
|
||||
distribution: goreleaser # or 'goreleaser-pro'
|
||||
version: "~> v2" # or 'latest', 'nightly', semver
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -43,32 +41,3 @@ jobs:
|
||||
-q .body > tmp-CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Run GoReleaser for pre-release
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
if: contains(github.ref, 'pre')
|
||||
with:
|
||||
version: v1.16.2
|
||||
# You can pass flags to goreleaser via GORELEASER_ARGS
|
||||
# --clean will save you deleting the dist dir
|
||||
args: release --clean --release-notes=tmp-CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_CURRENT_TAG: ${{ steps.changelog.outputs.RELEASE_TAG }}
|
||||
|
||||
homebrew:
|
||||
name: "Bump Homebrew formula"
|
||||
runs-on: ubuntu-22.04
|
||||
if: false
|
||||
# if: (!contains(github.ref, 'pre'))
|
||||
steps:
|
||||
- uses: mislav/bump-homebrew-formula-action@v2
|
||||
with:
|
||||
# A PR will be sent to github.com/Homebrew/homebrew-core to update this formula:
|
||||
formula-name: atlantis
|
||||
commit-message: |
|
||||
{{formulaName}} {{version}}
|
||||
|
||||
Created by https://github.com/mislav/bump-homebrew-formula-action
|
||||
env:
|
||||
COMMITTER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
9
.github/workflows/renovate-config.yml
vendored
@@ -12,10 +12,13 @@ on:
|
||||
- '.github/renovate.json5'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
||||
- run: npx --package renovate -c 'renovate-config-validator'
|
||||
|
||||
56
.github/workflows/scorecard.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 5 * * 1'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
persist-credentials: false
|
||||
show-progress: false
|
||||
|
||||
- name: 'Run analysis'
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: 'Upload artifact'
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
10
.github/workflows/stale.yml
vendored
@@ -2,11 +2,17 @@ name: Close Stale PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9
|
||||
with:
|
||||
stale-pr-message: 'This issue is stale because it has been open for 1 month with no activity. Remove stale label or comment or this will be closed in 1 month.'
|
||||
stale-issue-message: This issue is stale because it has been open for 1 month with no activity. Remove stale label or comment or this will be closed in 1 month.'
|
||||
|
||||
114
.github/workflows/test.yml
vendored
@@ -3,8 +3,8 @@ name: tester
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
- "main"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
@@ -12,45 +12,60 @@ on:
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
- "main"
|
||||
- "release-**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
permissions:
|
||||
contents: read # for dorny/paths-filter to fetch a list of changed files
|
||||
pull-requests: read # for dorny/paths-filter to read pull requests
|
||||
outputs:
|
||||
should-run-tests: ${{ steps.changes.outputs.go == 'true' }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
go:
|
||||
- '**.go'
|
||||
- '**.txt' # golden file test output
|
||||
- 'go.*'
|
||||
- '**.tmpl'
|
||||
- '.github/workflows/test.yml'
|
||||
test:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-tests == 'true'
|
||||
name: Tests
|
||||
runs-on: ubuntu-22.04
|
||||
container: ghcr.io/runatlantis/testing-env:latest
|
||||
runs-on: ubuntu-24.04
|
||||
container: ghcr.io/runatlantis/testing-env:latest@sha256:55abc22b2ae956ac91ab4170be17e13ed6d34f932ddd2891c1bc2b1c00fa11fb
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
# need to setup go toolchain explicitly
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- run: make test-all
|
||||
- run: make check-fmt
|
||||
|
||||
###########################################################
|
||||
# Notifying #contributors about test failure on main branch
|
||||
###########################################################
|
||||
- name: Slack failure notification
|
||||
if: ${{ github.ref == 'refs/heads/main' && failure() }}
|
||||
uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0
|
||||
uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
@@ -89,6 +104,81 @@ jobs:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-tests == 'false'
|
||||
name: Tests
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
e2e-github:
|
||||
runs-on: ubuntu-latest
|
||||
# dont run e2e tests on forked PRs
|
||||
if: github.event.pull_request.head.repo.fork == false
|
||||
env:
|
||||
TERRAFORM_VERSION: 1.9.2
|
||||
ATLANTIS_GH_USER: ${{ secrets.ATLANTISBOT_GITHUB_USERNAME }}
|
||||
ATLANTIS_GH_TOKEN: ${{ secrets.ATLANTISBOT_GITHUB_TOKEN }}
|
||||
NGROK_AUTH_TOKEN: ${{ secrets.ATLANTISBOT_NGROK_AUTH_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# This version of TF will be downloaded before Atlantis is started.
|
||||
# We do this instead of setting --default-tf-version because setting
|
||||
# that flag starts the download asynchronously so we'd have a race
|
||||
# condition.
|
||||
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
|
||||
with:
|
||||
terraform_version: ${{ env.TERRAFORM_VERSION }}
|
||||
|
||||
- name: Setup ngrok
|
||||
run: |
|
||||
wget -q -O ngrok.tar.gz https://bin.equinox.io/a/4no1PS1PoRF/ngrok-v3-3.13.0-linux-amd64.tar.gz
|
||||
tar -xzf ngrok.tar.gz
|
||||
chmod +x ngrok
|
||||
./ngrok version
|
||||
- name: Setup gitconfig
|
||||
run: |
|
||||
git config --global user.email "maintainers@runatlantis.io"
|
||||
git config --global user.name "atlantisbot"
|
||||
|
||||
- run: |
|
||||
make build-service
|
||||
./scripts/e2e.sh
|
||||
e2e-gitlab:
|
||||
runs-on: ubuntu-latest
|
||||
# dont run e2e tests on forked PRs
|
||||
if: github.event.pull_request.head.repo.fork == false
|
||||
env:
|
||||
TERRAFORM_VERSION: 1.9.2
|
||||
ATLANTIS_GITLAB_USER: ${{ secrets.ATLANTISBOT_GITLAB_USERNAME }}
|
||||
ATLANTIS_GITLAB_TOKEN: ${{ secrets.ATLANTISBOT_GITLAB_TOKEN }}
|
||||
NGROK_AUTH_TOKEN: ${{ secrets.ATLANTISBOT_NGROK_AUTH_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# This version of TF will be downloaded before Atlantis is started.
|
||||
# We do this instead of setting --default-tf-version because setting
|
||||
# that flag starts the download asynchronously so we'd have a race
|
||||
# condition.
|
||||
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
|
||||
with:
|
||||
terraform_version: ${{ env.TERRAFORM_VERSION }}
|
||||
|
||||
- name: Setup ngrok
|
||||
run: |
|
||||
wget -q -O ngrok.tar.gz https://bin.equinox.io/a/4no1PS1PoRF/ngrok-v3-3.13.0-linux-amd64.tar.gz
|
||||
tar -xzf ngrok.tar.gz
|
||||
chmod +x ngrok
|
||||
./ngrok version
|
||||
- name: Setup gitconfig
|
||||
run: |
|
||||
git config --global user.email "maintainers@runatlantis.io"
|
||||
git config --global user.name "atlantisbot"
|
||||
|
||||
- run: |
|
||||
make build-service
|
||||
./scripts/e2e.sh
|
||||
|
||||
25
.github/workflows/testing-env-image.yml
vendored
@@ -17,13 +17,16 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
permissions:
|
||||
contents: read # for dorny/paths-filter to fetch a list of changed files
|
||||
pull-requests: read # for dorny/paths-filter to read pull requests
|
||||
outputs:
|
||||
should-run-build: ${{ steps.changes.outputs.src == 'true' }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@@ -35,21 +38,21 @@ jobs:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-build == 'true'
|
||||
name: Build Testing Env Image
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: arm64,arm
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
|
||||
|
||||
- name: Login to Packages Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -57,7 +60,7 @@ jobs:
|
||||
|
||||
- run: echo "TODAY=$(date +"%Y.%m.%d")" >> $GITHUB_ENV
|
||||
- name: Build and push testing-env:${{env.TODAY}} image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -72,6 +75,6 @@ jobs:
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.should-run-build == 'false'
|
||||
name: Build Testing Env Image
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
46
.github/workflows/website.yml
vendored
@@ -24,16 +24,16 @@ jobs:
|
||||
outputs:
|
||||
should-run-link-check: ${{ steps.changes.outputs.src == 'true' }}
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
src:
|
||||
- '**.js'
|
||||
- 'yarn.lock'
|
||||
- 'package-lock.json'
|
||||
- 'package.json'
|
||||
- '.github/workflows/website.yml'
|
||||
|
||||
@@ -46,37 +46,55 @@ jobs:
|
||||
name: Website Link Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- name: markdown-lint
|
||||
uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
config: .markdownlint.yaml
|
||||
globs: 'runatlantis.io/**/*.md'
|
||||
|
||||
- name: setup npm
|
||||
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: run http-server
|
||||
env:
|
||||
# renovate: datasource=github-releases depName=raviqqe/muffet
|
||||
MUFFET_VERSION: 2.9.3
|
||||
MUFFET_VERSION: 2.10.3
|
||||
run: |
|
||||
npm install -g yarn
|
||||
|
||||
# install raviqqe/muffet to check for broken links.
|
||||
curl -Ls https://github.com/raviqqe/muffet/releases/download/v${MUFFET_VERSION}/muffet_linux_amd64.tar.gz | tar -xz
|
||||
|
||||
# build the site
|
||||
yarn && yarn website:build
|
||||
# build site
|
||||
npm install
|
||||
npm run website:build
|
||||
|
||||
# run http-server for muffet to check the links
|
||||
npx http-server runatlantis.io/.vuepress/dist &
|
||||
# start http-server for integration testing
|
||||
npx http-server runatlantis.io/.vitepress/dist &
|
||||
|
||||
- name: Run Playwright E2E tests
|
||||
run: |
|
||||
npx playwright install --with-deps
|
||||
npm run e2e
|
||||
|
||||
- name: wait until server listened
|
||||
run: curl --retry-delay 1 --retry 30 --retry-all-error http://localhost:8080
|
||||
|
||||
# medium.com => was being rate limited: HTTP 429
|
||||
# twitter.com => too many redirections
|
||||
# www.flaticon.com => 403 error
|
||||
- run: |
|
||||
./muffet \
|
||||
-e 'https://medium.com/runatlantis' \
|
||||
-e 'https://dev.to/*' \
|
||||
-e 'https://twitter.com/*' \
|
||||
-e 'https://www.flaticon.com/*' \
|
||||
-e 'https://github\.com/runatlantis/atlantis/edit/main/.*' \
|
||||
-e 'https://github.com/runatlantis/helm-charts#customization' \
|
||||
-e 'https://github.com/sethvargo/atlantis-on-gke/blob/master/terraform/tls.tf#L64-L84' \
|
||||
-e 'https://confluence.atlassian.com/*' \
|
||||
--header 'Accept-Encoding:deflate, gzip' \
|
||||
--buffer-size 8192 \
|
||||
|
||||
13
.gitignore
vendored
@@ -8,7 +8,6 @@ output
|
||||
.cover
|
||||
.terraform/
|
||||
node_modules/
|
||||
**/.vuepress/*
|
||||
helm/test-values.yaml
|
||||
*.swp
|
||||
golangci-lint
|
||||
@@ -16,7 +15,6 @@ atlantis
|
||||
.devcontainer
|
||||
atlantis.env
|
||||
*.act
|
||||
package-lock.json
|
||||
Dockerfile.local
|
||||
|
||||
# gitreleaser
|
||||
@@ -27,3 +25,14 @@ tmp-CHANGELOG.md
|
||||
|
||||
# IDE files
|
||||
*.code-workspace
|
||||
|
||||
# draw.io backup files
|
||||
*.bkp
|
||||
|
||||
# VitePress build output & cache directory
|
||||
**/.vitepress/cache
|
||||
**/.vitepress/dist
|
||||
**/.vitepress/config.ts.timestamp-*
|
||||
|
||||
# playwright
|
||||
test-results/
|
||||
|
||||
@@ -19,14 +19,14 @@ linters:
|
||||
- gofmt
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- revive
|
||||
- staticcheck
|
||||
- testifylint
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- vet
|
||||
- vetshadow
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
version: 2
|
||||
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
builds:
|
||||
- targets:
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- linux_386
|
||||
- linux_amd64
|
||||
- linux_arm
|
||||
- linux_arm64
|
||||
- windows_386
|
||||
- windows_amd64
|
||||
- id: atlantis
|
||||
|
||||
targets:
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- linux_386
|
||||
- linux_amd64
|
||||
- linux_arm
|
||||
- linux_arm64
|
||||
- windows_386
|
||||
- windows_amd64
|
||||
|
||||
flags:
|
||||
- -trimpath
|
||||
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
|
||||
|
||||
archives:
|
||||
- id: zip
|
||||
@@ -19,10 +31,10 @@ archives:
|
||||
- none*
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
name_template: "checksums.txt"
|
||||
|
||||
changelog:
|
||||
skip: true
|
||||
disable: true
|
||||
|
||||
release:
|
||||
# If set to true, will not auto-publish the release.
|
||||
|
||||
38
.markdownlint.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
# MD013/line-length
|
||||
#
|
||||
# We're not particular about line length, generally preferring longer
|
||||
# lines, since tools like Grammarly and other writing assistance tools
|
||||
# work best with "normal" lines not broken up arbitrary.
|
||||
#
|
||||
# https://github.com/DavidAnson/markdownlint/blob/main/doc/md013.md
|
||||
MD013: false
|
||||
|
||||
# MD033/no-inline-html
|
||||
#
|
||||
# We're fine with inline HTML, there are lots of valid VitePress features
|
||||
# that depends on this.
|
||||
#
|
||||
# https://github.com/DavidAnson/markdownlint/blob/main/doc/md033.md
|
||||
MD033: false
|
||||
|
||||
# MD024/no-duplicate-heading
|
||||
#
|
||||
# VitePress do not follow GitHub heading styling, so duplicate headlines
|
||||
# are fine as long as they are not siblings (aka same indention hierarchy)
|
||||
#
|
||||
# https://github.com/DavidAnson/markdownlint/blob/main/doc/md024.md
|
||||
MD024:
|
||||
siblings_only: true
|
||||
|
||||
# MD051/link-fragments
|
||||
#
|
||||
# VitePress generate these differently that markdownlint expects, so disabling
|
||||
# for now, and something to improve on later (cc @jippi)
|
||||
#
|
||||
# https://github.com/DavidAnson/markdownlint/blob/main/doc/md051.md
|
||||
MD051: false
|
||||
|
||||
# for blog posts
|
||||
MD025: false
|
||||
MD045: false
|
||||
MD001: false
|
||||
@@ -1 +1 @@
|
||||
20.10.0
|
||||
20.17.0
|
||||
|
||||
2
.tool-versions
Normal file
@@ -0,0 +1,2 @@
|
||||
node 20.14.0
|
||||
go 1.23.0
|
||||
@@ -1,3 +1,3 @@
|
||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
* @runatlantis/maintainers
|
||||
* @runatlantis/maintainers @runatlantis/core-contributors
|
||||
|
||||
@@ -55,8 +55,9 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team by messaging `@lkysow` on the [Atlantis Slack channel](https://join.slack.com/t/atlantis-community/shared_invite/zt-1nt7yx7uq-AnVRc_JItF1CDwZtfqv_OA). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
reported by contacting the project team by messaging `@lkysow` on the
|
||||
[Atlantis Slack community](https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw).
|
||||
All complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
102
CONTRIBUTING.md
@@ -1,9 +1,24 @@
|
||||
# Topics
|
||||
* [Reporting Issues](#reporting-issues)
|
||||
* [Reporting Security Issues](#reporting-security-issues)
|
||||
* [Updating The Website](#updating-the-website)
|
||||
* [Developing](#developing)
|
||||
* [Releasing](#creating-a-new-release)
|
||||
# Contributing <!-- omit in toc -->
|
||||
|
||||
# Table of Contents <!-- omit in toc -->
|
||||
- [Reporting Issues](#reporting-issues)
|
||||
- [Reporting Security Issues](#reporting-security-issues)
|
||||
- [Updating The Website](#updating-the-website)
|
||||
- [Developing](#developing)
|
||||
- [Running Atlantis Locally](#running-atlantis-locally)
|
||||
- [Running Atlantis With Local Changes](#running-atlantis-with-local-changes)
|
||||
- [Rebuilding](#rebuilding)
|
||||
- [Running Tests Locally](#running-tests-locally)
|
||||
- [Running Tests In Docker](#running-tests-in-docker)
|
||||
- [Calling Your Local Atlantis From GitHub](#calling-your-local-atlantis-from-github)
|
||||
- [Code Style](#code-style)
|
||||
- [Logging](#logging)
|
||||
- [Errors](#errors)
|
||||
- [Testing](#testing)
|
||||
- [Mocks](#mocks)
|
||||
- [Backporting Fixes](#backporting-fixes)
|
||||
- [Manual Backporting Fixes](#manual-backporting-fixes)
|
||||
- [Creating a New Release](#creating-a-new-release)
|
||||
|
||||
# Reporting Issues
|
||||
* When reporting issues, please include the output of `atlantis version`.
|
||||
@@ -14,7 +29,7 @@
|
||||
We take security issues seriously. Please report a security vulnerability to the maintainers using [private vulnerability reporting](https://github.com/runatlantis/atlantis/security/advisories/new).
|
||||
|
||||
# Updating The Website
|
||||
* To view the generated website locally, run `yarn website:dev` and then
|
||||
* To view the generated website locally, run `npm website:dev` and then
|
||||
open your browser to http://localhost:8080.
|
||||
* The website will be regenerated when your pull request is merged to main.
|
||||
|
||||
@@ -23,11 +38,11 @@ open your browser to http://localhost:8080.
|
||||
## Running Atlantis Locally
|
||||
* Clone the repo from https://github.com/runatlantis/atlantis/
|
||||
* Compile Atlantis:
|
||||
```
|
||||
```sh
|
||||
go install
|
||||
```
|
||||
* Run Atlantis:
|
||||
```
|
||||
```sh
|
||||
atlantis server --gh-user <your username> --gh-token <your token> --repo-allowlist <your repo> --gh-webhook-secret <your webhook secret> --log-level debug
|
||||
```
|
||||
If you get an error like `command not found: atlantis`, ensure that `$GOPATH/bin` is in your `$PATH`.
|
||||
@@ -36,43 +51,46 @@ open your browser to http://localhost:8080.
|
||||
Docker compose is set up to start an atlantis container and ngrok container in the same network in order to expose the atlantis instance to the internet. In order to do this, create a file in the repository called `atlantis.env` and add the required env vars for the atlantis server configuration.
|
||||
|
||||
e.g.
|
||||
```
|
||||
|
||||
```sh
|
||||
NGROK_AUTH=1234567890
|
||||
|
||||
ATLANTIS_GH_APP_ID=123
|
||||
ATLANTIS_GH_APP_KEY_FILE="/.ssh/somekey.pem"
|
||||
ATLANTIS_GH_WEBHOOK_SECRET=12345
|
||||
```
|
||||
|
||||
Note: `~/.ssh` is mounted to allow for referencing any local ssh keys
|
||||
Note: `~/.ssh` is mounted to allow for referencing any local ssh keys.
|
||||
|
||||
Following this just run:
|
||||
|
||||
```
|
||||
```sh
|
||||
make build-service
|
||||
docker-compose up
|
||||
docker-compose up --detach
|
||||
docker-compose logs --follow
|
||||
```
|
||||
|
||||
### Rebuilding
|
||||
|
||||
If 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.
|
||||
|
||||
e.g.
|
||||
|
||||
```
|
||||
```sh
|
||||
make build-service
|
||||
docker-compose up --detach --build
|
||||
```
|
||||
|
||||
## Running Tests Locally:
|
||||
|
||||
## Running Tests Locally
|
||||
`make test`. If you want to run the integration tests that actually run real `terraform` commands, run `make test-all`.
|
||||
|
||||
## Running Tests In Docker:
|
||||
```
|
||||
## Running Tests In Docker
|
||||
```sh
|
||||
docker 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
|
||||
```
|
||||
|
||||
Or to run the integration tests
|
||||
```
|
||||
|
||||
```sh
|
||||
docker 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
|
||||
```
|
||||
|
||||
@@ -80,18 +98,19 @@ docker run --rm -v $(pwd):/go/src/github.com/runatlantis/atlantis -w /go/src/git
|
||||
- Create a test terraform repository in your GitHub.
|
||||
- Create a personal access token for Atlantis. See [Create a GitHub token](https://github.com/runatlantis/atlantis/tree/main/runatlantis.io/docs/access-credentials.md#generating-an-access-token).
|
||||
- Start Atlantis in server mode using that token:
|
||||
```
|
||||
```sh
|
||||
atlantis server --gh-user <your username> --gh-token <your token> --repo-allowlist <your repo> --gh-webhook-secret <your webhook secret> --log-level debug
|
||||
```
|
||||
- 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.
|
||||
- When you've downloaded and extracted ngrok, run it on port `4141`:
|
||||
```
|
||||
```sh
|
||||
ngrok http 4141
|
||||
```
|
||||
- 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/main/runatlantis.io/docs/configuring-webhooks.md#configuring-webhooks).
|
||||
- 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.
|
||||
|
||||
## Code Style
|
||||
|
||||
### Logging
|
||||
- `ctx.Log` should be available in most methods. If not, pass it down.
|
||||
- levels:
|
||||
@@ -160,18 +179,33 @@ If you get an error about `pegomock` not being available, install it:
|
||||
go get github.com/petergtz/pegomock/...
|
||||
```
|
||||
|
||||
# Backporting Fixes
|
||||
Atlantis now uses a [cherry-pick-bot](https://github.com/googleapis/repo-automation-bots/tree/main/packages/cherry-pick-bot) from Google. The bot assists in maintaining changes across releases branches by easily cherry-picking changes via pull requests.
|
||||
|
||||
Maintainers and Core Contributors can add a comment to a pull request:
|
||||
|
||||
```sh
|
||||
/cherry-pick target-branch-name
|
||||
```
|
||||
|
||||
target-branch-name is the branch to cherry-pick to. cherry-pick-bot will cherry-pick the merged commit to a new branch (created from the target branch) and open a new pull request to the target branch.
|
||||
|
||||
The bot will immediately try to cherry-pick a merged PR. On unmerged pull request, it will not do anything immediately, but wait until merge. You can comment multiple times on a PR for multiple release branches.
|
||||
|
||||
## Manual Backporting Fixes
|
||||
The bot will fail to cherry-pick if the feature branches' git history is not linear (merge commits instead of rebase). In that case, you will need to manually cherry-pick the squashed merged commit from main to the release branch
|
||||
|
||||
1. Switch to the release branch intended for the fix.
|
||||
1. Run `git cherry-pick <sha>` with the commit hash from the main branch.
|
||||
1. Push the newly cherry-picked commit up to the remote release branch.
|
||||
|
||||
# Creating a New Release
|
||||
1. Update version number in `main.go`.
|
||||
1. Update image tag version in the [kustomize/bundle.yaml](kustomize/bundle.yaml).
|
||||
1. Update `CHANGELOG.md` with latest release number and information (this URL might be useful: https://github.com/runatlantis/atlantis/compare/v0.3.5...main)
|
||||
1. Create a pull request and merge to main
|
||||
1. Check out main and fetch latest
|
||||
1. Run `make release`
|
||||
1. If you get `signal: killed` errors, bump up your Docker resources to have more memory, e.g. 6 G.B.
|
||||
1. (Major/Minor release only) Create a new release branch `release-x.y`
|
||||
1. Go to https://github.com/runatlantis/atlantis/releases and click "Draft a new release"
|
||||
1. Prefix version with `v`
|
||||
1. Prefix version with `v` and increment based on last release.
|
||||
1. The title of the release is the same as the tag (ex. v0.2.2)
|
||||
1. Fill in description by copying from the CHANGELOG just without the Downloads section
|
||||
1. Drag in binaries made with `make release`
|
||||
1. Re-run main branch build to ensure tag gets pushed to Github: https://github.com/runatlantis/atlantis/pkgs/container/atlantis
|
||||
1. Update the default version in `Chart.yaml` in [the official Helm chart](https://github.com/runatlantis/helm-charts/blob/main/charts/atlantis/values.yaml).
|
||||
1. Fill in description by clicking on the "Generate Release Notes" button.
|
||||
1. You may have to manually move around some commit titles as they are determined by PR labels (see .github/labeler.yml & .github/release.yml)
|
||||
1. (Latest Major/Minor branches only) Make sure the release is set as latest
|
||||
1. Don't set "latest release" for patches on older release branches.
|
||||
1. Check and update the default version in `Chart.yaml` in [the official Helm chart](https://github.com/runatlantis/helm-charts/blob/main/charts/atlantis/values.yaml) as needed.
|
||||
|
||||
67
Dockerfile
@@ -1,14 +1,19 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# syntax=docker/dockerfile:1@sha256:865e5dd094beca432e8c0a1d5e1c465db5f998dca4e439981029b3b81fb39ed5
|
||||
# what distro is the image being built for
|
||||
ARG ALPINE_TAG=3.19.0
|
||||
ARG DEBIAN_TAG=12.2-slim
|
||||
ARG ALPINE_TAG=3.20.3@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d
|
||||
ARG DEBIAN_TAG=12.7-slim@sha256:ad86386827b083b3d71139050b47ffb32bbd9559ea9b1345a739b14fec2d9ecf
|
||||
ARG GOLANG_TAG=1.23.0-alpine@sha256:d0b31558e6b3e4cc59f6011d79905835108c919143ebecc58f35965bf79948f4
|
||||
|
||||
ARG DEFAULT_TERRAFORM_VERSION=1.6.3
|
||||
ARG DEFAULT_CONFTEST_VERSION=0.46.0
|
||||
# renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
|
||||
ARG DEFAULT_TERRAFORM_VERSION=1.9.7
|
||||
# renovate: datasource=github-releases depName=opentofu/opentofu versioning=hashicorp
|
||||
ARG DEFAULT_OPENTOFU_VERSION=1.8.2
|
||||
# renovate: datasource=github-releases depName=open-policy-agent/conftest
|
||||
ARG DEFAULT_CONFTEST_VERSION=0.55.0
|
||||
|
||||
# Stage 1: build artifact and download deps
|
||||
|
||||
FROM golang:1.21.5-alpine AS builder
|
||||
FROM golang:${GOLANG_TAG} AS builder
|
||||
|
||||
ARG ATLANTIS_VERSION=dev
|
||||
ENV ATLANTIS_VERSION=${ATLANTIS_VERSION}
|
||||
@@ -66,7 +71,6 @@ ARG TARGETPLATFORM
|
||||
WORKDIR /tmp/build
|
||||
|
||||
# install conftest
|
||||
# renovate: datasource=github-releases depName=open-policy-agent/conftest
|
||||
ARG DEFAULT_CONFTEST_VERSION
|
||||
ENV DEFAULT_CONFTEST_VERSION=${DEFAULT_CONFTEST_VERSION}
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
@@ -90,7 +94,7 @@ RUN AVAILABLE_CONFTEST_VERSIONS=${DEFAULT_CONFTEST_VERSION} && \
|
||||
|
||||
# install git-lfs
|
||||
# renovate: datasource=github-releases depName=git-lfs/git-lfs
|
||||
ENV GIT_LFS_VERSION=3.4.0
|
||||
ENV GIT_LFS_VERSION=3.5.1
|
||||
|
||||
RUN case ${TARGETPLATFORM} in \
|
||||
"linux/amd64") GIT_LFS_ARCH=amd64 ;; \
|
||||
@@ -104,31 +108,26 @@ RUN case ${TARGETPLATFORM} in \
|
||||
git-lfs --version
|
||||
|
||||
# install terraform binaries
|
||||
# renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
|
||||
ARG DEFAULT_TERRAFORM_VERSION
|
||||
ENV DEFAULT_TERRAFORM_VERSION=${DEFAULT_TERRAFORM_VERSION}
|
||||
ARG DEFAULT_OPENTOFU_VERSION
|
||||
ENV DEFAULT_OPENTOFU_VERSION=${DEFAULT_OPENTOFU_VERSION}
|
||||
|
||||
# COPY scripts/download-release.sh .
|
||||
COPY --from=builder /app/scripts/download-release.sh download-release.sh
|
||||
|
||||
# In the official Atlantis image, we only have the latest of each Terraform version.
|
||||
# Each binary is about 80 MB so we limit it to the 4 latest minor releases or fewer
|
||||
RUN AVAILABLE_TERRAFORM_VERSIONS="1.3.10 1.4.6 1.5.7 ${DEFAULT_TERRAFORM_VERSION}" && \
|
||||
case "${TARGETPLATFORM}" in \
|
||||
"linux/amd64") TERRAFORM_ARCH=amd64 ;; \
|
||||
"linux/arm64") TERRAFORM_ARCH=arm64 ;; \
|
||||
"linux/arm/v7") TERRAFORM_ARCH=arm ;; \
|
||||
*) echo "ERROR: 'TARGETPLATFORM' value expected: ${TARGETPLATFORM}"; exit 1 ;; \
|
||||
esac && \
|
||||
for VERSION in ${AVAILABLE_TERRAFORM_VERSIONS}; do \
|
||||
curl -LOs "https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_${TERRAFORM_ARCH}.zip" && \
|
||||
curl -LOs "https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_SHA256SUMS" && \
|
||||
sed -n "/terraform_${VERSION}_linux_${TERRAFORM_ARCH}.zip/p" "terraform_${VERSION}_SHA256SUMS" | sha256sum -c && \
|
||||
mkdir -p "/usr/local/bin/tf/versions/${VERSION}" && \
|
||||
unzip "terraform_${VERSION}_linux_${TERRAFORM_ARCH}.zip" -d "/usr/local/bin/tf/versions/${VERSION}" && \
|
||||
ln -s "/usr/local/bin/tf/versions/${VERSION}/terraform" "/usr/local/bin/terraform${VERSION}" && \
|
||||
rm "terraform_${VERSION}_linux_${TERRAFORM_ARCH}.zip" && \
|
||||
rm "terraform_${VERSION}_SHA256SUMS"; \
|
||||
done && \
|
||||
ln -s "/usr/local/bin/tf/versions/${DEFAULT_TERRAFORM_VERSION}/terraform" /usr/local/bin/terraform
|
||||
|
||||
RUN ./download-release.sh \
|
||||
"terraform" \
|
||||
"${TARGETPLATFORM}" \
|
||||
"${DEFAULT_TERRAFORM_VERSION}" \
|
||||
"1.6.6 1.7.5 1.8.5 ${DEFAULT_TERRAFORM_VERSION}" \
|
||||
&& ./download-release.sh \
|
||||
"tofu" \
|
||||
"${TARGETPLATFORM}" \
|
||||
"${DEFAULT_OPENTOFU_VERSION}" \
|
||||
"${DEFAULT_OPENTOFU_VERSION}"
|
||||
|
||||
# Stage 2 - Alpine
|
||||
# Creating the individual distro builds using targets
|
||||
@@ -148,16 +147,20 @@ RUN addgroup atlantis && \
|
||||
# copy atlantis binary
|
||||
COPY --from=builder /app/atlantis /usr/local/bin/atlantis
|
||||
# copy terraform binaries
|
||||
COPY --from=deps /usr/local/bin/terraform* /usr/local/bin/
|
||||
COPY --from=deps /usr/local/bin/terraform/terraform* /usr/local/bin/
|
||||
COPY --from=deps /usr/local/bin/tofu/tofu* /usr/local/bin/
|
||||
# copy dependencies
|
||||
COPY --from=deps /usr/local/bin/conftest /usr/local/bin/conftest
|
||||
COPY --from=deps /usr/bin/git-lfs /usr/bin/git-lfs
|
||||
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
# renovate: datasource=repology depName=alpine_3_20/ca-certificates versioning=loose
|
||||
ENV CA_CERTIFICATES_VERSION="20240705-r0"
|
||||
|
||||
# Install packages needed to run Atlantis.
|
||||
# We place this last as it will bust less docker layer caches when packages update
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates~=20230506 \
|
||||
ca-certificates~=${CA_CERTIFICATES_VERSION} \
|
||||
curl~=8 \
|
||||
git~=2 \
|
||||
unzip~=6 \
|
||||
@@ -166,7 +169,6 @@ RUN apk add --no-cache \
|
||||
dumb-init~=1 \
|
||||
gcompat~=1
|
||||
|
||||
|
||||
# Set the entry point to the atlantis user and run the atlantis command
|
||||
USER atlantis
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
@@ -188,7 +190,8 @@ RUN useradd --create-home --user-group --shell /bin/bash atlantis && \
|
||||
# copy atlantis binary
|
||||
COPY --from=builder /app/atlantis /usr/local/bin/atlantis
|
||||
# copy terraform binaries
|
||||
COPY --from=deps /usr/local/bin/terraform* /usr/local/bin/
|
||||
COPY --from=deps /usr/local/bin/terraform/terraform* /usr/local/bin/
|
||||
COPY --from=deps /usr/local/bin/tofu/tofu* /usr/local/bin/
|
||||
# copy dependencies
|
||||
COPY --from=deps /usr/local/bin/conftest /usr/local/bin/conftest
|
||||
COPY --from=deps /usr/bin/git-lfs /usr/bin/git-lfs
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
FROM ghcr.io/runatlantis/atlantis:latest
|
||||
FROM ghcr.io/runatlantis/atlantis:latest@sha256:5318d83dc11546c30ea2487108f465c7e000c8d190c66bf925fc3dba7a993d3f
|
||||
COPY atlantis /usr/local/bin/atlantis
|
||||
WORKDIR /atlantis/src
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
The current Maintainers Group for the [Atlantis] Project consists of:
|
||||
|
||||
| Name | GitHub ID | Employer | Responsibilities |
|
||||
| ------------- | --------- | -------- | ---------------- |
|
||||
| Dylan Page | [GenPage](https://github.com/GenPage) | Autodesk | Maintainer |
|
||||
| PePe Amengual | [jamengual](https://github.com/jamengual) | Slalom | Maintainer |
|
||||
| Rui Chen | [chenrui333](https://github.com/chenrui333) | Meetup | Maintainer |
|
||||
| Ronak | [nitrocode](https://github.com/nitrocode) |RB Consulting LLC | Contributor, Reviewer |
|
||||
| Name | GitHub ID | Employer | Responsibilities |
|
||||
| ------------- | ------------------------------------------- | ----------------- | ---------------- |
|
||||
| Dylan Page | [GenPage](https://github.com/GenPage) | Lambda | Maintainer |
|
||||
| PePe Amengual | [jamengual](https://github.com/jamengual) | Slalom | Maintainer |
|
||||
| Rui Chen | [chenrui333](https://github.com/chenrui333) | Meetup | Maintainer |
|
||||
| Ronak | [nitrocode](https://github.com/nitrocode) | RB Consulting LLC | Core Contributor |
|
||||
|
||||
9
Makefile
@@ -6,6 +6,9 @@ IMAGE_NAME := runatlantis/atlantis
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||
GOLANGCI_LINT_VERSION := v1.59.1
|
||||
|
||||
.PHONY: help
|
||||
help: ## List targets & descriptions
|
||||
@cat Makefile* | grep -E '^[a-zA-Z\/_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
@@ -93,12 +96,12 @@ lint: ## Run linter locally
|
||||
|
||||
.PHONY: check-lint
|
||||
check-lint: ## Run linter in CI/CD. If running locally use 'lint'
|
||||
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin v1.49.0
|
||||
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin $(GOLANGCI_LINT_VERSION)
|
||||
./bin/golangci-lint run -j 4 --timeout 5m
|
||||
|
||||
.PHONY: check-fmt
|
||||
check-fmt: ## Fail if not formatted
|
||||
if [[ $$(goimports -l $$(find . -type f -name '*.go' ! -path "./vendor/*" ! -path "**/mocks/*")) ]]; then exit 1; fi
|
||||
./scripts/fmt.sh
|
||||
|
||||
.PHONY: end-to-end-deps
|
||||
end-to-end-deps: ## Install e2e dependencies
|
||||
@@ -110,4 +113,4 @@ end-to-end-tests: ## Run e2e tests
|
||||
|
||||
.PHONY: website-dev
|
||||
website-dev: ## Run runatlantic.io on localhost:8080
|
||||
yarn website:dev
|
||||
npm website:dev
|
||||
|
||||
12
README.md
@@ -1,15 +1,15 @@
|
||||
# Atlantis <!-- omit in toc -->
|
||||
|
||||
[](https://github.com/runatlantis/atlantis/releases/latest)
|
||||
[](https://twitter.com/kelseyhightower/status/893260922222813184)
|
||||
[](https://twitter.com/kelseyhightower/status/893260922222813184)
|
||||
[](https://goreportcard.com/report/github.com/runatlantis/atlantis)
|
||||
[](https://pkg.go.dev/github.com/runatlantis/atlantis)
|
||||
[](https://codecov.io/gh/runatlantis/atlantis)
|
||||
[](https://circleci.com/gh/runatlantis/atlantis/tree/main)
|
||||
[](https://join.slack.com/t/atlantis-community/shared_invite/zt-1nt7yx7uq-AnVRc_JItF1CDwZtfqv_OA)
|
||||
[](https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/runatlantis/atlantis)
|
||||
[](https://www.bestpractices.dev/projects/9428)
|
||||
|
||||
<p align="center">
|
||||
<img src="./runatlantis.io/.vuepress/public/hero.png" alt="Atlantis Logo"/><br><br>
|
||||
<img src="./runatlantis.io/public/hero.png" alt="Atlantis Logo"/><br><br>
|
||||
<b>Terraform Pull Request Automation</b>
|
||||
</p>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* How to get started: [www.runatlantis.io/guide](https://www.runatlantis.io/guide)
|
||||
* Full documentation: [www.runatlantis.io/docs](https://www.runatlantis.io/docs)
|
||||
* Download the latest release: [github.com/runatlantis/atlantis/releases/latest](https://github.com/runatlantis/atlantis/releases/latest)
|
||||
* Get help in our [Slack channel](https://join.slack.com/t/atlantis-community/shared_invite/zt-1nt7yx7uq-AnVRc_JItF1CDwZtfqv_OA)
|
||||
* Get help in our [Slack channel](https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw)
|
||||
* Start Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## What is Atlantis?
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We take security issues seriously. Please report a security vulnerability to the maintainers using [private vulnerability reporting](https://github.com/runatlantis/atlantis/security/advisories/new).
|
||||
|
||||
## Maintained releases
|
||||
|
||||
Only the two latest minor releases are maintained. For example, if `0.29.7` is the latest, then `0.29.x` and `0.28.x` will receive security fixes.
|
||||
|
||||
172
cmd/server.go
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/runatlantis/atlantis/server"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/bitbucketcloud"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
)
|
||||
@@ -38,6 +37,12 @@ const (
|
||||
CheckoutStrategyMerge = "merge"
|
||||
)
|
||||
|
||||
// TF distributions
|
||||
const (
|
||||
TFDistributionTerraform = "terraform"
|
||||
TFDistributionOpenTofu = "opentofu"
|
||||
)
|
||||
|
||||
// To add a new flag you must:
|
||||
// 1. Add a const with the flag name (in alphabetic order).
|
||||
// 2. Add a new field to server.UserConfig and set the mapstructure tag equal to the flag name.
|
||||
@@ -73,12 +78,13 @@ const (
|
||||
DisableAutoplanLabelFlag = "disable-autoplan-label"
|
||||
DisableMarkdownFoldingFlag = "disable-markdown-folding"
|
||||
DisableRepoLockingFlag = "disable-repo-locking"
|
||||
DisableGlobalApplyLockFlag = "disable-global-apply-lock"
|
||||
DisableUnlockLabelFlag = "disable-unlock-label"
|
||||
DiscardApprovalOnPlanFlag = "discard-approval-on-plan"
|
||||
EmojiReaction = "emoji-reaction"
|
||||
EnableDiffMarkdownFormat = "enable-diff-markdown-format"
|
||||
EnablePolicyChecksFlag = "enable-policy-checks"
|
||||
EnableRegExpCmdFlag = "enable-regexp-cmd"
|
||||
EnableDiffMarkdownFormat = "enable-diff-markdown-format"
|
||||
ExecutableName = "executable-name"
|
||||
FailOnPreWorkflowHookError = "fail-on-pre-workflow-hook-error"
|
||||
HideUnchangedPlanComments = "hide-unchanged-plan-comments"
|
||||
@@ -90,9 +96,15 @@ const (
|
||||
GHAppKeyFlag = "gh-app-key"
|
||||
GHAppKeyFileFlag = "gh-app-key-file"
|
||||
GHAppSlugFlag = "gh-app-slug"
|
||||
GHAppInstallationIDFlag = "gh-app-installation-id"
|
||||
GHOrganizationFlag = "gh-org"
|
||||
GHWebhookSecretFlag = "gh-webhook-secret" // nolint: gosec
|
||||
GHAllowMergeableBypassApply = "gh-allow-mergeable-bypass-apply" // nolint: gosec
|
||||
GiteaBaseURLFlag = "gitea-base-url"
|
||||
GiteaTokenFlag = "gitea-token"
|
||||
GiteaUserFlag = "gitea-user"
|
||||
GiteaWebhookSecretFlag = "gitea-webhook-secret" // nolint: gosec
|
||||
GiteaPageSizeFlag = "gitea-page-size"
|
||||
GitlabHostnameFlag = "gitlab-hostname"
|
||||
GitlabTokenFlag = "gitlab-token"
|
||||
GitlabUserFlag = "gitlab-user"
|
||||
@@ -104,6 +116,7 @@ const (
|
||||
LockingDBType = "locking-db-type"
|
||||
LogLevelFlag = "log-level"
|
||||
MarkdownTemplateOverridesDirFlag = "markdown-template-overrides-dir"
|
||||
MaxCommentsPerCommand = "max-comments-per-command"
|
||||
ParallelPoolSize = "parallel-pool-size"
|
||||
StatsNamespace = "stats-namespace"
|
||||
AllowDraftPRs = "allow-draft-prs"
|
||||
@@ -117,17 +130,17 @@ const (
|
||||
RepoConfigFlag = "repo-config"
|
||||
RepoConfigJSONFlag = "repo-config-json"
|
||||
RepoAllowlistFlag = "repo-allowlist"
|
||||
RequireApprovalFlag = "require-approval"
|
||||
RequireMergeableFlag = "require-mergeable"
|
||||
SilenceNoProjectsFlag = "silence-no-projects"
|
||||
SilenceForkPRErrorsFlag = "silence-fork-pr-errors"
|
||||
SilenceVCSStatusNoPlans = "silence-vcs-status-no-plans"
|
||||
SilenceVCSStatusNoProjectsFlag = "silence-vcs-status-no-projects"
|
||||
SilenceAllowlistErrorsFlag = "silence-allowlist-errors"
|
||||
SkipCloneNoChanges = "skip-clone-no-changes"
|
||||
SlackTokenFlag = "slack-token"
|
||||
SSLCertFileFlag = "ssl-cert-file"
|
||||
SSLKeyFileFlag = "ssl-key-file"
|
||||
RestrictFileList = "restrict-file-list"
|
||||
TFDistributionFlag = "tf-distribution"
|
||||
TFDownloadFlag = "tf-download"
|
||||
TFDownloadURLFlag = "tf-download-url"
|
||||
UseTFPluginCache = "use-tf-plugin-cache"
|
||||
@@ -153,13 +166,16 @@ const (
|
||||
DefaultCheckoutDepth = 0
|
||||
DefaultBitbucketBaseURL = bitbucketcloud.BaseURL
|
||||
DefaultDataDir = "~/.atlantis"
|
||||
DefaultEmojiReaction = "eyes"
|
||||
DefaultEmojiReaction = ""
|
||||
DefaultExecutableName = "atlantis"
|
||||
DefaultMarkdownTemplateOverridesDir = "~/.markdown_templates"
|
||||
DefaultGHHostname = "github.com"
|
||||
DefaultGiteaBaseURL = "https://gitea.com"
|
||||
DefaultGiteaPageSize = 30
|
||||
DefaultGitlabHostname = "gitlab.com"
|
||||
DefaultLockingDBType = "boltdb"
|
||||
DefaultLogLevel = "info"
|
||||
DefaultMaxCommentsPerCommand = 100
|
||||
DefaultParallelPoolSize = 15
|
||||
DefaultStatsNamespace = "atlantis"
|
||||
DefaultPort = 4141
|
||||
@@ -167,6 +183,7 @@ const (
|
||||
DefaultRedisPort = 6379
|
||||
DefaultRedisTLSEnabled = false
|
||||
DefaultRedisInsecureSkipVerify = false
|
||||
DefaultTFDistribution = TFDistributionTerraform
|
||||
DefaultTFDownloadURL = "https://releases.hashicorp.com"
|
||||
DefaultTFDownload = true
|
||||
DefaultTFEHostname = "app.terraform.io"
|
||||
@@ -269,7 +286,7 @@ var stringFlags = map[string]stringFlag{
|
||||
defaultValue: "",
|
||||
},
|
||||
EmojiReaction: {
|
||||
description: "Emoji Reaction to use to react to comments",
|
||||
description: "Emoji Reaction to use to react to comments.",
|
||||
defaultValue: DefaultEmojiReaction,
|
||||
},
|
||||
ExecutableName: {
|
||||
@@ -319,6 +336,22 @@ var stringFlags = map[string]stringFlag{
|
||||
"This means that an attacker could spoof calls to Atlantis and cause it to perform malicious actions. " +
|
||||
"Should be specified via the ATLANTIS_GH_WEBHOOK_SECRET environment variable.",
|
||||
},
|
||||
GiteaBaseURLFlag: {
|
||||
description: "Base URL of Gitea server installation. Must include 'http://' or 'https://'.",
|
||||
},
|
||||
GiteaUserFlag: {
|
||||
description: "Gitea username of API user.",
|
||||
defaultValue: "",
|
||||
},
|
||||
GiteaTokenFlag: {
|
||||
description: "Gitea token of API user. Can also be specified via the ATLANTIS_GITEA_TOKEN environment variable.",
|
||||
},
|
||||
GiteaWebhookSecretFlag: {
|
||||
description: "Optional secret used to validate Gitea webhooks." +
|
||||
" SECURITY WARNING: If not specified, Atlantis won't be able to validate that the incoming webhook call came from Gitea. " +
|
||||
"This means that an attacker could spoof calls to Atlantis and cause it to perform malicious actions. " +
|
||||
"Should be specified via the ATLANTIS_GITEA_WEBHOOK_SECRET environment variable.",
|
||||
},
|
||||
GitlabHostnameFlag: {
|
||||
description: "Hostname of your GitLab Enterprise installation. If using gitlab.com, no need to set.",
|
||||
defaultValue: DefaultGitlabHostname,
|
||||
@@ -381,6 +414,10 @@ var stringFlags = map[string]stringFlag{
|
||||
SSLKeyFileFlag: {
|
||||
description: fmt.Sprintf("File containing x509 private key matching --%s.", SSLCertFileFlag),
|
||||
},
|
||||
TFDistributionFlag: {
|
||||
description: fmt.Sprintf("Which TF distribution to use. Can be set to %s or %s.", TFDistributionTerraform, TFDistributionOpenTofu),
|
||||
defaultValue: DefaultTFDistribution,
|
||||
},
|
||||
TFDownloadURLFlag: {
|
||||
description: "Base URL to download Terraform versions from.",
|
||||
defaultValue: DefaultTFDownloadURL,
|
||||
@@ -437,9 +474,13 @@ var boolFlags = map[string]boolFlag{
|
||||
description: "Disable atlantis auto planning feature",
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
DisableRepoLockingFlag: {
|
||||
description: "Disable atlantis locking repos",
|
||||
},
|
||||
DisableGlobalApplyLockFlag: {
|
||||
description: "Disable atlantis global apply lock in UI",
|
||||
},
|
||||
DiscardApprovalOnPlanFlag: {
|
||||
description: "Enables the discarding of approval if a new plan has been executed. Currently only Github is supported",
|
||||
defaultValue: false,
|
||||
@@ -497,16 +538,6 @@ var boolFlags = map[string]boolFlag{
|
||||
description: "Controls whether the Redis client verifies the Redis server's certificate chain and host name. If true, accepts any certificate presented by the server and any host name in that certificate.",
|
||||
defaultValue: DefaultRedisInsecureSkipVerify,
|
||||
},
|
||||
RequireApprovalFlag: {
|
||||
description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.",
|
||||
defaultValue: false,
|
||||
hidden: true,
|
||||
},
|
||||
RequireMergeableFlag: {
|
||||
description: "Require pull requests to be mergeable before allowing the apply command to be run.",
|
||||
defaultValue: false,
|
||||
hidden: true,
|
||||
},
|
||||
SilenceNoProjectsFlag: {
|
||||
description: "Silences Atlants from responding to PRs when it finds no projects.",
|
||||
defaultValue: false,
|
||||
@@ -519,6 +550,10 @@ var boolFlags = map[string]boolFlag{
|
||||
description: "Silences VCS commit status when autoplan finds no projects to plan.",
|
||||
defaultValue: false,
|
||||
},
|
||||
SilenceVCSStatusNoProjectsFlag: {
|
||||
description: "Silences VCS commit status when for all commands when a project is not defined.",
|
||||
defaultValue: false,
|
||||
},
|
||||
SilenceAllowlistErrorsFlag: {
|
||||
description: "Silences the posting of allowlist error comments.",
|
||||
defaultValue: false,
|
||||
@@ -572,6 +607,14 @@ var intFlags = map[string]intFlag{
|
||||
" If merge base is further behind than this number of commits from any of branches heads, full fetch will be performed.",
|
||||
defaultValue: DefaultCheckoutDepth,
|
||||
},
|
||||
MaxCommentsPerCommand: {
|
||||
description: "If non-zero, the maximum number of comments to split command output into before truncating.",
|
||||
defaultValue: DefaultMaxCommentsPerCommand,
|
||||
},
|
||||
GiteaPageSizeFlag: {
|
||||
description: "Optional value that specifies the number of results per page to expect from Gitea.",
|
||||
defaultValue: DefaultGiteaPageSize,
|
||||
},
|
||||
ParallelPoolSize: {
|
||||
description: "Max size of the wait group that runs parallel plans and applies (if enabled).",
|
||||
defaultValue: DefaultParallelPoolSize,
|
||||
@@ -595,6 +638,13 @@ var int64Flags = map[string]int64Flag{
|
||||
description: "GitHub App Id. If defined, initializes the GitHub client with app-based credentials",
|
||||
defaultValue: 0,
|
||||
},
|
||||
GHAppInstallationIDFlag: {
|
||||
description: "GitHub App Installation Id. If defined, initializes the GitHub client with app-based credentials " +
|
||||
"using this specific GitHub Application Installation ID, otherwise it attempts to auto-detect it. " +
|
||||
"Note that this value must be set if you want to have one App and multiple installations of that same " +
|
||||
"application.",
|
||||
defaultValue: 0,
|
||||
},
|
||||
}
|
||||
|
||||
// ValidLogLevels are the valid log levels that can be set
|
||||
@@ -751,7 +801,7 @@ func (s *ServerCmd) run() error {
|
||||
if err := s.Viper.Unmarshal(&userConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
s.setDefaults(&userConfig)
|
||||
s.setDefaults(&userConfig, s.Viper)
|
||||
|
||||
// Now that we've parsed the config we can set our local logger to the
|
||||
// right level.
|
||||
@@ -792,7 +842,7 @@ func (s *ServerCmd) run() error {
|
||||
return server.Start()
|
||||
}
|
||||
|
||||
func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
func (s *ServerCmd) setDefaults(c *server.UserConfig, v *viper.Viper) {
|
||||
if c.AzureDevOpsHostname == "" {
|
||||
c.AzureDevOpsHostname = DefaultADHostname
|
||||
}
|
||||
@@ -817,6 +867,12 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
if c.GitlabHostname == "" {
|
||||
c.GitlabHostname = DefaultGitlabHostname
|
||||
}
|
||||
if c.GiteaBaseURL == "" {
|
||||
c.GiteaBaseURL = DefaultGiteaBaseURL
|
||||
}
|
||||
if c.GiteaPageSize == 0 {
|
||||
c.GiteaPageSize = DefaultGiteaPageSize
|
||||
}
|
||||
if c.BitbucketBaseURL == "" {
|
||||
c.BitbucketBaseURL = DefaultBitbucketBaseURL
|
||||
}
|
||||
@@ -835,6 +891,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
if c.MarkdownTemplateOverridesDir == "" {
|
||||
c.MarkdownTemplateOverridesDir = DefaultMarkdownTemplateOverridesDir
|
||||
}
|
||||
if !v.IsSet("max-comments-per-command") {
|
||||
c.MaxCommentsPerCommand = DefaultMaxCommentsPerCommand
|
||||
}
|
||||
if c.ParallelPoolSize == 0 {
|
||||
c.ParallelPoolSize = DefaultParallelPoolSize
|
||||
}
|
||||
@@ -850,6 +909,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
if c.RedisPort == 0 {
|
||||
c.RedisPort = DefaultRedisPort
|
||||
}
|
||||
if c.TFDistribution == "" {
|
||||
c.TFDistribution = DefaultTFDistribution
|
||||
}
|
||||
if c.TFDownloadURL == "" {
|
||||
c.TFDownloadURL = DefaultTFDownloadURL
|
||||
}
|
||||
@@ -876,6 +938,11 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
return fmt.Errorf("invalid log level: must be one of %v", ValidLogLevels)
|
||||
}
|
||||
|
||||
if userConfig.TFDistribution != TFDistributionTerraform && userConfig.TFDistribution != TFDistributionOpenTofu {
|
||||
return fmt.Errorf("invalid tf distribution: expected one of %s or %s",
|
||||
TFDistributionTerraform, TFDistributionOpenTofu)
|
||||
}
|
||||
|
||||
checkoutStrategy := userConfig.CheckoutStrategy
|
||||
if checkoutStrategy != CheckoutStrategyBranch && checkoutStrategy != CheckoutStrategyMerge {
|
||||
return fmt.Errorf("invalid checkout strategy: not one of %s or %s",
|
||||
@@ -889,12 +956,17 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
// The following combinations are valid.
|
||||
// 1. github user and token set
|
||||
// 2. github app ID and (key file set or key set)
|
||||
// 3. gitlab user and token set
|
||||
// 4. bitbucket user and token set
|
||||
// 5. azuredevops user and token set
|
||||
// 6. any combination of the above
|
||||
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHAppIDFlag, GHAppKeyFileFlag, GHAppIDFlag, GHAppKeyFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag)
|
||||
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) {
|
||||
// 3. gitea user and token set
|
||||
// 4. gitlab user and token set
|
||||
// 5. bitbucket user and token set
|
||||
// 6. azuredevops user and token set
|
||||
// 7. any combination of the above
|
||||
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHAppIDFlag, GHAppKeyFileFlag, GHAppIDFlag, GHAppKeyFlag, GiteaUserFlag, GiteaTokenFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag)
|
||||
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) ||
|
||||
((userConfig.GiteaUser == "") != (userConfig.GiteaToken == "")) ||
|
||||
((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) ||
|
||||
((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) ||
|
||||
((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) {
|
||||
return vcsErr
|
||||
}
|
||||
if (userConfig.GithubAppID != 0) && ((userConfig.GithubAppKey == "") && (userConfig.GithubAppKeyFile == "")) {
|
||||
@@ -905,7 +977,7 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
}
|
||||
// At this point, we know that there can't be a single user/token without
|
||||
// its partner, but we haven't checked if any user/token is set at all.
|
||||
if userConfig.GithubAppID == 0 && userConfig.GithubUser == "" && userConfig.GitlabUser == "" && userConfig.BitbucketUser == "" && userConfig.AzureDevopsUser == "" {
|
||||
if userConfig.GithubAppID == 0 && userConfig.GithubUser == "" && userConfig.GiteaUser == "" && userConfig.GitlabUser == "" && userConfig.BitbucketUser == "" && userConfig.AzureDevopsUser == "" {
|
||||
return vcsErr
|
||||
}
|
||||
|
||||
@@ -928,6 +1000,14 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
return fmt.Errorf("--%s must have http:// or https://, got %q", BitbucketBaseURLFlag, userConfig.BitbucketBaseURL)
|
||||
}
|
||||
|
||||
parsed, err = url.Parse(userConfig.GiteaBaseURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing --%s flag value %q: %s", GiteaWebhookSecretFlag, userConfig.GiteaBaseURL, err)
|
||||
}
|
||||
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||
return fmt.Errorf("--%s must have http:// or https://, got %q", GiteaBaseURLFlag, userConfig.GiteaBaseURL)
|
||||
}
|
||||
|
||||
if userConfig.RepoConfig != "" && userConfig.RepoConfigJSON != "" {
|
||||
return fmt.Errorf("cannot use --%s and --%s at the same time", RepoConfigFlag, RepoConfigJSONFlag)
|
||||
}
|
||||
@@ -940,6 +1020,8 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
GitlabWebhookSecretFlag: userConfig.GitlabWebhookSecret,
|
||||
BitbucketTokenFlag: userConfig.BitbucketToken,
|
||||
BitbucketWebhookSecretFlag: userConfig.BitbucketWebhookSecret,
|
||||
GiteaTokenFlag: userConfig.GiteaToken,
|
||||
GiteaWebhookSecretFlag: userConfig.GiteaWebhookSecret,
|
||||
} {
|
||||
if strings.Contains(token, "\n") {
|
||||
s.Logger.Warn("--%s contains a newline which is usually unintentional", name)
|
||||
@@ -1033,6 +1115,7 @@ func (s *ServerCmd) setVarFileAllowlist(userConfig *server.UserConfig) {
|
||||
// trimAtSymbolFromUsers trims @ from the front of the github and gitlab usernames
|
||||
func (s *ServerCmd) trimAtSymbolFromUsers(userConfig *server.UserConfig) {
|
||||
userConfig.GithubUser = strings.TrimPrefix(userConfig.GithubUser, "@")
|
||||
userConfig.GiteaUser = strings.TrimPrefix(userConfig.GiteaUser, "@")
|
||||
userConfig.GitlabUser = strings.TrimPrefix(userConfig.GitlabUser, "@")
|
||||
userConfig.BitbucketUser = strings.TrimPrefix(userConfig.BitbucketUser, "@")
|
||||
userConfig.AzureDevopsUser = strings.TrimPrefix(userConfig.AzureDevopsUser, "@")
|
||||
@@ -1042,6 +1125,9 @@ func (s *ServerCmd) securityWarnings(userConfig *server.UserConfig) {
|
||||
if userConfig.GithubUser != "" && userConfig.GithubWebhookSecret == "" && !s.SilenceOutput {
|
||||
s.Logger.Warn("no GitHub webhook secret set. This could allow attackers to spoof requests from GitHub")
|
||||
}
|
||||
if userConfig.GiteaUser != "" && userConfig.GiteaWebhookSecret == "" && !s.SilenceOutput {
|
||||
s.Logger.Warn("no Gitea webhook secret set. This could allow attackers to spoof requests from Gitea")
|
||||
}
|
||||
if userConfig.GitlabUser != "" && userConfig.GitlabWebhookSecret == "" && !s.SilenceOutput {
|
||||
s.Logger.Warn("no GitLab webhook secret set. This could allow attackers to spoof requests from GitLab")
|
||||
}
|
||||
@@ -1057,33 +1143,15 @@ func (s *ServerCmd) securityWarnings(userConfig *server.UserConfig) {
|
||||
}
|
||||
|
||||
// deprecationWarnings prints a warning if flags that are deprecated are
|
||||
// being used. Right now this only applies to flags that have been made obsolete
|
||||
// due to server-side config.
|
||||
// being used.
|
||||
func (s *ServerCmd) deprecationWarnings(userConfig *server.UserConfig) error {
|
||||
var commandReqs []string
|
||||
var deprecatedFlags []string
|
||||
if userConfig.RequireApproval {
|
||||
deprecatedFlags = append(deprecatedFlags, RequireApprovalFlag)
|
||||
commandReqs = append(commandReqs, valid.ApprovedCommandReq)
|
||||
}
|
||||
if userConfig.RequireMergeable {
|
||||
deprecatedFlags = append(deprecatedFlags, RequireMergeableFlag)
|
||||
commandReqs = append(commandReqs, valid.MergeableCommandReq)
|
||||
}
|
||||
|
||||
// Build up strings with what the recommended yaml and json config should
|
||||
// be instead of using the deprecated flags.
|
||||
yamlCfg := "---\nrepos:\n- id: /.*/"
|
||||
jsonCfg := `{"repos":[{"id":"/.*/"`
|
||||
if len(commandReqs) > 0 {
|
||||
yamlCfg += fmt.Sprintf("\n plan_requirements: [%s]", strings.Join(commandReqs, ", "))
|
||||
yamlCfg += fmt.Sprintf("\n apply_requirements: [%s]", strings.Join(commandReqs, ", "))
|
||||
yamlCfg += fmt.Sprintf("\n import_requirements: [%s]", strings.Join(commandReqs, ", "))
|
||||
jsonCfg += fmt.Sprintf(`, "plan_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
|
||||
jsonCfg += fmt.Sprintf(`, "apply_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
|
||||
jsonCfg += fmt.Sprintf(`, "import_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
|
||||
}
|
||||
jsonCfg += "}]}"
|
||||
// Currently there are no deprecated flags; if flags become deprecated, add them here like so
|
||||
// if userConfig.SomeDeprecatedFlag {
|
||||
// deprecatedFlags = append(deprecatedFlags, SomeDeprecatedFlag)
|
||||
// }
|
||||
//
|
||||
|
||||
if len(deprecatedFlags) > 0 {
|
||||
warning := "WARNING: "
|
||||
@@ -1092,12 +1160,6 @@ func (s *ServerCmd) deprecationWarnings(userConfig *server.UserConfig) error {
|
||||
} else {
|
||||
warning += fmt.Sprintf("Flags --%s and --%s have been deprecated.", strings.Join(deprecatedFlags[0:len(deprecatedFlags)-1], ", --"), deprecatedFlags[len(deprecatedFlags)-1:][0])
|
||||
}
|
||||
warning += fmt.Sprintf("\nCreate a --%s file with the following config instead:\n\n%s\n\nor use --%s='%s'\n",
|
||||
RepoConfigFlag,
|
||||
yamlCfg,
|
||||
RepoConfigJSONFlag,
|
||||
jsonCfg,
|
||||
)
|
||||
fmt.Println(warning)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/runatlantis/atlantis/server"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/testdata"
|
||||
@@ -52,13 +52,17 @@ func (s *ServerStarterMock) Start() error {
|
||||
// Adding a new flag? Add it to this slice for testing in alphabetical
|
||||
// order.
|
||||
var testFlags = map[string]interface{}{
|
||||
ADHostnameFlag: "dev.azure.com",
|
||||
ADTokenFlag: "ad-token",
|
||||
ADUserFlag: "ad-user",
|
||||
ADWebhookPasswordFlag: "ad-wh-pass",
|
||||
ADWebhookUserFlag: "ad-wh-user",
|
||||
AtlantisURLFlag: "url",
|
||||
AutoplanModules: false,
|
||||
AutoplanModulesFromProjects: "",
|
||||
AllowCommandsFlag: "version,plan,apply,unlock,import,approve_policies",
|
||||
AllowForkPRsFlag: true,
|
||||
APISecretFlag: "",
|
||||
AutoDiscoverModeFlag: "auto",
|
||||
AutomergeFlag: true,
|
||||
AutoplanFileListFlag: "**/*.tf,**/*.yml",
|
||||
@@ -67,38 +71,63 @@ var testFlags = map[string]interface{}{
|
||||
BitbucketUserFlag: "bitbucket-user",
|
||||
BitbucketWebhookSecretFlag: "bitbucket-secret",
|
||||
CheckoutStrategyFlag: CheckoutStrategyMerge,
|
||||
CheckoutDepthFlag: 0,
|
||||
DataDirFlag: "/path",
|
||||
DefaultTFVersionFlag: "v0.11.0",
|
||||
DisableApplyAllFlag: true,
|
||||
DisableMarkdownFoldingFlag: true,
|
||||
DisableRepoLockingFlag: true,
|
||||
DisableGlobalApplyLockFlag: false,
|
||||
DiscardApprovalOnPlanFlag: true,
|
||||
EmojiReaction: "eyes",
|
||||
ExecutableName: "atlantis",
|
||||
FailOnPreWorkflowHookError: false,
|
||||
GHAllowMergeableBypassApply: false,
|
||||
GHHostnameFlag: "ghhostname",
|
||||
GHTeamAllowlistFlag: "",
|
||||
GHTokenFlag: "token",
|
||||
GHUserFlag: "user",
|
||||
GHAppIDFlag: int64(0),
|
||||
GHAppKeyFlag: "",
|
||||
GHAppKeyFileFlag: "",
|
||||
GHAppSlugFlag: "atlantis",
|
||||
GHAppInstallationIDFlag: int64(0),
|
||||
GHOrganizationFlag: "",
|
||||
GHWebhookSecretFlag: "secret",
|
||||
GiteaBaseURLFlag: "http://localhost",
|
||||
GiteaTokenFlag: "gitea-token",
|
||||
GiteaUserFlag: "gitea-user",
|
||||
GiteaWebhookSecretFlag: "gitea-secret",
|
||||
GiteaPageSizeFlag: 30,
|
||||
GitlabHostnameFlag: "gitlab-hostname",
|
||||
GitlabTokenFlag: "gitlab-token",
|
||||
GitlabUserFlag: "gitlab-user",
|
||||
GitlabWebhookSecretFlag: "gitlab-secret",
|
||||
HideUnchangedPlanComments: false,
|
||||
HidePrevPlanComments: false,
|
||||
IncludeGitUntrackedFiles: false,
|
||||
LockingDBType: "boltdb",
|
||||
LogLevelFlag: "debug",
|
||||
MarkdownTemplateOverridesDirFlag: "/path2",
|
||||
MaxCommentsPerCommand: 10,
|
||||
StatsNamespace: "atlantis",
|
||||
AllowDraftPRs: true,
|
||||
PortFlag: 8181,
|
||||
ParallelPoolSize: 100,
|
||||
ParallelPlanFlag: true,
|
||||
ParallelApplyFlag: true,
|
||||
QuietPolicyChecks: false,
|
||||
RedisHost: "",
|
||||
RedisInsecureSkipVerify: false,
|
||||
RedisPassword: "",
|
||||
RedisPort: 6379,
|
||||
RedisTLSEnabled: false,
|
||||
RedisDB: 0,
|
||||
RepoAllowlistFlag: "github.com/runatlantis/atlantis",
|
||||
RequireApprovalFlag: true,
|
||||
RequireMergeableFlag: true,
|
||||
RepoConfigFlag: "",
|
||||
RepoConfigJSONFlag: "",
|
||||
SilenceNoProjectsFlag: false,
|
||||
SilenceVCSStatusNoProjectsFlag: false,
|
||||
SilenceForkPRErrorsFlag: true,
|
||||
SilenceAllowlistErrorsFlag: true,
|
||||
SilenceVCSStatusNoPlans: true,
|
||||
@@ -107,11 +136,19 @@ var testFlags = map[string]interface{}{
|
||||
SSLCertFileFlag: "cert-file",
|
||||
SSLKeyFileFlag: "key-file",
|
||||
RestrictFileList: false,
|
||||
TFDistributionFlag: "terraform",
|
||||
TFDownloadFlag: true,
|
||||
TFDownloadURLFlag: "https://my-hostname.com",
|
||||
TFEHostnameFlag: "my-hostname",
|
||||
TFELocalExecutionModeFlag: true,
|
||||
TFETokenFlag: "my-token",
|
||||
UseTFPluginCache: true,
|
||||
VarFileAllowlistFlag: "/path",
|
||||
VCSStatusName: "my-status",
|
||||
WebBasicAuthFlag: false,
|
||||
WebPasswordFlag: "atlantis",
|
||||
WebUsernameFlag: "atlantis",
|
||||
WebsocketCheckOrigin: false,
|
||||
WriteGitCredsFlag: true,
|
||||
DisableAutoplanFlag: true,
|
||||
DisableAutoplanLabelFlag: "no-auto-plan",
|
||||
@@ -127,6 +164,7 @@ func TestExecute_Defaults(t *testing.T) {
|
||||
c := setup(map[string]interface{}{
|
||||
GHUserFlag: "user",
|
||||
GHTokenFlag: "token",
|
||||
GiteaBaseURLFlag: "http://localhost",
|
||||
RepoAllowlistFlag: "*",
|
||||
}, t)
|
||||
err := c.Execute()
|
||||
@@ -145,6 +183,7 @@ func TestExecute_Defaults(t *testing.T) {
|
||||
strExceptions := map[string]string{
|
||||
GHUserFlag: "user",
|
||||
GHTokenFlag: "token",
|
||||
GiteaBaseURLFlag: "http://localhost",
|
||||
DataDirFlag: dataDir,
|
||||
MarkdownTemplateOverridesDirFlag: markdownTemplateOverridesDir,
|
||||
AtlantisURLFlag: "http://" + hostname + ":4141",
|
||||
@@ -184,6 +223,32 @@ func TestExecute_Flags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserConfigAllTested(t *testing.T) {
|
||||
t.Log("All settings in userConfig should be tested.")
|
||||
|
||||
u := reflect.TypeOf(server.UserConfig{})
|
||||
|
||||
for i := 0; i < u.NumField(); i++ {
|
||||
|
||||
userConfigKey := u.Field(i).Tag.Get("mapstructure")
|
||||
t.Run(userConfigKey, func(t *testing.T) {
|
||||
// By default, we expect all fields in UserConfig to have flags defined in server.go and tested here in server_test.go
|
||||
// Some fields are too complicated to have flags, so are only expressible in the config yaml
|
||||
flagKey := u.Field(i).Tag.Get("flag")
|
||||
if flagKey == "false" {
|
||||
return
|
||||
}
|
||||
// If a setting is configured in server.UserConfig, it should be tested here. If there is no corresponding const
|
||||
// for specifying the flag, that probably means one *also* needs to be added to server.go
|
||||
if _, ok := testFlags[userConfigKey]; !ok {
|
||||
t.Errorf("server.UserConfig has field with mapstructure %s that is not tested, and potentially also not configured as a flag. Either add it to testFlags (and potentially as a const in cmd/server), or remove it from server.UserConfig", userConfigKey)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExecute_ConfigFile(t *testing.T) {
|
||||
t.Log("Should use all the values from the config file.")
|
||||
// Use yaml package to quote values that need quoting
|
||||
@@ -367,7 +432,7 @@ func TestExecute_ValidateSSLConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
expErr := "--gh-user/--gh-token or --gh-app-id/--gh-app-key-file or --gh-app-id/--gh-app-key or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set"
|
||||
expErr := "--gh-user/--gh-token or --gh-app-id/--gh-app-key-file or --gh-app-id/--gh-app-key or --gitea-user/--gitea-token or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set"
|
||||
cases := []struct {
|
||||
description string
|
||||
flags map[string]interface{}
|
||||
@@ -385,6 +450,13 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"just gitea token set",
|
||||
map[string]interface{}{
|
||||
GiteaTokenFlag: "token",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"just gitlab token set",
|
||||
map[string]interface{}{
|
||||
@@ -413,6 +485,13 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"just gitea user set",
|
||||
map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"just github app set",
|
||||
map[string]interface{}{
|
||||
@@ -479,6 +558,22 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"github user and gitea token set",
|
||||
map[string]interface{}{
|
||||
GHUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"gitea user and github token set",
|
||||
map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GHTokenFlag: "token",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"github user and github token set and should be successful",
|
||||
map[string]interface{}{
|
||||
@@ -487,6 +582,14 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"gitea user and gitea token set and should be successful",
|
||||
map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"github app and key file set and should be successful",
|
||||
map[string]interface{}{
|
||||
@@ -532,6 +635,8 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
|
||||
map[string]interface{}{
|
||||
GHUserFlag: "user",
|
||||
GHTokenFlag: "token",
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
GitlabUserFlag: "user",
|
||||
GitlabTokenFlag: "token",
|
||||
BitbucketUserFlag: "user",
|
||||
@@ -644,6 +749,34 @@ func TestExecute_GithubApp(t *testing.T) {
|
||||
Equals(t, int64(1), passedConfig.GithubAppID)
|
||||
}
|
||||
|
||||
func TestExecute_GithubAppWithInstallationID(t *testing.T) {
|
||||
t.Log("Should pass the installation ID to the config.")
|
||||
c := setup(map[string]interface{}{
|
||||
GHAppKeyFlag: testdata.GithubPrivateKey,
|
||||
GHAppIDFlag: "1",
|
||||
GHAppInstallationIDFlag: "2",
|
||||
RepoAllowlistFlag: "*",
|
||||
}, t)
|
||||
err := c.Execute()
|
||||
Ok(t, err)
|
||||
|
||||
Equals(t, int64(1), passedConfig.GithubAppID)
|
||||
Equals(t, int64(2), passedConfig.GithubAppInstallationID)
|
||||
}
|
||||
|
||||
func TestExecute_GiteaUser(t *testing.T) {
|
||||
t.Log("Should remove the @ from the gitea username if it's passed.")
|
||||
c := setup(map[string]interface{}{
|
||||
GiteaUserFlag: "@user",
|
||||
GiteaTokenFlag: "token",
|
||||
RepoAllowlistFlag: "*",
|
||||
}, t)
|
||||
err := c.Execute()
|
||||
Ok(t, err)
|
||||
|
||||
Equals(t, "user", passedConfig.GiteaUser)
|
||||
}
|
||||
|
||||
func TestExecute_GitlabUser(t *testing.T) {
|
||||
t.Log("Should remove the @ from the gitlab username if it's passed.")
|
||||
c := setup(map[string]interface{}{
|
||||
@@ -879,3 +1012,45 @@ func configVal(t *testing.T, u server.UserConfig, tag string) interface{} {
|
||||
t.Fatalf("no field with tag %q found", tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gitea base URL must have a scheme.
|
||||
func TestExecute_GiteaBaseURLScheme(t *testing.T) {
|
||||
c := setup(map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
RepoAllowlistFlag: "*",
|
||||
GiteaBaseURLFlag: "mydomain.com",
|
||||
}, t)
|
||||
ErrEquals(t, "--gitea-base-url must have http:// or https://, got \"mydomain.com\"", c.Execute())
|
||||
|
||||
c = setup(map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
RepoAllowlistFlag: "*",
|
||||
GiteaBaseURLFlag: "://mydomain.com",
|
||||
}, t)
|
||||
ErrEquals(t, "error parsing --gitea-webhook-secret flag value \"://mydomain.com\": parse \"://mydomain.com\": missing protocol scheme", c.Execute())
|
||||
}
|
||||
|
||||
func TestExecute_GiteaWithWebhookSecret(t *testing.T) {
|
||||
c := setup(map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
RepoAllowlistFlag: "*",
|
||||
GiteaWebhookSecretFlag: "my secret",
|
||||
}, t)
|
||||
err := c.Execute()
|
||||
Ok(t, err)
|
||||
}
|
||||
|
||||
// Port should be retained on base url.
|
||||
func TestExecute_GiteaBaseURLPort(t *testing.T) {
|
||||
c := setup(map[string]interface{}{
|
||||
GiteaUserFlag: "user",
|
||||
GiteaTokenFlag: "token",
|
||||
RepoAllowlistFlag: "*",
|
||||
GiteaBaseURLFlag: "http://mydomain.com:7990",
|
||||
}, t)
|
||||
Ok(t, c.Execute())
|
||||
Equals(t, "http://mydomain.com:7990", passedConfig.GiteaBaseURL)
|
||||
}
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
# Note: This file is only used for Atlantis local development
|
||||
version: "3.8"
|
||||
services:
|
||||
ngrok:
|
||||
image: wernight/ngrok:latest
|
||||
ports:
|
||||
- 4040:4040
|
||||
environment:
|
||||
# https://dashboard.ngrok.com/get-started/your-authtoken
|
||||
# NGROK_AUTH: REPLACE-WITH-YOUR-TOKEN // set this in atlantis.env
|
||||
NGROK_PROTOCOL: http
|
||||
NGROK_PORT: atlantis:4141
|
||||
env_file:
|
||||
- ./atlantis.env
|
||||
depends_on:
|
||||
- atlantis
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- '6379:6379'
|
||||
command: redis-server --save 20 1 --loglevel warning --requirepass test123
|
||||
volumes:
|
||||
- redis:/data
|
||||
atlantis:
|
||||
depends_on:
|
||||
- redis
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
ports:
|
||||
- 4141:4141
|
||||
volumes:
|
||||
- ~/.ssh:/.ssh
|
||||
- ./:/atlantis/src
|
||||
# Contains the flags that atlantis uses in env var form
|
||||
env_file:
|
||||
- ./atlantis.env
|
||||
ngrok:
|
||||
image: wernight/ngrok:latest@sha256:d211f29ebcfe5f4e72df4fa8bdd9a667886e127d7fcb1be4a1af5ad83a8a1b77
|
||||
ports:
|
||||
- 4040:4040
|
||||
environment:
|
||||
# https://dashboard.ngrok.com/get-started/your-authtoken
|
||||
# NGROK_AUTH: REPLACE-WITH-YOUR-TOKEN // set this in atlantis.env
|
||||
NGROK_PROTOCOL: http
|
||||
NGROK_PORT: atlantis:4141
|
||||
env_file:
|
||||
- atlantis.env
|
||||
depends_on:
|
||||
- atlantis
|
||||
redis:
|
||||
image: redis:7.4-alpine@sha256:de13e74e14b98eb96bdf886791ae47686c3c5d29f9d5f85ea55206843e3fce26
|
||||
restart: always
|
||||
ports:
|
||||
- 6379:6379
|
||||
command: redis-server --save 20 1 --loglevel warning --requirepass test123
|
||||
volumes:
|
||||
- redis:/data
|
||||
atlantis:
|
||||
depends_on:
|
||||
- redis
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
ports:
|
||||
- 4141:4141
|
||||
volumes:
|
||||
- ${HOME}/.ssh:/.ssh:ro
|
||||
- ${PWD}:/atlantis/src:ro
|
||||
# Contains the flags that atlantis uses in env var form
|
||||
env_file:
|
||||
- atlantis.env
|
||||
|
||||
volumes:
|
||||
redis:
|
||||
driver: local
|
||||
redis:
|
||||
driver: local
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[user]
|
||||
name = atlantisbot
|
||||
email = lkysow+atlantis@gmail.com
|
||||
91
e2e/e2e.go
@@ -14,29 +14,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
type E2ETester struct {
|
||||
githubClient *GithubClient
|
||||
repoURL string
|
||||
ownerName string
|
||||
repoName string
|
||||
vcsClient VCSClient
|
||||
hookID int64
|
||||
cloneDirRoot string
|
||||
projectType Project
|
||||
}
|
||||
|
||||
type E2EResult struct {
|
||||
projectType string
|
||||
githubPullRequestURL string
|
||||
testResult string
|
||||
projectType string
|
||||
pullRequestURL string
|
||||
testResult string
|
||||
}
|
||||
|
||||
var testFileData = `
|
||||
@@ -45,7 +41,7 @@ resource "null_resource" "hello" {
|
||||
`
|
||||
|
||||
// nolint: gosec
|
||||
func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
func (t *E2ETester) Start(ctx context.Context) (*E2EResult, error) {
|
||||
cloneDir := fmt.Sprintf("%s/%s-test", t.cloneDirRoot, t.projectType.Name)
|
||||
branchName := fmt.Sprintf("%s-%s", t.projectType.Name, time.Now().Format("20060102150405"))
|
||||
testFileName := fmt.Sprintf("%s.tf", t.projectType.Name)
|
||||
@@ -58,11 +54,9 @@ func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
return e2eResult, fmt.Errorf("failed to create dir %q prior to cloning, attempting to continue: %v", cloneDir, err)
|
||||
}
|
||||
|
||||
cloneCmd := exec.Command("git", "clone", t.repoURL, cloneDir)
|
||||
// git clone the repo
|
||||
log.Printf("git cloning into %q", cloneDir)
|
||||
if output, err := cloneCmd.CombinedOutput(); err != nil {
|
||||
return e2eResult, fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
|
||||
err := t.vcsClient.Clone(cloneDir)
|
||||
if err != nil {
|
||||
return e2eResult, err
|
||||
}
|
||||
|
||||
// checkout a new branch for the project
|
||||
@@ -77,7 +71,7 @@ func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
randomData := []byte(testFileData)
|
||||
filePath := fmt.Sprintf("%s/%s/%s", cloneDir, t.projectType.Name, testFileName)
|
||||
log.Printf("creating file to commit %q", filePath)
|
||||
err := os.WriteFile(filePath, randomData, 0644)
|
||||
err = os.WriteFile(filePath, randomData, 0644)
|
||||
if err != nil {
|
||||
return e2eResult, fmt.Errorf("couldn't write file %s: %v", filePath, err)
|
||||
}
|
||||
@@ -108,23 +102,24 @@ func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
|
||||
// create a new pr
|
||||
title := fmt.Sprintf("This is a test pull request for atlantis e2e test for %s project type", t.projectType.Name)
|
||||
head := fmt.Sprintf("%s:%s", t.ownerName, branchName)
|
||||
body := ""
|
||||
base := "main"
|
||||
newPullRequest := &github.NewPullRequest{Title: &title, Head: &head, Body: &body, Base: &base}
|
||||
url, pullId, err := t.vcsClient.CreatePullRequest(ctx, title, branchName)
|
||||
|
||||
pull, _, err := t.githubClient.client.PullRequests.Create(t.githubClient.ctx, t.ownerName, t.repoName, newPullRequest)
|
||||
if err != nil {
|
||||
return e2eResult, fmt.Errorf("error while creating new pull request: %v", err)
|
||||
return e2eResult, err
|
||||
}
|
||||
|
||||
// set pull request url
|
||||
e2eResult.githubPullRequestURL = pull.GetHTMLURL()
|
||||
e2eResult.pullRequestURL = url
|
||||
|
||||
log.Printf("created pull request %s", pull.GetHTMLURL())
|
||||
log.Printf("created pull request %s", url)
|
||||
|
||||
// defer closing pull request and delete remote branch
|
||||
defer cleanUp(t, pull.GetNumber(), branchName) // nolint: errcheck
|
||||
defer func() {
|
||||
err := cleanUp(ctx, t, pullId, branchName)
|
||||
if err != nil {
|
||||
log.Printf("Failed to cleanup: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// wait for atlantis to respond to webhook and autoplan.
|
||||
time.Sleep(2 * time.Second)
|
||||
@@ -133,9 +128,9 @@ func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
// waiting for atlantis run and finish
|
||||
maxLoops := 20
|
||||
i := 0
|
||||
for ; i < maxLoops && checkStatus(state); i++ {
|
||||
for ; i < maxLoops && t.vcsClient.IsAtlantisInProgress(state); i++ {
|
||||
time.Sleep(2 * time.Second)
|
||||
state, _ = getAtlantisStatus(t, branchName)
|
||||
state, _ = t.vcsClient.GetAtlantisStatus(ctx, branchName)
|
||||
if state == "" {
|
||||
log.Println("atlantis run hasn't started")
|
||||
continue
|
||||
@@ -149,52 +144,26 @@ func (t *E2ETester) Start() (*E2EResult, error) {
|
||||
log.Printf("atlantis run finished with status %q", state)
|
||||
e2eResult.testResult = state
|
||||
// check if atlantis run was a success
|
||||
if state != "success" {
|
||||
if !t.vcsClient.DidAtlantisSucceed(state) {
|
||||
return e2eResult, fmt.Errorf("atlantis run project type %q failed with %q status", t.projectType.Name, state)
|
||||
}
|
||||
|
||||
return e2eResult, nil
|
||||
}
|
||||
|
||||
func getAtlantisStatus(t *E2ETester, branchName string) (string, error) {
|
||||
// check repo status
|
||||
combinedStatus, _, err := t.githubClient.client.Repositories.GetCombinedStatus(t.githubClient.ctx, t.ownerName, t.repoName, branchName, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, status := range combinedStatus.Statuses {
|
||||
if status.GetContext() == "atlantis/plan" {
|
||||
return status.GetState(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func checkStatus(state string) bool {
|
||||
for _, s := range []string{"success", "error", "failure"} {
|
||||
if state == s {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func cleanUp(t *E2ETester, pullRequestNumber int, branchName string) error {
|
||||
func cleanUp(ctx context.Context, t *E2ETester, pullRequestNumber int, branchName string) error {
|
||||
// clean up
|
||||
pullClosed, _, err := t.githubClient.client.PullRequests.Edit(t.githubClient.ctx, t.ownerName, t.repoName, pullRequestNumber, &github.PullRequest{State: github.String("closed")})
|
||||
err := t.vcsClient.ClosePullRequest(ctx, pullRequestNumber)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while closing new pull request: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("closed pull request %d", pullClosed.GetNumber())
|
||||
log.Printf("closed pull request %d", pullRequestNumber)
|
||||
|
||||
deleteBranchName := fmt.Sprintf("%s/%s", "heads", branchName)
|
||||
_, err = t.githubClient.client.Git.DeleteRef(t.githubClient.ctx, t.ownerName, t.repoName, deleteBranchName)
|
||||
err = t.vcsClient.DeleteBranch(ctx, branchName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while deleting branch %s: %v", deleteBranchName, err)
|
||||
return fmt.Errorf("error while deleting branch %s: %v", branchName, err)
|
||||
}
|
||||
log.Printf("deleted branch %s", deleteBranchName)
|
||||
log.Printf("deleted branch %s", branchName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
159
e2e/github.go
@@ -15,12 +15,163 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/google/go-github/v63/github"
|
||||
)
|
||||
|
||||
type GithubClient struct {
|
||||
client *github.Client
|
||||
ctx context.Context
|
||||
username string
|
||||
client *github.Client
|
||||
username string
|
||||
ownerName string
|
||||
repoName string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewGithubClient() *GithubClient {
|
||||
|
||||
githubUsername := os.Getenv("ATLANTIS_GH_USER")
|
||||
if githubUsername == "" {
|
||||
log.Fatalf("ATLANTIS_GH_USER cannot be empty")
|
||||
}
|
||||
githubToken := os.Getenv("ATLANTIS_GH_TOKEN")
|
||||
if githubToken == "" {
|
||||
log.Fatalf("ATLANTIS_GH_TOKEN cannot be empty")
|
||||
}
|
||||
ownerName := os.Getenv("GITHUB_REPO_OWNER_NAME")
|
||||
if ownerName == "" {
|
||||
ownerName = "runatlantis"
|
||||
}
|
||||
repoName := os.Getenv("GITHUB_REPO_NAME")
|
||||
if repoName == "" {
|
||||
repoName = "atlantis-tests"
|
||||
}
|
||||
|
||||
// create github client
|
||||
tp := github.BasicAuthTransport{
|
||||
Username: strings.TrimSpace(githubUsername),
|
||||
Password: strings.TrimSpace(githubToken),
|
||||
}
|
||||
ghClient := github.NewClient(tp.Client())
|
||||
|
||||
return &GithubClient{
|
||||
client: ghClient,
|
||||
username: githubUsername,
|
||||
ownerName: ownerName,
|
||||
repoName: repoName,
|
||||
token: githubToken,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g GithubClient) Clone(cloneDir string) error {
|
||||
|
||||
repoURL := fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", g.username, g.token, g.ownerName, g.repoName)
|
||||
cloneCmd := exec.Command("git", "clone", repoURL, cloneDir)
|
||||
// git clone the repo
|
||||
log.Printf("git cloning into %q", cloneDir)
|
||||
if output, err := cloneCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g GithubClient) CreateAtlantisWebhook(ctx context.Context, hookURL string) (int64, error) {
|
||||
contentType := "json"
|
||||
hookConfig := &github.HookConfig{
|
||||
ContentType: &contentType,
|
||||
URL: &hookURL,
|
||||
}
|
||||
// create atlantis hook
|
||||
atlantisHook := &github.Hook{
|
||||
Events: []string{"issue_comment", "pull_request", "push"},
|
||||
Config: hookConfig,
|
||||
Active: github.Bool(true),
|
||||
}
|
||||
|
||||
hook, _, err := g.client.Repositories.CreateHook(ctx, g.ownerName, g.repoName, atlantisHook)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
log.Println(hook.GetURL())
|
||||
|
||||
return hook.GetID(), nil
|
||||
}
|
||||
|
||||
func (g GithubClient) DeleteAtlantisHook(ctx context.Context, hookID int64) error {
|
||||
_, err := g.client.Repositories.DeleteHook(ctx, g.ownerName, g.repoName, hookID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("deleted webhook id %d", hookID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g GithubClient) CreatePullRequest(ctx context.Context, title, branchName string) (string, int, error) {
|
||||
head := fmt.Sprintf("%s:%s", g.ownerName, branchName)
|
||||
body := ""
|
||||
base := "main"
|
||||
newPullRequest := &github.NewPullRequest{Title: &title, Head: &head, Body: &body, Base: &base}
|
||||
|
||||
pull, _, err := g.client.PullRequests.Create(ctx, g.ownerName, g.repoName, newPullRequest)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("error while creating new pull request: %v", err)
|
||||
}
|
||||
|
||||
// set pull request url
|
||||
return pull.GetHTMLURL(), pull.GetNumber(), nil
|
||||
|
||||
}
|
||||
|
||||
func (g GithubClient) GetAtlantisStatus(ctx context.Context, branchName string) (string, error) {
|
||||
// check repo status
|
||||
combinedStatus, _, err := g.client.Repositories.GetCombinedStatus(ctx, g.ownerName, g.repoName, branchName, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, status := range combinedStatus.Statuses {
|
||||
if status.GetContext() == "atlantis/plan" {
|
||||
return status.GetState(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (g GithubClient) ClosePullRequest(ctx context.Context, pullRequestNumber int) error {
|
||||
// clean up
|
||||
_, _, err := g.client.PullRequests.Edit(ctx, g.ownerName, g.repoName, pullRequestNumber, &github.PullRequest{State: github.String("closed")})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while closing new pull request: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
func (g GithubClient) DeleteBranch(ctx context.Context, branchName string) error {
|
||||
|
||||
deleteBranchName := fmt.Sprintf("%s/%s", "heads", branchName)
|
||||
_, err := g.client.Git.DeleteRef(ctx, g.ownerName, g.repoName, deleteBranchName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while deleting branch %s: %v", branchName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g GithubClient) IsAtlantisInProgress(state string) bool {
|
||||
for _, s := range []string{"success", "error", "failure"} {
|
||||
if state == s {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g GithubClient) DidAtlantisSucceed(state string) bool {
|
||||
return state == "success"
|
||||
}
|
||||
|
||||
181
e2e/gitlab.go
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright 2017 HootSuite Media Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the License);
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an AS IS BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Modified hereafter by contributors to runatlantis/atlantis.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
type GitlabClient struct {
|
||||
client *gitlab.Client
|
||||
username string
|
||||
ownerName string
|
||||
repoName string
|
||||
token string
|
||||
projectId int
|
||||
// A mapping from branch names to MR IDs
|
||||
branchToMR map[string]int
|
||||
}
|
||||
|
||||
func NewGitlabClient() *GitlabClient {
|
||||
|
||||
gitlabUsername := os.Getenv("ATLANTIS_GITLAB_USER")
|
||||
if gitlabUsername == "" {
|
||||
log.Fatalf("ATLANTIS_GITLAB_USER cannot be empty")
|
||||
}
|
||||
gitlabToken := os.Getenv("ATLANTIS_GITLAB_TOKEN")
|
||||
if gitlabToken == "" {
|
||||
log.Fatalf("ATLANTIS_GITLAB_TOKEN cannot be empty")
|
||||
}
|
||||
ownerName := os.Getenv("GITLAB_REPO_OWNER_NAME")
|
||||
if ownerName == "" {
|
||||
ownerName = "run-atlantis"
|
||||
}
|
||||
repoName := os.Getenv("GITLAB_REPO_NAME")
|
||||
if repoName == "" {
|
||||
repoName = "atlantis-tests"
|
||||
}
|
||||
|
||||
gitlabClient, err := gitlab.NewClient(gitlabToken)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
project, _, err := gitlabClient.Projects.GetProject(fmt.Sprintf("%s/%s", ownerName, repoName), &gitlab.GetProjectOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to find project: %v", err)
|
||||
}
|
||||
|
||||
return &GitlabClient{
|
||||
client: gitlabClient,
|
||||
username: gitlabUsername,
|
||||
ownerName: ownerName,
|
||||
repoName: repoName,
|
||||
token: gitlabToken,
|
||||
projectId: project.ID,
|
||||
branchToMR: make(map[string]int),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g GitlabClient) Clone(cloneDir string) error {
|
||||
|
||||
repoURL := fmt.Sprintf("https://%s:%s@gitlab.com/%s/%s.git", g.username, g.token, g.ownerName, g.repoName)
|
||||
cloneCmd := exec.Command("git", "clone", repoURL, cloneDir)
|
||||
// git clone the repo
|
||||
log.Printf("git cloning into %q", cloneDir)
|
||||
if output, err := cloneCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (g GitlabClient) CreateAtlantisWebhook(ctx context.Context, hookURL string) (int64, error) {
|
||||
hook, _, err := g.client.Projects.AddProjectHook(g.projectId, &gitlab.AddProjectHookOptions{
|
||||
URL: &hookURL,
|
||||
IssuesEvents: gitlab.Ptr(true),
|
||||
MergeRequestsEvents: gitlab.Ptr(true),
|
||||
PushEvents: gitlab.Ptr(true),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
log.Printf("created webhook for %s", hook.URL)
|
||||
return int64(hook.ID), err
|
||||
}
|
||||
|
||||
func (g GitlabClient) DeleteAtlantisHook(ctx context.Context, hookID int64) error {
|
||||
_, err := g.client.Projects.DeleteProjectHook(g.projectId, int(hookID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("deleted webhook id %d", hookID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g GitlabClient) CreatePullRequest(ctx context.Context, title, branchName string) (string, int, error) {
|
||||
|
||||
mr, _, err := g.client.MergeRequests.CreateMergeRequest(g.projectId, &gitlab.CreateMergeRequestOptions{
|
||||
Title: gitlab.Ptr(title),
|
||||
SourceBranch: gitlab.Ptr(branchName),
|
||||
TargetBranch: gitlab.Ptr("main"),
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("error while creating new pull request: %v", err)
|
||||
}
|
||||
g.branchToMR[branchName] = mr.IID
|
||||
return mr.WebURL, mr.IID, nil
|
||||
|
||||
}
|
||||
|
||||
func (g GitlabClient) GetAtlantisStatus(ctx context.Context, branchName string) (string, error) {
|
||||
|
||||
pipelineInfos, _, err := g.client.MergeRequests.ListMergeRequestPipelines(g.projectId, g.branchToMR[branchName])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Possible todo: determine which status in the pipeline we care about?
|
||||
if len(pipelineInfos) != 1 {
|
||||
return "", fmt.Errorf("unexpected pipelines: %d", len(pipelineInfos))
|
||||
}
|
||||
pipelineInfo := pipelineInfos[0]
|
||||
pipeline, _, err := g.client.Pipelines.GetPipeline(g.projectId, pipelineInfo.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return pipeline.Status, nil
|
||||
}
|
||||
|
||||
func (g GitlabClient) ClosePullRequest(ctx context.Context, pullRequestNumber int) error {
|
||||
// clean up
|
||||
_, _, err := g.client.MergeRequests.UpdateMergeRequest(g.projectId, pullRequestNumber, &gitlab.UpdateMergeRequestOptions{
|
||||
StateEvent: gitlab.Ptr("close"),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while closing new pull request: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
func (g GitlabClient) DeleteBranch(ctx context.Context, branchName string) error {
|
||||
_, err := g.client.Branches.DeleteBranch(g.projectId, branchName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while deleting branch %s: %v", branchName, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (g GitlabClient) IsAtlantisInProgress(state string) bool {
|
||||
// From https://docs.gitlab.com/ee/api/pipelines.html
|
||||
// created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled
|
||||
for _, s := range []string{"success", "failed", "canceled", "skipped"} {
|
||||
if state == s {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g GitlabClient) DidAtlantisSucceed(state string) bool {
|
||||
return state == "success"
|
||||
}
|
||||
13
e2e/go.mod
@@ -1,13 +1,22 @@
|
||||
module github.com/runatlantis/atlantis/e2e
|
||||
|
||||
go 1.21
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/google/go-github/v57 v57.0.0
|
||||
github.com/google/go-github/v63 v63.0.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/xanzy/go-gitlab v0.109.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
golang.org/x/net v0.23.0 // indirect
|
||||
golang.org/x/oauth2 v0.6.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
)
|
||||
|
||||
51
e2e/go.sum
@@ -1,13 +1,60 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
|
||||
github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
|
||||
github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE=
|
||||
github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE=
|
||||
github.com/xanzy/go-gitlab v0.109.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
96
e2e/main.go
@@ -15,13 +15,12 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
@@ -36,32 +35,29 @@ type Project struct {
|
||||
ApplyCommand string
|
||||
}
|
||||
|
||||
func getVCSClient() (VCSClient, error) {
|
||||
|
||||
if os.Getenv("ATLANTIS_GH_USER") != "" {
|
||||
log.Print("Running tests for github")
|
||||
return NewGithubClient(), nil
|
||||
}
|
||||
if os.Getenv("ATLANTIS_GITLAB_USER") != "" {
|
||||
log.Print("Running tests for gitlab")
|
||||
return NewGitlabClient(), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("could not determine which vcs client")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
githubUsername := os.Getenv("GITHUB_USERNAME")
|
||||
if githubUsername == "" {
|
||||
log.Fatalf("GITHUB_USERNAME cannot be empty")
|
||||
}
|
||||
githubToken := os.Getenv("GITHUB_PASSWORD")
|
||||
if githubToken == "" {
|
||||
log.Fatalf("GITHUB_PASSWORD cannot be empty")
|
||||
}
|
||||
atlantisURL := os.Getenv("ATLANTIS_URL")
|
||||
if atlantisURL == "" {
|
||||
atlantisURL = defaultAtlantisURL
|
||||
}
|
||||
// add /events to the url
|
||||
atlantisURL = fmt.Sprintf("%s/events", atlantisURL)
|
||||
ownerName := os.Getenv("GITHUB_REPO_OWNER_NAME")
|
||||
if ownerName == "" {
|
||||
ownerName = "runatlantis"
|
||||
}
|
||||
repoName := os.Getenv("GITHUB_REPO_NAME")
|
||||
if repoName == "" {
|
||||
repoName = "atlantis-tests"
|
||||
}
|
||||
// using https to clone the repo
|
||||
repoURL := fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", githubUsername, githubToken, ownerName, repoName)
|
||||
|
||||
cloneDirRoot := os.Getenv("CLONE_DIR")
|
||||
if cloneDirRoot == "" {
|
||||
cloneDirRoot = "/tmp/atlantis-tests"
|
||||
@@ -74,38 +70,31 @@ func main() {
|
||||
log.Fatalf("failed to clean dir %q before cloning, attempting to continue: %v", cloneDirRoot, err)
|
||||
}
|
||||
|
||||
// create github client
|
||||
tp := github.BasicAuthTransport{
|
||||
Username: strings.TrimSpace(githubUsername),
|
||||
Password: strings.TrimSpace(githubToken),
|
||||
vcsClient, err := getVCSClient()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get vcs client: %v", err)
|
||||
}
|
||||
ghClient := github.NewClient(tp.Client())
|
||||
|
||||
githubClient := &GithubClient{client: ghClient, ctx: context.Background(), username: githubUsername}
|
||||
|
||||
ctx := context.Background()
|
||||
// we create atlantis hook once for the repo, since the atlantis server can handle multiple requests
|
||||
log.Printf("creating atlantis webhook with %s url", atlantisURL)
|
||||
hookID, err := createAtlantisWebhook(githubClient, ownerName, repoName, atlantisURL)
|
||||
hookID, err := vcsClient.CreateAtlantisWebhook(ctx, atlantisURL)
|
||||
if err != nil {
|
||||
log.Fatalf("error creating atlantis webhook: %v", err)
|
||||
}
|
||||
|
||||
// create e2e test
|
||||
e2e := E2ETester{
|
||||
githubClient: githubClient,
|
||||
repoURL: repoURL,
|
||||
ownerName: ownerName,
|
||||
repoName: repoName,
|
||||
vcsClient: vcsClient,
|
||||
hookID: hookID,
|
||||
cloneDirRoot: cloneDirRoot,
|
||||
}
|
||||
|
||||
// start e2e tests
|
||||
results, err := startTests(e2e)
|
||||
results, err := startTests(ctx, e2e)
|
||||
log.Printf("Test Results\n---------------------------\n")
|
||||
for _, result := range results {
|
||||
fmt.Printf("Project Type: %s \n", result.projectType)
|
||||
fmt.Printf("Pull Request Link: %s \n", result.githubPullRequestURL)
|
||||
fmt.Printf("Pull Request Link: %s \n", result.pullRequestURL)
|
||||
fmt.Printf("Atlantis Run Status: %s \n", result.testResult)
|
||||
fmt.Println("---------------------------")
|
||||
}
|
||||
@@ -115,52 +104,21 @@ func main() {
|
||||
|
||||
}
|
||||
|
||||
func createAtlantisWebhook(g *GithubClient, ownerName string, repoName string, hookURL string) (int64, error) {
|
||||
// create atlantis hook
|
||||
atlantisHook := &github.Hook{
|
||||
Events: []string{"issue_comment", "pull_request", "push"},
|
||||
Config: map[string]interface{}{
|
||||
"url": hookURL,
|
||||
"content_type": "json",
|
||||
},
|
||||
Active: github.Bool(true),
|
||||
}
|
||||
|
||||
// moved to github.go
|
||||
hook, _, err := g.client.Repositories.CreateHook(g.ctx, ownerName, repoName, atlantisHook)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
log.Println(hook.GetURL())
|
||||
|
||||
return hook.GetID(), nil
|
||||
}
|
||||
|
||||
func deleteAtlantisHook(g *GithubClient, ownerName string, repoName string, hookID int64) error {
|
||||
_, err := g.client.Repositories.DeleteHook(g.ctx, ownerName, repoName, hookID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("deleted webhook id %d", hookID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanDir(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func startTests(e2e E2ETester) ([]*E2EResult, error) {
|
||||
func startTests(ctx context.Context, e2e E2ETester) ([]*E2EResult, error) {
|
||||
var testResults []*E2EResult
|
||||
var testErrors *multierror.Error
|
||||
// delete webhook when we are done running tests
|
||||
defer deleteAtlantisHook(e2e.githubClient, e2e.ownerName, e2e.repoName, e2e.hookID) // nolint: errcheck
|
||||
defer e2e.vcsClient.DeleteAtlantisHook(ctx, e2e.hookID) // nolint: errcheck
|
||||
|
||||
for _, projectType := range projectTypes {
|
||||
log.Printf("starting e2e test for project type %q", projectType.Name)
|
||||
e2e.projectType = projectType
|
||||
// start e2e test
|
||||
result, err := e2e.Start()
|
||||
result, err := e2e.Start(ctx)
|
||||
testResults = append(testResults, result)
|
||||
testErrors = multierror.Append(testErrors, err)
|
||||
}
|
||||
|
||||
28
e2e/vcs.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017 HootSuite Media Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the License);
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an AS IS BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Modified hereafter by contributors to runatlantis/atlantis.
|
||||
|
||||
package main
|
||||
|
||||
import "context"
|
||||
|
||||
type VCSClient interface {
|
||||
Clone(cloneDir string) error
|
||||
CreateAtlantisWebhook(ctx context.Context, hookURL string) (int64, error)
|
||||
DeleteAtlantisHook(ctx context.Context, hookID int64) error
|
||||
CreatePullRequest(ctx context.Context, title, branchName string) (string, int, error)
|
||||
GetAtlantisStatus(ctx context.Context, branchName string) (string, error)
|
||||
ClosePullRequest(ctx context.Context, pullRequestNumber int) error
|
||||
DeleteBranch(ctx context.Context, branchName string) error
|
||||
IsAtlantisInProgress(state string) bool
|
||||
DidAtlantisSucceed(state string) bool
|
||||
}
|
||||
128
go.mod
@@ -1,63 +1,62 @@
|
||||
module github.com/runatlantis/atlantis
|
||||
|
||||
go 1.21
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/alicebob/miniredis/v2 v2.31.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.8.0
|
||||
github.com/briandowns/spinner v1.23.0
|
||||
code.gitea.io/sdk/gitea v0.19.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/alicebob/miniredis/v2 v2.33.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.11.0
|
||||
github.com/briandowns/spinner v1.23.1
|
||||
github.com/cactus/go-statsd-client/v5 v5.1.0
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
|
||||
github.com/go-playground/validator/v10 v10.16.0
|
||||
github.com/go-test/deep v1.1.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/google/go-github/v57 v57.0.0
|
||||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/go-github/v63 v63.0.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/hashicorp/go-getter/v2 v2.2.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/go-getter/v2 v2.2.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72
|
||||
github.com/hashicorp/hc-install v0.9.0
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20240801114854-6714b46f5fe4
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/kr/pretty v0.3.1
|
||||
github.com/mcdafydd/go-azuredevops v0.12.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/petergtz/pegomock/v4 v4.0.0
|
||||
github.com/opentofu/tofudl v0.0.0-20240923062014-8c1e00f33ce6
|
||||
github.com/petergtz/pegomock/v4 v4.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/redis/go-redis/v9 v9.3.0
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456
|
||||
github.com/slack-go/slack v0.12.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
|
||||
github.com/slack-go/slack v0.14.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.18.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/uber-go/tally/v4 v4.1.10
|
||||
github.com/urfave/negroni/v3 v3.0.0
|
||||
github.com/warrensbox/terraform-switcher v0.1.1-0.20230206012955-d7dfd1b44605
|
||||
github.com/xanzy/go-gitlab v0.94.0
|
||||
go.etcd.io/bbolt v1.3.8
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.org/x/text v0.14.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
github.com/urfave/negroni/v3 v3.1.1
|
||||
github.com/xanzy/go-gitlab v0.109.0
|
||||
go.etcd.io/bbolt v1.3.11
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/term v0.24.0
|
||||
golang.org/x/text v0.18.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/agext/levenshtein v1.2.3
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.19.1
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.22.0
|
||||
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
)
|
||||
@@ -65,38 +64,47 @@ require (
|
||||
require github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-github/v56 v56.0.0 // indirect
|
||||
github.com/google/go-github/v62 v62.0.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
@@ -104,7 +112,7 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/onsi/gomega v1.27.6 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
@@ -113,23 +121,25 @@ require (
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.13.2 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
github.com/zclconf/go-cty v1.14.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.16.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
316
go.sum
@@ -30,17 +30,28 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y=
|
||||
code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -50,10 +61,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.31.0 h1:ObEFUNlJwoIiyjxdrYF0QIDE7qXcLc7D3WpSH4c22PU=
|
||||
github.com/alicebob/miniredis/v2 v2.31.0/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
@@ -66,14 +75,15 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.8.0 h1:yUmoVv70H3J4UOqxqsee39+KlXxNEDfTbAp8c/qULKk=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.8.0/go.mod h1:fmPmvCiBWhJla3zDv9ZTQSZc8AbwyRnGW1yg5ep1Pcs=
|
||||
github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A=
|
||||
github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.11.0 h1:R9d0v+iobRHSaE4wKUnXFiZp53AL4ED5MzgEMwGTZag=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.11.0/go.mod h1:0LWKQwOHewXO/1acI6TtyE0Xc4ObDb2rFN7eHBAG71M=
|
||||
github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650=
|
||||
github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cactus/go-statsd-client/v5 v5.0.0/go.mod h1:COEvJ1E+/E2L4q6QE5CkjWPi4eeDw9maJBMIuMPBZbY=
|
||||
github.com/cactus/go-statsd-client/v5 v5.1.0 h1:sbbdfIl9PgisjEoXzvXI1lwUKWElngsjJKaZeC021P4=
|
||||
github.com/cactus/go-statsd-client/v5 v5.1.0/go.mod h1:COEvJ1E+/E2L4q6QE5CkjWPi4eeDw9maJBMIuMPBZbY=
|
||||
@@ -86,27 +96,44 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
||||
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -118,8 +145,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@@ -128,23 +155,24 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -184,13 +212,12 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v56 v56.0.0 h1:TysL7dMa/r7wsQi44BjqlwaHvwlFlqkK8CtBWCX3gb4=
|
||||
github.com/google/go-github/v56 v56.0.0/go.mod h1:D8cdcX98YWJvi7TLo7zM4/h8ZTx6u6fwGEkCdisopo0=
|
||||
github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
|
||||
github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
|
||||
github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
|
||||
github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
|
||||
github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE=
|
||||
github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
@@ -209,56 +236,55 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter/v2 v2.2.1 h1:2JXqPZs1Jej67RtdTi0YZaEB2hEFB3fkBA4cPYKQwFQ=
|
||||
github.com/hashicorp/go-getter/v2 v2.2.1/go.mod h1:EcJx6oZE8hmGuRR1l38QrfnyiujQbwsEAn11eHv6l2M=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk=
|
||||
github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE=
|
||||
github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
|
||||
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72 h1:nZ5gGjbe5o7XUu1d7j+Y5Ztcxlp+yaumTKH9i0D3wlg=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72/go.mod h1:l8HcFPm9cQh6Q0KSWoYPiePqMvRFenybP1CH2MjKdlg=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20240801114854-6714b46f5fe4 h1:RwY5HBgtBZ997UtKJAO2Rx+94ETyevwWEVXWx1SL5YY=
|
||||
github.com/hashicorp/terraform-config-inspect v0.0.0-20240801114854-6714b46f5fe4/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -268,9 +294,11 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@@ -281,27 +309,24 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mcdafydd/go-azuredevops v0.12.1 h1:WxwLVyGuJ8oL7uWQp1/J6GefX1wMQQZUHWRGsrm+uE8=
|
||||
github.com/mcdafydd/go-azuredevops v0.12.1/go.mod h1:B4UDyn7WEj1/97f45j3VnzEfkWKe05+/dCcAPdOET4A=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -312,7 +337,6 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
@@ -330,10 +354,14 @@ github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/petergtz/pegomock/v4 v4.0.0 h1:BIGMUof4NXc+xBbuFk0VBfK5Ls7DplcP+LWz4hfYWsY=
|
||||
github.com/petergtz/pegomock/v4 v4.0.0/go.mod h1:Xscaw/kXYcuh9sGsns+If19FnSMMQy4Wz60YJTn3XOU=
|
||||
github.com/opentofu/tofudl v0.0.0-20240923062014-8c1e00f33ce6 h1:+1yJm0gEoDaxYmMhmmU3gRAOMx3A43z84bokm1dQroU=
|
||||
github.com/opentofu/tofudl v0.0.0-20240923062014-8c1e00f33ce6/go.mod h1:CD1BhvxxNPp4ZBwNBjWycf5isG9UaPrzfE7J/E/s6RY=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/petergtz/pegomock/v4 v4.1.0 h1:Reoy2rlwshuxNaD2ZWp5TrSCrmoFH5SSLHb5U1z2pog=
|
||||
github.com/petergtz/pegomock/v4 v4.1.0/go.mod h1:Xscaw/kXYcuh9sGsns+If19FnSMMQy4Wz60YJTn3XOU=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -366,8 +394,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
|
||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
|
||||
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -380,46 +408,47 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456 h1:6dExqsYngGEiixqa1vmtlUd+zbyISilg0Cf3GWVdeYM=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
|
||||
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc=
|
||||
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/slack-go/slack v0.12.3 h1:92/dfFU8Q5XP6Wp5rr5/T5JHLM5c5Smtn53fhToAP88=
|
||||
github.com/slack-go/slack v0.12.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/slack-go/slack v0.14.0 h1:6c0UTfbRnvRssZUsZ2qe0Iu07VAMPjRqOa6oX8ewF4k=
|
||||
github.com/slack-go/slack v0.14.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
|
||||
github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
@@ -429,22 +458,24 @@ github.com/uber-go/tally/v4 v4.1.10 h1:2GSX7Tmq26wjAvOtQEc5EvRROIkX2OX4vpROt6mlR
|
||||
github.com/uber-go/tally/v4 v4.1.10/go.mod h1:pPR56rjthjtLB8xQlEx2I1VwAwRGCh/i4xMUcmG+6z4=
|
||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/negroni/v3 v3.0.0 h1:Vo8CeZfu1lFR9gW8GnAb6dOGCJyijfil9j/jKKc/JhU=
|
||||
github.com/urfave/negroni/v3 v3.0.0/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs=
|
||||
github.com/warrensbox/terraform-switcher v0.1.1-0.20230206012955-d7dfd1b44605 h1:bRt3KvPapqnO3s9XenyU4COpU9X7cNW3BMELyHRxuSs=
|
||||
github.com/warrensbox/terraform-switcher v0.1.1-0.20230206012955-d7dfd1b44605/go.mod h1:saryXNaL624mlulV138FP+HhVw7IpvETUXLS3nTvH1g=
|
||||
github.com/xanzy/go-gitlab v0.94.0 h1:GmBl2T5zqUHqyjkxFSvsT7CbelGdAH/dmBqUBqS+4BE=
|
||||
github.com/xanzy/go-gitlab v0.94.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
github.com/urfave/negroni/v3 v3.1.1 h1:6MS4nG9Jk/UuCACaUlNXCbiKa0ywF9LXz5dGu09v8hw=
|
||||
github.com/urfave/negroni/v3 v3.1.1/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs=
|
||||
github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE=
|
||||
github.com/xanzy/go-gitlab v0.109.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
|
||||
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0=
|
||||
github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
|
||||
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -453,12 +484,13 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -466,10 +498,12 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -480,8 +514,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -503,6 +537,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -537,8 +574,10 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -547,8 +586,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -559,10 +598,12 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -602,14 +643,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -617,9 +662,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -666,8 +714,9 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -694,8 +743,8 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -749,8 +798,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -760,12 +809,13 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
37
goss.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
# See: https://github.com/goss-org/goss/blob/master/docs/gossfile.md
|
||||
|
||||
command:
|
||||
# ensure atlantis is available
|
||||
atlantis-available:
|
||||
exec: "atlantis version"
|
||||
exit-status: 0
|
||||
stdout: []
|
||||
stderr: []
|
||||
|
||||
# ensure conftest is available
|
||||
conftest-available:
|
||||
exec: "conftest -v"
|
||||
exit-status: 0
|
||||
stdout: []
|
||||
stderr: []
|
||||
|
||||
# ensure git-lfs is available
|
||||
git-lfs-available:
|
||||
exec: "git-lfs -v"
|
||||
exit-status: 0
|
||||
stdout: []
|
||||
stderr: []
|
||||
|
||||
# ensure terraform is available
|
||||
terraform-available:
|
||||
exec: "terraform version"
|
||||
exit-status: 0
|
||||
stdout: []
|
||||
stderr: []
|
||||
|
||||
# ensure tofu binary is available
|
||||
tofu-available:
|
||||
exec: "tofu version"
|
||||
exit-status: 0
|
||||
stdout: []
|
||||
stderr: []
|
||||
32
netlify.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
# Netlify Config, https://www.netlify.com/docs/netlify-toml-reference/
|
||||
[build]
|
||||
base = "/"
|
||||
command = "npm install && npm run website:build"
|
||||
publish = "runatlantis.io/.vitepress/dist/"
|
||||
|
||||
[[redirects]]
|
||||
force = true
|
||||
from = "/guide/getting-started.html"
|
||||
status = 301
|
||||
to = "/guide/"
|
||||
|
||||
[[redirects]]
|
||||
force = true
|
||||
from = "/docs/atlantis-yaml-reference.html"
|
||||
status = 301
|
||||
to = "/docs/repo-level-atlantis-yaml.html"
|
||||
|
||||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
Cache-Control = "public, max-age=86400"
|
||||
Referrer-Policy = "no-referrer"
|
||||
Strict-Transport-Security = "max-age=86400; includeSubDomains; preload"
|
||||
X-Content-Type-Options = "nosniff"
|
||||
X-Frame-Options = "DENY"
|
||||
X-XSS-Protection = "1; mode=block"
|
||||
|
||||
[[headers]]
|
||||
for = "*.html"
|
||||
[headers.values]
|
||||
Content-Type = "text/html; charset=UTF-8"
|
||||
4118
package-lock.json
generated
Normal file
21
package.json
@@ -1,12 +1,23 @@
|
||||
{
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@vuepress/plugin-docsearch": "2.0.0-beta.66",
|
||||
"@vuepress/plugin-google-analytics": "2.0.0-rc.0",
|
||||
"vuepress": "2.0.0-beta.66"
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@types/node": "^20.12.12",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdownlint-cli": "^0.40.0",
|
||||
"mermaid": "^10.9.1",
|
||||
"sitemap-ts": "^1.7.3",
|
||||
"vitepress": "^1.2.3",
|
||||
"vitepress-plugin-mermaid": "^2.0.16",
|
||||
"vue": "^3.4.27"
|
||||
},
|
||||
"scripts": {
|
||||
"website:dev": "vuepress dev runatlantis.io",
|
||||
"website:build": "NODE_OPTIONS=--openssl-legacy-provider vuepress build runatlantis.io"
|
||||
"website:dev": "vitepress dev --host localhost --port 8080 runatlantis.io",
|
||||
"website:lint": "markdownlint runatlantis.io",
|
||||
"website:lint-fix": "markdownlint --fix runatlantis.io",
|
||||
"website:build": "vitepress build runatlantis.io",
|
||||
"e2e": "playwright test"
|
||||
}
|
||||
}
|
||||
|
||||
3
playwright.config.cjs
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
testDir: './runatlantis.io/e2e'
|
||||
};
|
||||
3315
pnpm-lock.yaml
generated
Normal file
89
runatlantis.io/.vitepress/components/Banner.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { ref, watchEffect } from 'vue';
|
||||
const el = ref<HTMLElement>();
|
||||
const { height } = useElementSize(el);
|
||||
watchEffect(() => {
|
||||
if (height.value) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--vp-layout-top-height',
|
||||
`${height.value + 16}px`
|
||||
);
|
||||
}
|
||||
});
|
||||
const dismiss = () => {
|
||||
localStorage.setItem(
|
||||
'survey-banner',
|
||||
(Date.now() + 8.64e7 * 1).toString() // current time + 1 day
|
||||
);
|
||||
document.documentElement.classList.add('banner-dismissed');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <div ref="el" class="banner">
|
||||
<div class="text">
|
||||
</div>
|
||||
|
||||
<button type="button" @click="dismiss">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.banner-dismissed {
|
||||
--vp-layout-top-height: 0px !important;
|
||||
}
|
||||
html {
|
||||
--vp-layout-top-height: 88px;
|
||||
}
|
||||
@media (min-width: 375px) {
|
||||
html {
|
||||
--vp-layout-top-height: 64px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
--vp-layout-top-height: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.banner-dismissed .banner {
|
||||
display: none;
|
||||
}
|
||||
.banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: var(--vp-z-index-layout-top);
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
background: #383636;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.text {
|
||||
flex: 1;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
5
runatlantis.io/.vitepress/components/shims.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent;
|
||||
export default component;
|
||||
}
|
||||
133
runatlantis.io/.vitepress/config.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { generateSitemap as sitemap } from "sitemap-ts"
|
||||
import footnote from 'markdown-it-footnote'
|
||||
import { defineConfig } from 'vitepress';
|
||||
import * as navbars from "./navbars";
|
||||
import * as sidebars from "./sidebars";
|
||||
import { withMermaid } from "vitepress-plugin-mermaid";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
const config = defineConfig({
|
||||
title: 'Atlantis',
|
||||
description: 'Atlantis: Terraform Pull Request Automation',
|
||||
lang: 'en-US',
|
||||
lastUpdated: true,
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
lang: 'en-US',
|
||||
themeConfig: {
|
||||
nav: navbars.en,
|
||||
sidebar: sidebars.en,
|
||||
},
|
||||
},
|
||||
},
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
editLink: {
|
||||
pattern: 'https://github.com/runatlantis/atlantis/edit/main/runatlantis.io/:path'
|
||||
},
|
||||
// headline "depth" the right nav will show for its TOC
|
||||
//
|
||||
// https://vitepress.dev/reference/frontmatter-config#outline
|
||||
outline: [2, 3],
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
// We internally discussed how this API key is exposed in the code and decided
|
||||
// that it is a non-issue because this API key can easily be extracted by
|
||||
// looking at the browser dev tools since the key is used in the API requests.
|
||||
apiKey: '3b733dff1539ca3a210775860301fa86',
|
||||
indexName: 'runatlantis',
|
||||
appId: 'BH4D9OD16A',
|
||||
locales: {
|
||||
'/': {
|
||||
placeholder: 'Search Documentation',
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Search Documentation',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: "slack", link: "https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw" },
|
||||
{ icon: "twitter", link: "https://twitter.com/runatlantis" },
|
||||
{ icon: "github", link: "https://github.com/runatlantis/atlantis" },
|
||||
],
|
||||
},
|
||||
// SEO Improvement - sitemap.xml & robots.txt
|
||||
buildEnd: async ({ outDir }) => {
|
||||
sitemap({
|
||||
hostname: "https://www.runatlantis.io/",
|
||||
outDir: outDir,
|
||||
generateRobotsTxt: true,
|
||||
})
|
||||
},
|
||||
head: [
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-196x196.png', sizes: '196x196' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-96x96.png', sizes: '96x96' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-32x32.png', sizes: '32x32' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-16x16.png', sizes: '16x16' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-128.png', sizes: '128x128' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '57x57', href: '/apple-touch-icon-57x57.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '114x114', href: '/apple-touch-icon-114x114.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '72x72', href: '/apple-touch-icon-72x72.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '144x144', href: '/apple-touch-icon-144x144.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '60x60', href: '/apple-touch-icon-60x60.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '120x120', href: '/apple-touch-icon-120x120.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '76x76', href: '/apple-touch-icon-76x76.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '152x152', href: '/apple-touch-icon-152x152.png' }],
|
||||
['meta', { name: 'msapplication-TileColor', content: '#FFFFFF' }],
|
||||
['meta', { name: 'msapplication-TileImage', content: '/mstile-144x144.png' }],
|
||||
['meta', { name: 'msapplication-square70x70logo', content: '/mstile-70x70.png' }],
|
||||
['meta', { name: 'msapplication-square150x150logo', content: '/mstile-150x150.png' }],
|
||||
['meta', { name: 'msapplication-wide310x150logo', content: '/mstile-310x150.png' }],
|
||||
['meta', { name: 'msapplication-square310x310logo', content: '/mstile-310x310.png' }],
|
||||
['link', { rel: 'stylesheet', sizes: '152x152', href: 'https://fonts.googleapis.com/css?family=Lato:400,900' }],
|
||||
['meta', { name: 'google-site-verification', content: 'kTnsDBpHqtTNY8oscYxrQeeiNml2d2z-03Ct9wqeCeE' }],
|
||||
// google analytics
|
||||
[
|
||||
'script',
|
||||
{ async: '', src: 'https://www.googletagmanager.com/gtag/js?id=UA-6850151-3' }
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{},
|
||||
`window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-6850151-3');`
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{ id: 'restore-banner-preference' },
|
||||
`
|
||||
(() => {
|
||||
const restore = (key, cls, def = false) => {
|
||||
const saved = localStorage.getItem(key);
|
||||
if (saved ? saved !== 'false' && new Date() < saved : def) {
|
||||
document.documentElement.classList.add(cls);
|
||||
}
|
||||
};
|
||||
restore('survey-banner', 'banner-dismissed');
|
||||
})();`,
|
||||
]
|
||||
],
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
md.use(footnote)
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
fs: {
|
||||
cachedChecks: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default withMermaid(config)
|
||||
9
runatlantis.io/.vitepress/navbars.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
const en = [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Guide", link: "/guide" },
|
||||
{ text: "Docs", link: "/docs" },
|
||||
{ text: "Contributing", link: "/contributing" },
|
||||
{ text: "Blog", link: "/blog" },
|
||||
];
|
||||
|
||||
export { en };
|
||||
165
runatlantis.io/.vitepress/sidebars.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
const en = [
|
||||
{
|
||||
text: "Guide",
|
||||
link: "/guide",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: "Test Drive", link: "/guide/test-drive" },
|
||||
{ text: "Testing locally", link: "/guide/testing-locally" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Docs",
|
||||
link: "/docs",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Installing Atlantis",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Installing Guide", link: "/docs/installation-guide" },
|
||||
{ text: "Requirements", link: "/docs/requirements" },
|
||||
{ text: "Git Host Access Credentials", link: "/docs/access-credentials" },
|
||||
{ text: "Webhook Secrets", link: "/docs/webhook-secrets" },
|
||||
{ text: "Deployment", link: "/docs/deployment" },
|
||||
{ text: "Configuring Webhooks", link: "/docs/configuring-webhooks" },
|
||||
{ text: "Provider Credentials", link: "/docs/provider-credentials" },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Configuring Atlantis",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Overview", link: "/docs/configuring-atlantis" },
|
||||
{ text: "Server Configuration", link: "/docs/server-configuration" },
|
||||
{ text: "Server Side Repo Config", link: "/docs/server-side-repo-config" },
|
||||
{ text: "Pre Workflow Hooks", link: "/docs/pre-workflow-hooks" },
|
||||
{ text: "Post Workflow Hooks", link: "/docs/post-workflow-hooks" },
|
||||
{ text: "Conftest Policy Checking", link: "/docs/policy-checking" },
|
||||
{ text: "Custom Workflows", link: "/docs/custom-workflows" },
|
||||
{ text: "Repo and Project Permissions", link: "/docs/repo-and-project-permissions" },
|
||||
{ text: "Repo Level atlantis.yaml", link: "/docs/repo-level-atlantis-yaml" },
|
||||
{ text: "Upgrading atlantis.yaml", link: "/docs/upgrading-atlantis-yaml" },
|
||||
{ text: "Command Requirements", link: "/docs/command-requirements" },
|
||||
{ text: "Checkout Strategy", link: "/docs/checkout-strategy" },
|
||||
{ text: "Terraform Versions", link: "/docs/terraform-versions" },
|
||||
{ text: "Terraform Cloud", link: "/docs/terraform-cloud" },
|
||||
{ text: "Using Slack Hooks", link: "/docs/using-slack-hooks" },
|
||||
{ text: "Stats", link: "/docs/stats" },
|
||||
{ text: "FAQ", link: "/docs/faq" },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Using Atlantis",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Overview", link: "/docs/using-atlantis" },
|
||||
{ text: "API endpoints", link: "/docs/api-endpoints" },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'How Atlantis Works',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/docs/how-atlantis-works', },
|
||||
{ text: 'Locking', link: '/docs/locking', },
|
||||
{ text: 'Autoplanning', link: '/docs/autoplanning', },
|
||||
{ text: 'Automerging', link: '/docs/automerging', },
|
||||
{ text: 'Security', link: '/docs/security', },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Real-time Terraform Logs',
|
||||
link: '/docs/streaming-logs',
|
||||
},
|
||||
{
|
||||
text: 'Troubleshooting',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'HTTPS, SSL, TLS', 'link': '/docs/troubleshooting-https', },
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Contributing",
|
||||
link: "/contributing",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Implementation Details',
|
||||
items: [
|
||||
{ text: "Events Controller", link: "/contributing/events-controller" },
|
||||
]
|
||||
},
|
||||
{ text: "Glossary", link: "/contributing/glossary" },
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
text: "Blog",
|
||||
link: "/blog",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: "2024",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Integrating Atlantis with OpenTofu",
|
||||
link: "/blog/2024/integrating-atlantis-with-opentofu"
|
||||
},
|
||||
{
|
||||
text: "Atlantis User Survey Results",
|
||||
link: "/blog/2024/april-2024-survey-results"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "2019",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "4 Reasons To Try HashiCorp's (New) Free Terraform Remote State Storage",
|
||||
link: "/blog/2019/4-reasons-to-try-hashicorps-new-free-terraform-remote-state-storage"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "2018",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "I'm Joining HashiCorp!",
|
||||
link: "/blog/2018/joining-hashicorp"
|
||||
},
|
||||
{
|
||||
text: "Putting The Dev Into DevOps: Why Your Developers Should Write Terraform Too",
|
||||
link: "/blog/2018/putting-the-dev-into-devops-why-your-developers-should-write-terraform-too"
|
||||
},
|
||||
{
|
||||
text: "Atlantis 0.4.4 Now Supports Bitbucket",
|
||||
link: "/blog/2018/atlantis-0-4-4-now-supports-bitbucket"
|
||||
},
|
||||
{
|
||||
text: "Terraform And The Dangers Of Applying Locally",
|
||||
link: "/blog/2018/terraform-and-the-dangers-of-applying-locally"
|
||||
},
|
||||
{
|
||||
text: "Hosting Our Static Site over SSL with S3, ACM, CloudFront and Terraform",
|
||||
link: "/blog/2018/hosting-our-static-site-over-ssl-with-s3-acm-cloudfront-and-terraform"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "2017",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Introducing Atlantis", link: "/blog/2017/introducing-atlantis" },
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export { en }
|
||||
11
runatlantis.io/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import { defineAsyncComponent, h } from 'vue';
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'layout-top': () => h(defineAsyncComponent(() => import('../components/Banner.vue')))
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,190 +0,0 @@
|
||||
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
|
||||
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
|
||||
import { getDirname, path } from '@vuepress/utils'
|
||||
import { defaultTheme, defineUserConfig } from 'vuepress'
|
||||
|
||||
const __dirname = getDirname(import.meta.url)
|
||||
|
||||
export default defineUserConfig({
|
||||
alias: {
|
||||
'@theme/Home.vue': path.resolve(__dirname, './theme/components/Home.vue'),
|
||||
},
|
||||
locales: {
|
||||
'/': {
|
||||
lang: 'en-US',
|
||||
title: 'Atlantis',
|
||||
description: 'Atlantis: Terraform Pull Request Automation',
|
||||
},
|
||||
/*
|
||||
'/es/': {
|
||||
lang: 'es-ES',
|
||||
title: 'Atlantis',
|
||||
description: 'Atlantis: Automatización de Pull Requests para Terraform',
|
||||
},
|
||||
*/
|
||||
},
|
||||
plugins: [
|
||||
googleAnalyticsPlugin({
|
||||
id: 'UA-6850151-3',
|
||||
}),
|
||||
docsearchPlugin({
|
||||
// We internally discussed how this API key is exposed in the code and decided
|
||||
// that it is a non-issue because this API key can easily be extracted by
|
||||
// looking at the browser dev tools since the key is used in the API requests.
|
||||
apiKey: '3b733dff1539ca3a210775860301fa86',
|
||||
indexName: 'runatlantis',
|
||||
appId: 'BH4D9OD16A',
|
||||
locales: {
|
||||
'/': {
|
||||
placeholder: 'Search Documentation',
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Search Documentation',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
head: [
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-196x196.png', sizes: '196x196' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-96x96.png', sizes: '96x96' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-32x32.png', sizes: '32x32' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-16x16.png', sizes: '16x16' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon-128.png', sizes: '128x128' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '57x57', href: '/apple-touch-icon-57x57.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '114x114', href: '/apple-touch-icon-114x114.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '72x72', href: '/apple-touch-icon-72x72.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '144x144', href: '/apple-touch-icon-144x144.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '60x60', href: '/apple-touch-icon-60x60.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '120x120', href: '/apple-touch-icon-120x120.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '76x76', href: '/apple-touch-icon-76x76.png' }],
|
||||
['link', { rel: 'apple-touch-icon-precomposed', sizes: '152x152', href: '/apple-touch-icon-152x152.png' }],
|
||||
['meta', { name: 'msapplication-TileColor', content: '#FFFFFF' }],
|
||||
['meta', { name: 'msapplication-TileImage', content: '/mstile-144x144.png' }],
|
||||
['meta', { name: 'msapplication-square70x70logo', content: '/mstile-70x70.png' }],
|
||||
['meta', { name: 'msapplication-square150x150logo', content: '/mstile-150x150.png' }],
|
||||
['meta', { name: 'msapplication-wide310x150logo', content: '/mstile-310x150.png' }],
|
||||
['meta', { name: 'msapplication-square310x310logo', content: '/mstile-310x310.png' }],
|
||||
['link', { rel: 'stylesheet', sizes: '152x152', href: 'https://fonts.googleapis.com/css?family=Lato:400,900' }],
|
||||
['meta', { name: 'google-site-verification', content: 'kTnsDBpHqtTNY8oscYxrQeeiNml2d2z-03Ct9wqeCeE' }],
|
||||
],
|
||||
themePlugins: {
|
||||
activeHeaderLinks: false,
|
||||
},
|
||||
theme: defaultTheme({
|
||||
docsBranch: "main",
|
||||
logo: '/hero.png',
|
||||
locales: {
|
||||
'/': {
|
||||
selectLanguageName: 'English',
|
||||
navbar: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Guide', link: '/guide/' },
|
||||
{ text: 'Docs', link: '/docs/' },
|
||||
{ text: 'Blog', link: 'https://medium.com/runatlantis' },
|
||||
],
|
||||
},
|
||||
/*
|
||||
'/es/': {
|
||||
selectLanguageName: 'Spanish',
|
||||
navbar: [
|
||||
{ text: 'Home', link: '/es/' },
|
||||
{ text: 'Guide', link: '/es/guide/' },
|
||||
{ text: 'Docs', link: '/es/docs/' },
|
||||
{ text: 'Blog', link: 'https://medium.com/runatlantis' },
|
||||
],
|
||||
},
|
||||
*/
|
||||
},
|
||||
sidebar: {
|
||||
'/guide/': [
|
||||
'',
|
||||
'test-drive',
|
||||
'testing-locally',
|
||||
],
|
||||
'/docs/': [
|
||||
{
|
||||
text: 'Installing Atlantis',
|
||||
collapsible: true,
|
||||
children: [
|
||||
'installation-guide',
|
||||
'requirements',
|
||||
'access-credentials',
|
||||
'webhook-secrets',
|
||||
'deployment',
|
||||
'configuring-webhooks',
|
||||
'provider-credentials',
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Configuring Atlantis',
|
||||
collapsible: true,
|
||||
children: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: 'configuring-atlantis',
|
||||
},
|
||||
'server-configuration',
|
||||
'server-side-repo-config',
|
||||
'pre-workflow-hooks',
|
||||
'post-workflow-hooks',
|
||||
'policy-checking',
|
||||
'custom-workflows',
|
||||
'repo-level-atlantis-yaml',
|
||||
'upgrading-atlantis-yaml',
|
||||
'command-requirements',
|
||||
'checkout-strategy',
|
||||
'terraform-versions',
|
||||
'terraform-cloud',
|
||||
'using-slack-hooks',
|
||||
'stats',
|
||||
'faq',
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Using Atlantis',
|
||||
collapsible: true,
|
||||
children: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: 'using-atlantis',
|
||||
},
|
||||
'api-endpoints',
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'How Atlantis Works',
|
||||
collapsible: true,
|
||||
children: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: 'how-atlantis-works',
|
||||
},
|
||||
'locking',
|
||||
'autoplanning',
|
||||
'automerging',
|
||||
'security',
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Real-time Terraform Logs',
|
||||
collapsible: true,
|
||||
children: [
|
||||
'streaming-logs',
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Troubleshooting',
|
||||
collapsible: true,
|
||||
children: [
|
||||
'troubleshooting-https',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
repo: 'runatlantis/atlantis',
|
||||
docsDir: 'runatlantis.io',
|
||||
editLink: true,
|
||||
})
|
||||
})
|
||||
@@ -1,2 +0,0 @@
|
||||
/guide/getting-started.html /guide/
|
||||
/docs/atlantis-yaml-reference.html /docs/repo-level-atlantis-yaml.html
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#3BCC9C" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M13.3,44c-0.7,0-1.3-0.6-1.3-1.2V17.2c0-0.7,0.6-1.2,1.3-1.2h37.4c0.7,0,1.3,0.6,1.3,1.2v25.5
|
||||
c0,0.7-0.6,1.2-1.3,1.2H13.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#D3D5DD" d="M45,20c0.5,0,1,0.5,1,1s-0.5,1-1,1H18c-0.5,0-1-0.5-1-1s0.5-1,1-1H45z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#D3D5DD" d="M45,25c0.5,0,1,0.5,1,1s-0.5,1-1,1H18c-0.5,0-1-0.5-1-1s0.5-1,1-1H45z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<polygon fill="#E84B4B" points="44,43 44,44 44,53 40.1,50.8 36,53 36,44 36,43 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#316CFF" d="M26,35c0.5,0,1,0.4,1,1c0,0.5-0.5,1-1,1h-8c-0.5,0-1-0.5-1-1c0-0.6,0.5-1,1-1H26z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFC533" d="M44,43.2c-0.1,0.1-0.1,0.1-0.2,0.2l-0.5,0.8c-0.4,0.6-1.2,0.9-1.9,0.6l-0.8-0.3c-0.4-0.1-0.8-0.1-1.1,0
|
||||
l-0.8,0.3c-0.7,0.3-1.5,0-1.9-0.6l-0.5-0.8c0-0.1-0.1-0.1-0.2-0.2c-0.2-0.2-0.4-0.4-0.7-0.4l-0.9-0.3c-0.7-0.2-1.2-0.9-1.1-1.6
|
||||
l0-1.1c0-0.4-0.1-0.7-0.3-1L32.3,38c-0.4-0.6-0.4-1.3,0-1.9l0.6-0.9c0.2-0.3,0.3-0.6,0.3-1l0-1.1c0-0.7,0.4-1.4,1.1-1.6l0.9-0.3
|
||||
c0.4-0.1,0.7-0.3,0.9-0.7l0.5-0.8c0.4-0.6,1.2-0.9,1.9-0.6l0.8,0.3c0.4,0.1,0.8,0.1,1.1,0l0.8-0.3c0.7-0.3,1.5,0,1.9,0.6l0.5,0.8
|
||||
c0.2,0.3,0.5,0.5,0.9,0.7l0.9,0.3c0.7,0.2,1.2,0.9,1.1,1.6l0,1.1c0,0.4,0.1,0.7,0.3,1l0.6,0.9c0.4,0.6,0.4,1.3,0,1.9L47,38.8
|
||||
c-0.2,0.3-0.3,0.6-0.3,1l0,1.1c0,0.7-0.4,1.4-1.1,1.6l-0.9,0.3C44.4,42.9,44.2,43,44,43.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#F2AC0F" d="M40,32c2.8,0,5,2.2,5,5s-2.2,5-5,5s-5-2.2-5-5S37.2,32,40,32z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FCEA81" d="M40,34c1.7,0,3,1.3,3,3s-1.3,3-3,3s-3-1.3-3-3S38.3,34,40,34z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Build-Stage-Passed" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#39AA56;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g id="stage-passed-icon">
|
||||
<circle class="st0" cx="7.5" cy="7.5" r="7"/>
|
||||
<polyline class="st0" points="10.63,4.711 7.064,10.213 4.294,8.012 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 645 B |
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<circle style="fill:#F33052;" cx="256" cy="256" r="256"/>
|
||||
<g>
|
||||
<path style="fill:#FFFFFF;" d="M213.12,319.776L99.872,270.544V243.28l113.248-49.008v32.112l-79.008,30.208l79.008,31.328V319.776
|
||||
z"/>
|
||||
<path style="fill:#FFFFFF;" d="M223.6,341.408l40.912-170.832h23.776l-41.36,170.832H223.6z"/>
|
||||
<path style="fill:#FFFFFF;" d="M298.768,319.904V288l79.104-31.104l-79.104-30.752V194.48l113.36,49.008v27.04L298.768,319.904z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 964 B |
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<polygon style="fill:#EDEADA;" points="51.5,14 37.5,0 6.5,0 6.5,58 51.5,58 "/>
|
||||
<polygon style="fill:#CEC9AE;" points="37.5,0 37.5,14 51.5,14 "/>
|
||||
<path style="fill:#CEC9AE;" d="M41.5,22h-16c-0.552,0-1-0.447-1-1s0.448-1,1-1h16c0.552,0,1,0.447,1,1S42.052,22,41.5,22z"/>
|
||||
<path style="fill:#14A085;" d="M17.5,23c-0.257,0-0.514-0.099-0.708-0.293l-2-2c-0.391-0.391-0.391-1.023,0-1.414
|
||||
s1.023-0.391,1.414,0l1.367,1.367l4.301-3.441c0.43-0.345,1.061-0.275,1.405,0.156c0.345,0.432,0.275,1.061-0.156,1.406l-5,4
|
||||
C17.941,22.928,17.72,23,17.5,23z"/>
|
||||
<path style="fill:#CEC9AE;" d="M41.5,33h-16c-0.552,0-1-0.447-1-1s0.448-1,1-1h16c0.552,0,1,0.447,1,1S42.052,33,41.5,33z"/>
|
||||
<path style="fill:#14A085;" d="M17.5,34c-0.257,0-0.514-0.099-0.708-0.293l-2-2c-0.391-0.391-0.391-1.023,0-1.414
|
||||
s1.023-0.391,1.414,0l1.367,1.367l4.301-3.441c0.43-0.345,1.061-0.275,1.405,0.156c0.345,0.432,0.275,1.061-0.156,1.406l-5,4
|
||||
C17.941,33.928,17.72,34,17.5,34z"/>
|
||||
<path style="fill:#CEC9AE;" d="M41.5,44h-16c-0.552,0-1-0.447-1-1s0.448-1,1-1h16c0.552,0,1,0.447,1,1S42.052,44,41.5,44z"/>
|
||||
<path style="fill:#14A085;" d="M17.5,45c-0.257,0-0.514-0.099-0.708-0.293l-2-2c-0.391-0.391-0.391-1.023,0-1.414
|
||||
s1.023-0.391,1.414,0l1.367,1.367l4.301-3.441c0.43-0.345,1.061-0.275,1.405,0.156c0.345,0.432,0.275,1.061-0.156,1.406l-5,4
|
||||
C17.941,44.928,17.72,45,17.5,45z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 187 KiB |
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#5E5C89" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<polygon fill="#FFD833" points="44,27 37,27 41,15 27,15 25,32 29.6,32 24,49 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 264 KiB |
@@ -1,243 +0,0 @@
|
||||
// https://v2.vuepress.vuejs.org/reference/default-theme/styles.html#style-file
|
||||
|
||||
// colors
|
||||
$textColor: var(--c-text);
|
||||
$borderColor: var(--c-border);
|
||||
$buttonTextColor: var(--c-badge-danger-text);
|
||||
$buttonColor: var(--c-brand);
|
||||
$buttonHoverColor: var(--c-brand-light);
|
||||
$darkBackground: var(--c-bg-light);
|
||||
$darkBackgroundBorder: var(--c-border-dark);
|
||||
|
||||
// layout
|
||||
$navbarHeight: 3.6rem;
|
||||
$sidebarWidth: 20rem;
|
||||
$contentWidth: 740px;
|
||||
|
||||
// responsive breakpoints
|
||||
$MQNarrow: 959px;
|
||||
$MQMobile: 719px;
|
||||
$MQMobileNarrow: 419px;
|
||||
|
||||
$homeWidth: 960px;
|
||||
|
||||
.container {
|
||||
padding-top: 3.6rem;
|
||||
}
|
||||
|
||||
.home {
|
||||
padding: 0 2rem;
|
||||
max-width: $homeWidth;
|
||||
margin: 0px auto 80px;
|
||||
.hero {
|
||||
text-align: center;
|
||||
img {
|
||||
max-height: 280px;
|
||||
display: block;
|
||||
margin: 3rem auto 1.5rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
h1, .description, .action {
|
||||
margin: 1.8rem auto;
|
||||
}
|
||||
.description {
|
||||
max-width: 35rem;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 1.9rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.action {
|
||||
display: inline;
|
||||
}
|
||||
.action-button {
|
||||
display: inline-block;
|
||||
font-size: 1.2rem;
|
||||
color: $buttonTextColor;
|
||||
cursor: pointer;
|
||||
background-color: $buttonColor;
|
||||
padding: 0.8rem 1.6rem;
|
||||
border-radius: 4px;
|
||||
transition: background-color .1s ease;
|
||||
box-sizing: border-box;
|
||||
margin: 0 10px;
|
||||
&:hover {
|
||||
background-color: $buttonHoverColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
border-bottom: none;
|
||||
}
|
||||
.features {
|
||||
border-top: 1px solid $borderColor;
|
||||
padding: 1.2rem 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
.footer {
|
||||
padding: 2.5rem;
|
||||
border-top: 1px solid $borderColor;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.getting-started-footer {
|
||||
padding: 2.5rem 0;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.workflow-container {
|
||||
border-top: 2px solid $borderColor;
|
||||
}
|
||||
|
||||
.workflow {
|
||||
text-align: center;
|
||||
margin: 80px auto;
|
||||
max-width: $homeWidth;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
.mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.benefits-container {
|
||||
border-top: 1px solid $darkBackgroundBorder;
|
||||
.benefit-container {
|
||||
border-bottom: 1px solid $darkBackgroundBorder;
|
||||
.title {
|
||||
padding-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
&.-dark {
|
||||
background-color: $darkBackground;
|
||||
}
|
||||
.benefit {
|
||||
max-width: $homeWidth;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
.item {
|
||||
flex-basis: 50%;
|
||||
flex-grow: 1;
|
||||
min-width: 250px;
|
||||
.image {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
img {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.description {
|
||||
padding: 40px;
|
||||
h2 {
|
||||
border: none;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 25px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.checkmark {
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
vertical-align: middle;
|
||||
align-self: baseline;
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobile) {
|
||||
.workflow {
|
||||
.mobile {
|
||||
display: block;
|
||||
}
|
||||
.desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.benefits-container {
|
||||
.benefit-container {
|
||||
.benefit {
|
||||
flex-direction: column;
|
||||
.item {
|
||||
&.image {
|
||||
order: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $MQMobileNarrow) {
|
||||
.home {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
.hero {
|
||||
img {
|
||||
max-height: 210px;
|
||||
margin: 2rem auto 1.2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h1, .description, .action {
|
||||
margin: 1.2rem auto;
|
||||
}
|
||||
.description {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.action-button {
|
||||
font-size: 1rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-container {
|
||||
&.home-custom {
|
||||
.hero {
|
||||
h1 {
|
||||
font-size: 64px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-weight: 900;
|
||||
}
|
||||
img {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
p {
|
||||
&.description {
|
||||
position: relative;
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 40px;
|
||||
height: 3px;
|
||||
top: -19px;
|
||||
left: 50%;
|
||||
margin-left: -20px;
|
||||
background: #f36;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sidebar-heading {
|
||||
font-size: inherit;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
$accentColor: #0074db;
|
||||
$textColor: #2c3e50;
|
||||
$borderColor: #eaecef;
|
||||
$codeBgColor: #282c34;
|
||||
@@ -1,175 +0,0 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="home">
|
||||
<div class="hero">
|
||||
<img v-if="data.heroImage" :src="$withBase(data.heroImage)" alt="hero">
|
||||
<h1>{{ data.heroText || $title || 'Hello' }}</h1>
|
||||
<p class="description">
|
||||
Terraform Pull Request Automation
|
||||
</p>
|
||||
<p class="action" v-if="data.actionText && data.actionLink">
|
||||
<a href="/guide/" class="nav-link action-button">Get Started →</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workflow-container">
|
||||
<div class="workflow">
|
||||
<h1>The Atlantis Workflow</h1>
|
||||
<img src="/mobile-workflow-min.png" class="mobile" alt="Atlantis Workflow">
|
||||
<img src="/workflow-min.png" class="desktop" alt="Atlantis Workflow">
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefits-container">
|
||||
<div class="benefit-container -dark">
|
||||
<div class="title">
|
||||
<h1>Benefits</h1>
|
||||
</div>
|
||||
<div class="benefit">
|
||||
<div class="item">
|
||||
<div class="description">
|
||||
<h2>Fewer Mistakes</h2>
|
||||
<p>Bring the benefits of <strong>code review</strong> to your operations
|
||||
workflow.</p>
|
||||
<ul>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Catch errors in
|
||||
the Terraform plan output before it's applied.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Ensure that you
|
||||
apply changes before merging to main.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item image">
|
||||
<div class="image">
|
||||
<img src="/list.svg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefit-container">
|
||||
<div class="benefit">
|
||||
<div class="item image">
|
||||
<div class="image">
|
||||
<img src="/coding.svg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="description">
|
||||
<h2>Put the <span style="text-decoration: underline">Dev</span> back into DevOps</h2>
|
||||
<p>Empower your developers to write Terraform. <strong>Safely.</strong></p>
|
||||
<ul>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Developers can
|
||||
submit Terraform pull requests without needing credentials.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Operators can
|
||||
require approvals prior to allowing an apply.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefit-container -dark">
|
||||
<div class="benefit">
|
||||
<div class="item">
|
||||
<div class="description">
|
||||
<h2>Instant Audit Logs And Compliance</h2>
|
||||
<p>Pass audits without compromising your workflow.</p>
|
||||
<ul>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Each pull request now holds a detailed log of what infrastructure changes were made and when; along with who made the change and who approved it.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Atlantis can be configured to require approvals on every production change.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item image">
|
||||
<div class="image">
|
||||
<img src="/certificate.svg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefits-container">
|
||||
<div class="benefit-container">
|
||||
<h1 class="title">Proven at Scale</h1>
|
||||
<div class="benefit">
|
||||
<div class="item image">
|
||||
<div class="image">
|
||||
<img src="/powerful.svg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="description">
|
||||
<ul>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Used by one of the world's top companies to manage over 600 Terraform repos with 300 developers.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">In production use since 2017.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefits-container">
|
||||
<div class="benefit-container">
|
||||
<h1 class="title">How It Works</h1>
|
||||
<div class="benefit">
|
||||
<div class="item">
|
||||
<div class="description">
|
||||
<ul>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Atlantis is
|
||||
self-hosted. Your credentials don't leave your infrastructure.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Runs as a Golang
|
||||
binary or Docker image and can be deployed on VMs, Kubernetes,
|
||||
Fargate, etc.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Listens for
|
||||
webhooks from GitHub/GitLab/Bitbucket/Azure DevOps.
|
||||
</li>
|
||||
<li><img class="checkmark" src="/checkmark.svg">Runs terraform
|
||||
commands remotely and comments back with their output.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item image">
|
||||
<div class="image">
|
||||
<img src="/hero.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home getting-started-footer">
|
||||
<div class="hero">
|
||||
<p class="action" v-if="data.actionText && data.actionLink">
|
||||
<a href="/guide/" class="nav-link action-button">Get Started →</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer" v-if="data.footer">
|
||||
{{ data.footer }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
data() {
|
||||
return this.$page.frontmatter
|
||||
},
|
||||
actionLink() {
|
||||
return {
|
||||
link: this.data.actionLink,
|
||||
text: this.data.actionText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
// introduce custom home with navbar
|
||||
// https://stackoverflow.com/a/60220684
|
||||
// https://vuepress.vuejs.org/theme/inheritance.html#usage
|
||||
module.exports = {
|
||||
extend: '@vuepress/theme-default'
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
home: true
|
||||
pageClass: home-custom
|
||||
heroImage: /hero.png
|
||||
heroText: Atlantis
|
||||
actionText: Get Started →
|
||||
actionLink: /guide/
|
||||
title: Terraform Pull Request Automation
|
||||
---
|
||||
28
runatlantis.io/blog.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Welcome to Our Blog
|
||||
aside: false
|
||||
---
|
||||
|
||||
# Welcome to Our Blog
|
||||
|
||||
We are thrilled to have you here! Our blog is a collection of insightful articles, tips, and updates from our team. Whether you're new or have been following us for a while, there's always something new to learn and explore.
|
||||
|
||||
### Explore Our Popular Posts
|
||||
|
||||
We have a rich history of blog posts dating back to 2017-2019. Here are some of our popular posts:
|
||||
|
||||
- [4 Reasons To Try HashiCorp's (New) Free Terraform Remote State Storage](/blog/2019/4-reasons-to-try-hashicorps-new-free-terraform-remote-state-storage)
|
||||
- [I'm Joining HashiCorp!](/blog/2018/joining-hashicorp)
|
||||
- [Putting The Dev Into DevOps: Why Your Developers Should Write Terraform Too](/blog/2018/putting-the-dev-into-devops-why-your-developers-should-write-terraform-too)
|
||||
- [Atlantis 0.4.4 Now Supports Bitbucket](/blog/2018/atlantis-0-4-4-now-supports-bitbucket)
|
||||
- [Terraform And The Dangers Of Applying Locally](/blog/2018/terraform-and-the-dangers-of-applying-locally)
|
||||
- [Hosting Our Static Site over SSL with S3, ACM, CloudFront and Terraform](/blog/2018/hosting-our-static-site-over-ssl-with-s3-acm-cloudfront-and-terraform)
|
||||
- [Introducing Atlantis](/blog/2017/introducing-atlantis)
|
||||
|
||||
### Welcoming New Blog Authors
|
||||
|
||||
We are excited to welcome new authors to our blog. Our diverse team brings a wealth of knowledge and experience to share with our readers. Stay tuned for fresh perspectives and in-depth articles on the latest trends and technologies.
|
||||
|
||||
If you have any questions or topics you would like us to cover, feel free to reach out [on Slack](https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw). We are always looking to engage with our community and provide valuable content.
|
||||
|
||||
Happy reading!
|
||||
113
runatlantis.io/blog/2017/introducing-atlantis.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Introducing Atlantis
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Introducing Atlantis
|
||||
|
||||
::: info
|
||||
This post was originally written on September 11th, 2017
|
||||
|
||||
Original post: <https://medium.com/runatlantis/introducing-atlantis-6570d6de7281>
|
||||
:::
|
||||
|
||||
We're very excited to announce the open source release of Atlantis! Atlantis is a tool for
|
||||
collaborating on Terraform that's been in use at Hootsuite for over a year. The core
|
||||
functionality of Atlantis enables developers and operators to run `terraform plan` and
|
||||
`apply` directly from Terraform pull requests. Atlantis then comments back on the pull
|
||||
request with the output of the commands:
|
||||
|
||||

|
||||
|
||||
This is a simple feature, however it has had a massive effect on how our team writes Terraform.
|
||||
By bringing a Terraform workflow to pull requests, Atlantis helped our Ops team collaborate
|
||||
better on Terraform and also enabled our entire development team to write and execute Terraform safely.
|
||||
|
||||
Atlantis was built to solve two problems that arose at Hootsuite as we adopted Terraform:
|
||||
|
||||
### 1. Effective Collaboration
|
||||
|
||||
What's the best way to collaborate on Terraform in a team setting?
|
||||
|
||||
### 2. Developers Writing Terraform
|
||||
|
||||
How can we enable our developers to write and apply Terraform safely?
|
||||
|
||||
## Effective Collaboration
|
||||
|
||||
When writing Terraform, there are a number of workflows you can follow. The simplest workflow is just using `master`:
|
||||
|
||||

|
||||
|
||||
In this workflow, you work on `master` and run `terraform` locally.
|
||||
The problem with this workflow is that there is no collaboration or code review.
|
||||
So we start to use pull requests:
|
||||
|
||||

|
||||
|
||||
We still run `terraform plan` locally, but once we're satisfied with the changes we create a pull request for review. When the pull request is approved, we run `apply` locally.
|
||||
|
||||
This workflow is an improvement, but there are still problems. The first problem is that it's hard to review just the diff on the pull request. To properly review a change, you really need to see the output from `terraform plan`.
|
||||
|
||||

|
||||
|
||||
What looks like a small change...
|
||||
|
||||

|
||||
|
||||
...can have a big plan
|
||||
|
||||
The second problem is that now it's easy for `master` to get out of sync with what's actually been applied. This can happen if you merge a pull request without running `apply` or if the `apply` has an error halfway through, you forget to fix it and then you merge to `master`. Now what's in `master` isn't actually what's running on production. At best, this causes confusion the next time someone runs `terraform plan`. At worst, it causes an outage when someone assumes that what's in `master` is actually running, and depends on it.
|
||||
|
||||
With the Atlantis workflow, these problems are solved:
|
||||
|
||||

|
||||
|
||||
Now it's easy to review changes because you see the `terraform plan` output on the pull request.
|
||||
|
||||

|
||||
|
||||
Pull requests are easy to review since you can see the plan
|
||||
|
||||
It's also easy to ensure that the pull request is `terraform apply`'d before merging to master because you can see the actual `apply` output on the pull request.
|
||||
|
||||

|
||||
|
||||
So, Atlantis makes working on Terraform within an operations team much easier, but how does it help with getting your whole team to write Terraform?
|
||||
|
||||
## Developers Writing Terraform
|
||||
|
||||
Terraform usually starts out being used by the Ops team. As a result of using Terraform, the Ops team becomes much faster at making infrastructure changes, but the way developers request those changes remains the same: they use a ticketing system or chat to ask operations for help, the request goes into a queue and later Ops responds that the task is complete.
|
||||
|
||||
Soon however, the Ops team starts to realize that it's possible for developers to make some of these Terraform changes themselves! There are some problems that arise though:
|
||||
|
||||
- Developers don't have the credentials to actually run Terraform commands
|
||||
- If you give them credentials, it's hard to review what is actually being applied
|
||||
|
||||
With Atlantis, these problems are solved. All `terraform plan` and `apply` commands are run from the pull request. This means developers don't need to have any credentials to run Terraform locally. Of course, this can be dangerous: how can you ensure developers (who might be new to Terraform) aren't applying things they shouldn't? The answer is code reviews and approvals.
|
||||
|
||||
Since Atlantis comments back with the `plan` output directly on the pull request, it's easy for an operations engineer to review exactly what changes will be applied. And Atlantis can run in `require-approval` mode, that will require a GitHub pull request approval before allowing `apply` to be run:
|
||||
|
||||

|
||||
|
||||
With Atlantis, developers are able to write and apply Terraform safely. They submit pull requests, can run `atlantis plan` until their change looks good and then get approval from Ops to `apply`.
|
||||
|
||||
Since the introduction of Atlantis at Hootsuite, we've had **78** contributors to our Terraform repositories, **58** of whom are developers (**75%**).
|
||||
|
||||
## Where we are now
|
||||
|
||||
Since the introduction of Atlantis at Hootsuite we've grown to 144 Terraform repositories [^1] that manage thousands of Amazon resources. Atlantis is used for every single Terraform change throughout our organization.
|
||||
|
||||
## Getting started with Atlantis
|
||||
|
||||
If you'd like to try out Atlantis for your team you can download the latest release from <https://github.com/runatlantis/atlantis/releases>. If you run `atlantis testdrive` you can get started in less than 5 minutes. To read more about Atlantis go to <https://www.runatlantis.io/>.
|
||||
|
||||
Check out our video for more information:
|
||||
|
||||
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FTmIPWda0IKg%3Ffeature%3Doembed&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DTmIPWda0IKg&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FTmIPWda0IKg%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube" allowfullscreen="" frameborder="0" height="480" width="640" title="Atlantis Walkthrough" class="fr n gh dv bg" scrolling="no"></iframe>
|
||||
|
||||
[^1]: We split our Terraform up into multiple states, each with its own repository (see [1], [2], [3]).
|
||||
|
||||
[1]: https://blog.gruntwork.io/how-to-manage-terraform-state-28f5697e68fa
|
||||
[2]: https://charity.wtf/2016/03/30/terraform-vpc-and-why-you-want-a-tfstate-file-per-env/
|
||||
[3]: https://www.nclouds.com/blog/terraform-multi-state-management/
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Atlantis 0.4.4 Now Supports Bitbucket
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Atlantis 0.4.4 Now Supports Bitbucket
|
||||
|
||||
::: info
|
||||
This post was originally written on July 25th, 2018
|
||||
|
||||
Original post: <https://medium.com/runatlantis/atlantis-0-4-4-now-supports-bitbucket-86c53a550b45>
|
||||
:::
|
||||
|
||||

|
||||
|
||||
Atlantis is an [open source](https://github.com/runatlantis/atlantis) platform for using Terraform in teams. I'm happy to announce that the [latest release](https://github.com/runatlantis/atlantis/releases) of Atlantis (0.4.4) now supports both Bitbucket Cloud (bitbucket.org) **and** Bitbucket Server (aka Stash).
|
||||
|
||||

|
||||
|
||||
Atlantis now supports the three major Git hosts: GitHub, GitLab and Bitbucket. The rest of this post will talk about how to use Atlantis with Bitbucket.
|
||||
|
||||
## What is Atlantis?
|
||||
|
||||
Atlantis is a self-hosted application that listens for Terraform pull request events via webhooks. It runs `terraform plan` and `apply` remotely and comments back on the pull request with the output.
|
||||
|
||||
With Atlantis, you collaborate on the Terraform pull request itself instead of running `terraform apply` from your own computers which can be dangerous:
|
||||
|
||||
Check out <www.runatlantis.io> for more information.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The easiest way to try out Atlantis with Bitbucket is to run Atlantis locally on your own computer. Eventually you'll want to deploy it as a standalone app but this is the easiest way to try it out. Follow [these instructions](https://www.runatlantis.io/guide/getting-started.html) to get Atlantis running locally.
|
||||
|
||||
Create a Pull Request
|
||||
If you've got the Atlantis webhook configured for your repository and Atlantis is running, it's time to create a new pull request. I recommend adding a `null_resource` to one of your Terraform files for the the test pull request. It won't actually create anything so it's safe to use as a test.
|
||||
|
||||
Using the web editor, open up one of your Terraform files and add:
|
||||
|
||||
```tf
|
||||
resource "null_resource" "example" {}
|
||||
```
|
||||
|
||||

|
||||
|
||||
Click Commit and select **Create a pull request for this change**.
|
||||
|
||||

|
||||
|
||||
Wait a few seconds and then refresh. Atlantis should have automatically run `terraform plan` and commented back on the pull request:
|
||||
|
||||

|
||||
|
||||
Now it's easier for your colleagues to review the pull request because they can see the `terraform plan` output.
|
||||
|
||||
### Terraform Apply
|
||||
|
||||
Since all we're doing is adding a null resource, I think it's safe to run `terraform apply`. To do so, I add a comment to the pull request: `atlantis apply`:
|
||||
|
||||

|
||||
|
||||
Atlantis is listening for pull request comments and will run `terraform apply` remotely and comment back with the output:
|
||||
|
||||

|
||||
|
||||
### Pull Request Approvals
|
||||
|
||||
If you don't want anyone to be able to `terraform apply`, you can run Atlantis with `--require-approval` or add that setting to your [atlantis.yaml file](https://www.runatlantis.io/docs/command-requirements.html#approved).
|
||||
|
||||
This will ensure that the pull request has been approved before someone can run `apply`.
|
||||
|
||||
## Other Features
|
||||
|
||||
### Customizable Commands
|
||||
|
||||
Apart from being able to `plan` and `apply` from the pull request, Atlantis also enables you to customize the exact commands that are run via an `atlantis.yaml` config file. For example to use the `-var-file` flag:
|
||||
|
||||
```yaml{14}
|
||||
# atlantis.yaml
|
||||
version: 2
|
||||
projects:
|
||||
- name: staging
|
||||
dir: "."
|
||||
workflow: staging
|
||||
|
||||
workflows:
|
||||
staging:
|
||||
plan:
|
||||
steps:
|
||||
- init
|
||||
- plan:
|
||||
extra_args: ["-var-file", "staging.tfvars"]
|
||||
```
|
||||
|
||||
### Locking For Coordination
|
||||
|
||||

|
||||
|
||||
Atlantis will prevent other pull requests from running against the same directory as an open pull request so that each plan is applied atomically. Once the first pull request is merged, other pull requests are unlocked.
|
||||
|
||||
## Next Steps
|
||||
|
||||
If you're interested in using Atlantis with Bitbucket, check out our Getting Started docs. Happy Terraforming!
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Hosting Our Static Site over SSL with S3, ACM, CloudFront and Terraform
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Hosting Our Static Site over SSL with S3, ACM, CloudFront and Terraform
|
||||
|
||||
::: info
|
||||
This post was originally written on March 4, 2018
|
||||
|
||||
Original post: <https://medium.com/runatlantis/hosting-our-static-site-over-ssl-with-s3-acm-cloudfront-and-terraform-513b799aec0f>
|
||||
:::
|
||||
|
||||
In this post I cover how I hosted <www.runatlantis.io> using
|
||||
|
||||
- S3 — for storing the static site
|
||||
- CloudFront — for serving the static site over SSL
|
||||
- AWS Certificate Manager — for generating the SSL certificates
|
||||
- Route53 — for routing the domain name <www.runatlantis.io> to the correct location
|
||||
|
||||
I chose Terraform in this case because Atlantis is a tool for automating and collaborating on Terraform in a team (see github.com/runatlantis/atlantis)–and so obviously it made sense to host our homepage using Terraform–but also because it's now much easier to manage. I don't have to go into the AWS console and click around to find what settings I want to change. Instead I can just look at ~100 lines of code, make a change, and run `terraform apply`.
|
||||
|
||||
::: info
|
||||
NOTE: 4 months after this writing, I moved the site to [Netlify](https://www.netlify.com/) because it automatically builds from my master branch on any change, updates faster since I don't need to wait for the Cloudfront cache to expire and gives me [deploy previews](https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/) of changes. The DNS records are still hosted on AWS.
|
||||
:::
|
||||
|
||||
# Overview
|
||||
|
||||
There's a surprising number of components required to get all this working so I'm going to start with an overview of what they're all needed for. Here's what the final architecture looks like:
|
||||
|
||||

|
||||
|
||||
That's what the final product looks like, but lets start with the steps required to get there.
|
||||
|
||||
## Step 1 — Generate The Site
|
||||
|
||||
The first step is to have a site generated. Our site uses [Hugo](https://gohugo.io/), a Golang site generator. Once it's set up, you just need to run `hugo` and it will generate a directory with HTML and all your content ready to host.
|
||||
|
||||
## Step 2 — Host The Content
|
||||
|
||||
Once you've got a website, you need it to be accessible on the internet. I used S3 for this because it's dirt cheap and it integrates well with all the other necessary components. I simply upload my website folder to the S3 bucket.
|
||||
|
||||
## Step 3 — Generate an SSL Certificate
|
||||
|
||||
I needed to generate an SSL certificate for <https://www.runatlantis.io>. I used the AWS Certificate Manager for this because it's free and is easily integrated with the rest of the system.
|
||||
|
||||
## Step 4 — Set up DNS
|
||||
|
||||
Because I'm going to host the site on AWS services, I need requests to <www.runatlantis.io> to be routed to those services. Route53 is the obvious solution.
|
||||
|
||||
## Step 5 — Host with CloudFront
|
||||
|
||||
At this point, we've generated an SSL certificate for <www.runatlantis.io> and our website is available on the internet via its S3 url so can't we just CNAME to the S3 bucket and call it a day? Unfortunately not.
|
||||
|
||||
Since we generated our own certificate, we would need S3 to sign its responses using our certificiate. S3 doesn't support this and thus we need CloudFront. CloudFront supports using our own SSL cert and will just pull its data from the S3 bucket.
|
||||
|
||||
# Terraform Time
|
||||
|
||||
Now that we know what our architecture should look like, it's simply a matter of writing the Terraform.
|
||||
|
||||
## Initial Setup
|
||||
|
||||
Create a new file `main.tf`:
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/main.tf
|
||||
|
||||
## S3 Bucket
|
||||
|
||||
Assuming we've generated our site content already, we need to create an S3 bucket to host the content.
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/s3-bucket.tf
|
||||
|
||||
We should be able to run Terraform now to create the S3 bucket
|
||||
|
||||
```sh
|
||||
terraform init
|
||||
`terraform apply`
|
||||
```
|
||||
|
||||

|
||||
|
||||
Now we want to upload our content to the S3 bucket:
|
||||
|
||||
```sh
|
||||
$ cd dir/with/website
|
||||
# generate the HTML
|
||||
$ hugo -d generated
|
||||
$ cd generated
|
||||
# send it to our S3 bucket
|
||||
$ aws s3 sync . s3://www.runatlantis.io/ # change this to your bucket
|
||||
```
|
||||
|
||||
Now we need the S3 url to see our content:
|
||||
|
||||
```sh
|
||||
$ terraform state show aws_s3_bucket.www | grep website_endpoint
|
||||
website_endpoint = www.runatlantis.io.s3-website-us-east-1.amazonaws.com
|
||||
```
|
||||
|
||||
You should see your site hosted at that url!
|
||||
|
||||
## SSL Certificate
|
||||
|
||||
Let's use the AWS Certificate Manager to create our SSL certificate.
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/ssl-cert.tf
|
||||
|
||||
Before you run `terraform apply`, ensure you're forwarding any of
|
||||
|
||||
- `administrator@your_domain_name`
|
||||
- `hostmaster@your_domain_name`
|
||||
- `postmaster@your_domain_name`
|
||||
- `webmaster@your_domain_name`
|
||||
- `admin@your_domain_name`
|
||||
|
||||
To an email address you can access. Then, run `terraform apply` and you should get an email from AWS to confirm you own this domain where you'll need to click on the link.
|
||||
|
||||
## CloudFront
|
||||
|
||||
Now we're ready for CloudFront to host our website using the S3 bucket for the content and using our SSL certificate. Warning! There's a lot of code ahead but most of it is just defaults.
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/cloudfront.tf
|
||||
|
||||
Apply the changes with `terraform apply` and then find the domain name that CloudFront gives us:
|
||||
|
||||
```sh
|
||||
$ terraform state show aws_cloudfront_distribution.www_distribution | grep ^domain_name
|
||||
domain_name = d1l8j8yicxhafq.cloudfront.net
|
||||
```
|
||||
|
||||
You'll probably get an error if you go to that URL right away. You need to wait a couple minutes for CloudFront to set itself up. It took me 10 minutes. You can view its progress in the console: <https://console.aws.amazon.com/cloudfront/home>
|
||||
|
||||
## DNS
|
||||
|
||||
We're almost done! We've got CloudFront hosting our site, now we need to point our DNS at it.
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/dns.tf
|
||||
|
||||
If you bought your domain from somewhere else like Namecheap, you'll need to point your DNS at the nameservers listed in the state for the Route53 zone you created. First `terraform apply` (which may take a while), then find out your nameservers.
|
||||
|
||||
```sh
|
||||
$ terraform state show aws_route53_zone.zone
|
||||
id = Z2FNAJGFW912JG
|
||||
comment = Managed by Terraform
|
||||
force_destroy = false
|
||||
name = runatlantis.io
|
||||
name_servers.# = 4
|
||||
name_servers.0 = ns-1349.awsdns-40.org
|
||||
name_servers.1 = ns-1604.awsdns-08.co.uk
|
||||
name_servers.2 = ns-412.awsdns-51.com
|
||||
name_servers.3 = ns-938.awsdns-53.net
|
||||
tags.% = 0
|
||||
zone_id = Z2FNAJGFW912JG
|
||||
```
|
||||
|
||||
Then look at your domain's docs for how to change your nameservers to all 4 listed.
|
||||
|
||||
## That's it...?
|
||||
|
||||
Once the DNS propagates you should see your site at `https://www.yourdomain`! But what about `https://yourdomain`? i.e. without the `www.`? Shouldn't this redirect to `https://www.yourdomain`?
|
||||
|
||||
## Root Domain
|
||||
|
||||
It turns out, we need to create a whole new S3 bucket, CloudFront distribution and Route53 record just to get this to happen. That's because although S3 can serve up a redirect to the www version of your site, it can't host SSL certs and so you need CloudFront. I've included all the terraform necessary for that below.
|
||||
|
||||
Congrats! You're done!
|
||||
|
||||
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2Fl0MYt5jPR6QX5pnqM%2Ftwitter%2Fiframe&display_name=Giphy&url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2Fl0MYt5jPR6QX5pnqM%2Fgiphy.gif&image=https%3A%2F%2Fi.giphy.com%2Fmedia%2Fl0MYt5jPR6QX5pnqM%2Fgiphy.gif&key=d04bfffea46d4aeda930ec88cc64b87c&type=text%2Fhtml&schema=giphy" allowfullscreen="" frameborder="0" height="244" width="435" title="The Office Party Hard GIF - Find & Share on GIPHY" class="fr n gh dv bg" scrolling="no"></iframe>
|
||||
|
||||
If you're using Terraform in a team, check out Atlantis: <https://github.com/runatlantis/atlantis> for automation and collaboration to make your team happier!
|
||||
|
||||
Here's the Terraform needed to redirect your root domain:
|
||||
|
||||
<<< @/public/blog/hosting-our-static-site/code/full.tf
|
||||
50
runatlantis.io/blog/2018/joining-hashicorp.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: I'm Joining HashiCorp!
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# I'm Joining HashiCorp
|
||||
|
||||
::: info
|
||||
This post was originally written on October 23th, 2018
|
||||
|
||||
Original post: <https://medium.com/runatlantis/joining-hashicorp-200ee9572dc5>
|
||||
:::
|
||||
|
||||
Dear Atlantis Community,
|
||||
|
||||
My name is Luke and I'm the maintainer of [Atlantis](https://www.runatlantis.io/), an open source tool for Terraform collaboration. Today I'm excited to announce that I'm joining HashiCorp!
|
||||
|
||||

|
||||
|
||||
## What Does This Mean For Atlantis?
|
||||
|
||||
In the near term, nothing will change for Atlantis and its users. As a HashiCorp employee I will continue to maintain Atlantis, review pull requests, triage issues, and write code.
|
||||
|
||||
In the long term, HashiCorp and I want to address collaboration workflows for all users of Terraform. We are still working out the details of how Atlantis will fit into the longer term plan, but whatever direction we take, we're committed to keeping Atlantis free and open source.
|
||||
|
||||
## HashiCorp and Atlantis
|
||||
|
||||
Why does HashiCorp want to support Atlantis?
|
||||
|
||||
Today HashiCorp [announced their commitment to provide collaboration solutions to the whole Terraform community](https://www.hashicorp.com/blog/terraform-collaboration-for-everyone). They see the Atlantis project as one manifestation of this vision and understand its importance to many in the Terraform community. They believe that by working together, we can create a solution that will scale from a single user to hundreds of collaborators in a large organization.
|
||||
|
||||
## Why am I joining?
|
||||
|
||||
Those of you who know me, may wonder why I made this decision. It came down to wanting to continue working on Atlantis–and the larger story of Terraform collaboration–and finding a way to support myself.
|
||||
|
||||
In January, 9 months ago, I quit my job at Hootsuite to work **full time** on Atlantis (Atlantis was originally created at Hootsuite by my friend [Anubhav Mishra](https://twitter.com/anubhavm)). I left because I knew that the Terraform community was in need of a solution for collaboration and that with full time development, Atlantis could be that solution.
|
||||
|
||||
During the last 9 months, Atlantis matured into a fully fledged collaboration solution and gained many new users. It has been an amazing time, but I've been working for free! I've always known that for Atlantis to be successful in the long term, I would need to find a way to support myself.
|
||||
|
||||
A couple of weeks ago, as I was playing around with Atlantis monetization strategies, HashiCorp contacted me. I learned that they shared a vision of building Terraform collaboration solutions for the broader community and that they were interested in combining forces. They also assured me that they wanted to do right by the Atlantis community.
|
||||
|
||||
This was a compelling offer versus solo-founding a company around Atlantis: I would be able to focus on coding and product instead of business and sales and I could spend all of my time on Atlantis and the larger story of Terraform collaboration. As a result, I came to the conclusion that joining HashiCorp was the right decision for me and the community.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Atlantis has been a passion of mine for almost two years now. I deeply care about the future of the project and its community and I know that this move will ensure that that future is bright.
|
||||
|
||||
There are probably some questions I haven't answered in this post so please don't hesitate to reach out, either via [Twitter](https://twitter.com/lkysow) or on the [Atlantis Slack](https://join.slack.com/t/atlantis-community/shared_invite/zt-9xlxtxtc-CUSKB1ATt_sQy6um~LDPNw).
|
||||
|
||||
I'm excited for the future of Atlantis and Terraform collaboration and I hope you are too.
|
||||
@@ -0,0 +1,244 @@
|
||||
---
|
||||
title: "Putting The Dev Into DevOps: Why Your Developers Should Write Terraform Too"
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Putting The Dev Into DevOps: Why Your Developers Should Write Terraform Too
|
||||
|
||||
::: info
|
||||
This post was originally written on August 29th, 2018
|
||||
|
||||
Original post: <https://medium.com/runatlantis/putting-the-dev-into-devops-why-your-developers-should-write-terraform-too-d3c079dfc6a8>
|
||||
:::
|
||||
|
||||
[Terraform](https://www.terraform.io/) is an amazing tool for provisioning infrastructure. Terraform enables your operators to perform their work faster and more reliably.
|
||||
|
||||
**But if only your ops team is writing Terraform, you're missing out.**
|
||||
|
||||
Terraform is not just a tool that makes ops teams more effective. Adopting Terraform is an opportunity to turn all of your developers into operators (at least for smaller tasks). This can make your entire engineering team more effective and create a better relationship between developers and operators.
|
||||
|
||||
### Quick Aside — What is Terraform?
|
||||
|
||||
Terraform is two things. It's a language for describing infrastructure:
|
||||
|
||||
```tf
|
||||
resource "aws_instance" "example" {
|
||||
ami = "ami-2757f631"
|
||||
instance_type = "t2.micro"
|
||||
}
|
||||
```
|
||||
|
||||
And it's a CLI tool that reads Terraform code and makes API calls to AWS (or any other cloud provider) to provision that infrastructure.
|
||||
|
||||
In this example, we're using the CLI to run `terraform apply` which will create an EC2 instance:
|
||||
|
||||
```sh
|
||||
$ terraform apply
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# aws_instance.example
|
||||
+ aws_instance.example
|
||||
ami: "ami-2757f631"
|
||||
instance_type: "t2.micro"
|
||||
...
|
||||
|
||||
Plan: 1 to add, 0 to change, 0 to destroy.
|
||||
|
||||
Do you want to perform these actions?
|
||||
Terraform will perform the actions described above.
|
||||
Only 'yes' will be accepted to approve.
|
||||
|
||||
Enter a value: yes
|
||||
|
||||
aws_instance.example: Creating...
|
||||
ami: "" => "ami-2757f631"
|
||||
instance_type: "" => "t2.micro"
|
||||
...
|
||||
|
||||
aws_instance.example: Still creating... (10s elapsed)
|
||||
aws_instance.example: Creation complete
|
||||
|
||||
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
|
||||
```
|
||||
|
||||
## Terraform Adoption From A Dev's Perspective
|
||||
|
||||
Adopting Terraform is great for your operations team's effectiveness but it doesn't change much for devs. Before Terraform adoption, devs typically interacted with an ops team like this:
|
||||
|
||||

|
||||
|
||||
1. **Dev: Creates ticket asking for some ops work**
|
||||
2. **Dev: Waits**
|
||||
3. _Ops: Looks at ticket when in queue_
|
||||
4. _Ops: Does work_
|
||||
5. _Ops: Updates ticket_
|
||||
6. **Dev: Continues their work**
|
||||
|
||||
After the Ops team adopts Terraform, the workflow from a dev's perspective is the same!
|
||||
|
||||

|
||||
|
||||
1. **Dev: Creates ticket asking for some ops work**
|
||||
2. **Dev: Waits**
|
||||
3. _Ops: Looks at ticket when in queue_
|
||||
4. _Ops: Does work. This time using Terraform (TF)_
|
||||
5. _Ops: Updates ticket_
|
||||
6. **Dev: Continues their work**
|
||||
|
||||
With Terraform, there's less of Step 2 (Dev: Waits) but apart from that, not much has changed.
|
||||
|
||||
> If only ops is writing Terraform, your developers' experience is the same.
|
||||
|
||||
## Devs Want To Help
|
||||
|
||||
Developers would love to help out with operations work. They know that for small changes they should be able to do the work themselves (with a review from ops). For example:
|
||||
|
||||
- Adding a new security group rule
|
||||
- Increasing the size of an autoscaling group
|
||||
- Using a larger instance because their app needs more memory
|
||||
|
||||
Developers could make all of these changes because they're small and well defined. Also, previous examples of doing the same thing can guide them.
|
||||
|
||||
## ...But Often They're Not Allowed
|
||||
|
||||
In many organizations, devs are locked out of the cloud console.
|
||||
|
||||

|
||||
|
||||
They might be locked out for good reasons:
|
||||
|
||||
- Security — You can do a lot of damage with full access to a cloud console
|
||||
- Compliance — Maybe your compliance requires only certain groups to have access
|
||||
- Cost — Devs might spin up some expensive resources and then forget about them
|
||||
|
||||
Even if they have access, operations can be complicated:
|
||||
|
||||
- It's often difficult to do seemingly simple things (think adding a security group rule that also requires peering VPCs). This means that just having access sometimes isn't enough. Devs might need help from an expert to get things done.
|
||||
|
||||
## Enter Terraform
|
||||
|
||||
With Terraform, everything changes. Or at least it can.
|
||||
|
||||
Now Devs can see in code how infrastructure is built. They can see the exact spot where security group rules are configured:
|
||||
|
||||
```tf
|
||||
resource "aws_security_group_rule" "allow_all" {
|
||||
type = "ingress"
|
||||
from_port = 0
|
||||
to_port = 65535
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
security_group_id = "sg-123456"
|
||||
}
|
||||
|
||||
resource "aws_security_group_rule" "allow_office" {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Or where the size of the autoscaling group is set:
|
||||
|
||||
```tf
|
||||
resource "aws_autoscaling_group" "asg" {
|
||||
name = "my-asg"
|
||||
max_size = 5
|
||||
desired_capacity = 4
|
||||
min_size = 2
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Devs understand code (surprise!) so it's a lot easier for them to make those small changes.
|
||||
|
||||
Here's the new workflow:
|
||||
|
||||

|
||||
|
||||
1. **Dev: Writes Terraform code**
|
||||
2. **Dev: Creates pull request**
|
||||
3. _Ops: Reviews pull request_
|
||||
4. **Dev: Applies the change with Terraform (TF)**
|
||||
5. **Dev: Continues their work**
|
||||
|
||||
Now:
|
||||
|
||||
- Devs are making small changes themselves. This saves time and increases the speed of the whole engineering organization.
|
||||
- Devs can see exactly what is required to make the change. This means there's less back and forth over a ticket: “Okay so I know you need the security group opened between server A and B, but on which ports and with which protocol?”
|
||||
- Devs start to see how infrastructure is built. This increases cooperation between dev and ops because they can understand each other's work.
|
||||
|
||||
Great! But there's another problem.
|
||||
|
||||
## Devs Are Locked Out Of Terraform Too
|
||||
|
||||
In order to execute Terraform you need to have cloud credentials! It's really hard to write Terraform without being able to run `terraform init` and `terraform plan`, for the same reason it would be hard to write code if you could never run it locally!
|
||||
|
||||
So are we back at square one?
|
||||
|
||||
## Enter Atlantis
|
||||
|
||||
[Atlantis](https://www.runatlantis.io/) is an [open source](https://github.com/runatlantis/atlantis) tool for running Terraform from pull requests. With Atlantis, Terraform is run on a separate server (Atlantis is self-hosted) so you don't need to give out credentials to everyone. Access is controlled through pull request approvals.
|
||||
|
||||
Here's what the workflow looks like:
|
||||
|
||||
### Step 1 — Create a Pull Request
|
||||
|
||||
A developer creates a pull request with their change to add a security group rule.
|
||||
|
||||

|
||||
|
||||
### Step 2 — Atlantis Runs Terraform Plan
|
||||
|
||||
Atlantis automatically runs `terraform plan` and comments back on the pull request with the output. Now developers can fix their Terraform errors before asking for a review.
|
||||
|
||||

|
||||
|
||||
### Step 3 — Fix The Terraform
|
||||
|
||||
The developer pushes a new commit that fixes their error and Atlantis comments back with the valid `terraform plan` output. Now the developer can verify that the plan output looks good.
|
||||
|
||||

|
||||
|
||||
### Step 4 — Get Approval
|
||||
|
||||
You'll probably want to run Atlantis with the --require-approval flag that requires pull requests to be Approved before running atlantis apply.
|
||||
|
||||

|
||||
|
||||
### Step 4a — Actually Get Approval
|
||||
|
||||
An operator can now come along and review the changes and the output of `terraform plan`. This is much faster than doing the change themselves.
|
||||
|
||||

|
||||
|
||||
### Step 5 — Apply
|
||||
|
||||
To apply the changes, the developer or operator comments “atlantis apply”.
|
||||
|
||||

|
||||
|
||||
## Success
|
||||
|
||||
Now we've got a workflow that makes everyone happy:
|
||||
|
||||
- Devs can write Terraform and iterate on the pull request until the `terraform plan` looks good
|
||||
- Operators can review pull requests and approve the changes before they're applied
|
||||
|
||||
Now developers can make small operations changes and learn more about how infrastructure is built. Everyone can work more effectively and with a shared understanding that enhances collaboration.
|
||||
|
||||
## Does It Work In Practice?
|
||||
|
||||
Atlantis has been used by my previous company, Hootsuite, for over 2 years. It's used daily by 20 operators but it's also used occasionally by over 60 developers!
|
||||
Another company uses Atlantis to manage 600+ Terraform repos collaborated on by over 300 developers and operators.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- If you'd like to learn more about Terraform, check out HashiCorp's [Introduction to Terraform](https://developer.hashicorp.com/terraform/intro)
|
||||
- If you'd like to try out Atlantis, go to <www.runatlantis.io>
|
||||
- If you have any questions, reach out to me on Twitter ([at]lkysow) or in the comments below.
|
||||
|
||||
## Credits
|
||||
|
||||
- Thanks to [Seth Vargo](https://medium.com/@sethvargo) for his talk [Version-Controlled Infrastructure with GitHub](https://www.youtube.com/watch?v=2TWqi7dLSro) that inspired a lot of this post.
|
||||
- Thanks to Isha for reading drafts of this post.
|
||||
- Icons in graphics from made by [Freepik](http://freepik.com/) from [Flaticon](https://www.flaticon.com/) and licensed by [CC 3.0](https://creativecommons.org/licenses/by/3.0/)
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: Terraform And The Dangers Of Applying Locally
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Terraform And The Dangers Of Applying Locally
|
||||
|
||||
::: info
|
||||
This post was originally written on July 13th, 2018
|
||||
|
||||
Original post: <https://medium.com/runatlantis/terraform-and-the-dangers-of-applying-locally-543563782a73>
|
||||
:::
|
||||
|
||||
If you're using Terraform then at some point you've likely ran a `terraform apply` that reverted someone else's change!
|
||||
|
||||
Here's how that tends to happen:
|
||||
|
||||
## The Setup
|
||||
|
||||
Say we have two developers: Alice and Bob. Alice needs to add a new security group rule. She checks out a new branch, adds her rule and creates a pull request:
|
||||
|
||||

|
||||
|
||||
When she runs `terraform plan` locally she sees what she expects.
|
||||
|
||||

|
||||
|
||||
Meanwhile, Bob is working on an emergency fix. He checks out a new branch and adds a different security group rule called `emergency`:
|
||||
|
||||

|
||||
|
||||
And, because it's an emergency, he **immediately runs apply**:
|
||||
|
||||

|
||||
|
||||
Now back to Alice. She's just gotten approval on her pull request change and so she runs `terraform apply`:
|
||||
|
||||

|
||||
|
||||
Did you catch what happened? Did you notice that the `apply` deleted Bob's rule?
|
||||
|
||||

|
||||
|
||||
In this example, it wasn't too hard to see. However if the plan is much longer, or if the change is less obvious then it can be easy to miss.
|
||||
|
||||
## Possible Solutions
|
||||
|
||||
There are some ways to avoid this:
|
||||
|
||||
### Use terraform plan `-out`
|
||||
|
||||
If Alice had run `terraform plan -out plan.tfplan` then when she ran `terraform apply plan.tfplan` she would see:
|
||||
|
||||

|
||||
|
||||
The problem with this solution is that few people run `terraform plan` anymore, much less `terraform plan -out`!
|
||||
|
||||
<iframe src="https://cdn.embedly.com/widgets/media.html?type=text%2Fhtml&key=a19fcc184b9711e1b4764040d3dc5c07&schema=twitter&url=https%3A//twitter.com/sethvargo/status/989979940098424832&image=https%3A//i.embed.ly/1/image%3Furl%3Dhttps%253A%252F%252Fpbs.twimg.com%252Fprofile_images%252F808025120296013825%252FfrGuc14s_400x400.jpg%26key%3Da19fcc184b9711e1b4764040d3dc5c07" allowfullscreen="" frameborder="0" height="249" width="680" title="Seth Vargo on Twitter" class="fr n gh dv bg" scrolling="no"></iframe>
|
||||
|
||||
It's easier to just run `terraform apply` and humans will take the easier path most of the time.
|
||||
|
||||
### Wrap `terraform apply` to ensure up to date with `master`
|
||||
|
||||
Another possible solution is to write a wrapper script that ensures our branch is up to date with `master`. But this doesn't solve the problem of Bob running `apply` locally and not yet merging to `master`. In this case, Alice's branch would have been up to date with `master` but not the latest apply'd state.
|
||||
|
||||
### Be more disciplined
|
||||
|
||||
What if everyone:
|
||||
|
||||
- ALWAYS created a branch, got a pull request review, merged to `master` and then ran apply. And also everyone
|
||||
- ALWAYS checked to ensure their branch was rebased from `master`. And also everyone
|
||||
- ALWAYS carefully inspected the `terraform plan` output and made sure it was exactly what they expected
|
||||
|
||||
...then we wouldn't have a problem!
|
||||
|
||||
Unfortunately this is not a real solution. We're all human and we're all going to make mistakes. Relying on people to follow a complicated process 100% of the time is not a solution because it doesn't work.
|
||||
|
||||
## Core Problem
|
||||
|
||||
The core problem is that everyone is applying from their own workstations and it's up to them to ensure that they're up to date and that they keep `master` up to date. This is like developers deploying to production from their laptops.
|
||||
|
||||
### What if, instead of applying locally, a remote system did the apply's?
|
||||
|
||||
This is why we built [Atlantis](https://www.runatlantis.io/) – an open source project for Terraform automation by pull request. You could also accomplished this with your own CI system or with [Terraform Enterprise](https://www.hashicorp.com/products/terraform). Here's how Atlantis solves this issue:
|
||||
|
||||
When Alice makes her change, she creates a pull request and Atlantis automatically runs `terraform plan` and comments on the pull request.
|
||||
|
||||
When Bob makes his change, he creates a pull request and Atlantis automatically runs `terraform plan` and comments on the pull request.
|
||||
|
||||

|
||||
|
||||
Atlantis also **locks the directory** to ensure that no one else can run `plan` or `apply` until Alice's plan has been intentionally deleted or she merges the pull request.
|
||||
|
||||
If Bob creates a pull request for his emergency change he'd see this error:
|
||||
|
||||

|
||||
|
||||
Alice can then comment `atlantis apply` and Atlantis will run the apply itself:
|
||||
|
||||

|
||||
|
||||
Finally, she merges the pull request and unlocks Bob's branch:
|
||||
|
||||

|
||||
|
||||
### But what if Bob ran `apply` locally?
|
||||
|
||||
In that case, Alice is still okay because when Atlantis ran `terraform plan` it used `-out`. If Alice tries to apply that plan, Terraform will give an error because the plan was generated against an old state.
|
||||
|
||||
### Why does Atlantis run `apply` on the branch and not after a merge to `master`?
|
||||
|
||||
We do this because `terraform apply` fails quite often, despite `terraform plan` succeeding. Usually it's because of a dependency issue between resources or because the cloud provider requires a certain format or a certain field to be set. Regardless, in practice we've found that `apply` fails a lot.
|
||||
|
||||
By locking the directory, we're essentially ensuring that the branch being `apply`'d is `"master"` since no one else can modify that state. We then get the benefit of being able to iterate on the pull request and push small fixes until we're sure that the changeset is `apply`'d. If `apply` failed after merging to `master`, we'd have to open new pull requests over and over again. There is definitely a tradeoff here, however we believe it's the right tradeoff.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In conclusion, running `terraform apply` when you're working with a team of operators can be dangerous. Look to solutions like your own CI, Atlantis or Terraform Enterprise to ensure you're always working off the latest code that was `apply`'d.
|
||||
|
||||
If you'd like to try Atlantis, you can get started here: <https://www.runatlantis.io/guide/>
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
title: 4 Reasons To Try HashiCorp's (New) Free Terraform Remote State Storage
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# 4 Reasons To Try HashiCorp's (New) Free Terraform Remote State Storage
|
||||
|
||||
::: info
|
||||
This post was originally written on April 2nd, 2019
|
||||
|
||||
Original post: <https://medium.com/runatlantis/4-reasons-to-try-hashicorps-new-free-terraform-remote-state-storage-b03f01bfd251>
|
||||
:::
|
||||
|
||||
Update (May 20/19) — Free State Storage is now called Terraform Cloud and is out of Beta, meaning anyone can sign up!
|
||||
|
||||
HashiCorp is planning to offer free Terraform Remote State Storage and they have a beta version available now. In this article, I talk about 4 reasons you should try it (Disclosure: I work at HashiCorp).
|
||||
|
||||
> _Sign up for Terraform Cloud [here](https://goo.gl/X5t5EM)._
|
||||
|
||||
## What is Terraform State?
|
||||
|
||||
Before I get into why you should use the new remote state storage, let's talk about what exactly we mean by state in Terraform.
|
||||
|
||||
Terraform uses _state_ to map your Terraform code to the real-world resources that it provisions. For example, if I have Terraform code to create an AWS EC2 instance:
|
||||
|
||||
```tf
|
||||
resource "aws_instance" "web" {
|
||||
ami = "ami-e6d9d68c"
|
||||
instance_type = "t2.micro"
|
||||
}
|
||||
```
|
||||
|
||||
When I run `terraform apply`, Terraform will make a “create EC2 instance” API call to AWS and AWS will return the unique ID of that instance (ex. `i-0ad17607e5ee026d0`). Terraform needs to record that ID somewhere so that later, it can make API calls to change or delete the instance.
|
||||
|
||||
To store this information, Terraform uses a state file. For the above code, the state file will look something like:
|
||||
|
||||
```json{4,7}
|
||||
{
|
||||
...
|
||||
"resources": {
|
||||
"aws_instance.web": {
|
||||
"type": "aws_instance",
|
||||
"primary": {
|
||||
"id": "i-0ad17607e5ee026d0",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Here you can see that the resource `aws_instance.web` from our Terraform code is mapped to the instance ID `i-0ad17607e5ee026d0`.
|
||||
|
||||
So if Terraform state is just a file, then what is remote state?
|
||||
|
||||
## Remote State
|
||||
|
||||
By default, Terraform writes its state file to your local filesystem. This is okay for personal projects, but once you start working with a team, things get messy. In a team, you need to make sure everyone has an up to date version of the state file **and** ensure that two people aren't making concurrent changes.
|
||||
|
||||
Enter remote state! Remote state is just storing the state file remotely, rather than on your filesystem. With remote state, there's only one copy so Terraform can ensure you're always up to date. To prevent team members from modifying state at the same time, Terraform can lock the remote state.
|
||||
|
||||
> Remote state is just storing the state file remotely, rather than on your filesystem.
|
||||
|
||||
Alright, so remote state is great, but unfortunately setting it up can be a bit tricky. In AWS, you can store it in an S3 bucket, but you need to create the bucket, configure it properly, set up its permissions properly, create a DynamoDB table for locking and then ensure everyone has proper credentials to write to it. It's much the same story in the other clouds.
|
||||
|
||||
As a result, setting up remote state can be an annoying stumbling block as teams adopt Terraform.
|
||||
|
||||
This brings us to the first reason to try HashiCorp's Free Remote State Storage...
|
||||
|
||||
## Reason #1 — Easy To Set Up
|
||||
|
||||
Unlike other remote state solutions that require complicated setup to get right, setting up free remote state storage is easy.
|
||||
|
||||
> Setting up HashiCorp's free remote state storage is easy
|
||||
|
||||
Step 1 — Sign up for your [free Terraform Cloud](https://app.terraform.io/signup) account
|
||||
|
||||
Step 2 — When you log in, you'll land on this page where you'll create your organization:
|
||||
|
||||

|
||||
|
||||
Step 3 — Next, go into User Settings and generate a token:
|
||||
|
||||

|
||||
|
||||
Step 4 — Take this token and create a local ~/.terraformrc file:
|
||||
|
||||
```tf
|
||||
credentials "app.terraform.io" {
|
||||
token = "mhVn15hHLylFvQ.atlasv1.jAH..."
|
||||
}
|
||||
```
|
||||
|
||||
Step 5 — That's it! Now you're ready to store your state.
|
||||
|
||||
In your Terraform project, add a `terraform` block:
|
||||
|
||||
```tf{3,5}
|
||||
terraform {
|
||||
backend "remote" {
|
||||
organization = "my-org" # org name from step 2.
|
||||
workspaces {
|
||||
name = "my-app" # name for your app's state.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run `terraform init` and tada! Your state is now being stored in Terraform Enterprise. You can see the state in the UI:
|
||||
|
||||

|
||||
|
||||
Speaking of seeing state in a UI...
|
||||
|
||||
## Reason #2 — Fully Featured State Viewer
|
||||
|
||||
The second reason to try Terraform Cloud is its fully featured state viewer:
|
||||
|
||||

|
||||
|
||||
If you've ever messed up your Terraform state and needed to download an old version or wanted an audit log to know who changed what, then you'll love this feature.
|
||||
|
||||
You can view the full state file at each point in time:
|
||||
|
||||

|
||||
|
||||
You can also see the diff of what changed:
|
||||
|
||||

|
||||
|
||||
Of course, you can find a way to get this information from some of the other state backends, but it's difficult. With HashiCorp's remote state storage, you get it for free.
|
||||
|
||||
## Reason #3 — Manual Locking
|
||||
|
||||
The third reason to try Terraform Cloud is the ability to manually lock your state.
|
||||
|
||||
Ever been working on a piece of infrastructure and wanted to ensure that no one could make any changes to it at the same time?
|
||||
|
||||
Terraform Cloud comes with the ability to lock and unlock states from the UI:
|
||||
|
||||

|
||||
|
||||
While the state is locked, `terraform` operations will receive an error:
|
||||
|
||||

|
||||
|
||||
This saves you a lot of these:
|
||||
|
||||

|
||||
|
||||
## Reason #4 — Works With Atlantis
|
||||
|
||||
The final reason to try out Terraform Cloud is that it works flawlessly with [Atlantis](https://www.runatlantis.io/)!
|
||||
|
||||
Set a `ATLANTIS_TFE_TOKEN` environment variable to a TFE token and you're ready to go. Head over to <https://www.runatlantis.io/docs/terraform-cloud.html> to learn more.
|
||||
|
||||
Conclusion
|
||||
I highly encourage you to try out the new free Remote State Storage backend. It's a compelling offering over other state backends thanks to its ease of set up, fully featured state viewer and locking capabilities.
|
||||
|
||||
If you're not on the waitlist, sign up here: <https://app.terraform.io/signup>.
|
||||
56
runatlantis.io/blog/2024/april-2024-survey-results.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Atlantis User Survey Results
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Atlantis User Survey Results
|
||||
|
||||
In April 2024, the Core Atlantis Team launched an anonymous survey of our users. Over the two months the survey was open we received 354 responses, which we will use to better understand our community's needs and help prioritize our roadmap.
|
||||
|
||||
Overall, the results below show that we have a diverse set of enthusiastic users, and that though many are still the classic Atlantis setup (a handful of repos running terraform against AWS in GitHub), there are many different use cases and directions the community are going and would like to see Atlantis support.
|
||||
|
||||
We are grateful for everyone who took the time to share their experiences with Atlantis. We plan to run this kind of survey on a semi-regular basis, stay tuned!
|
||||
|
||||
## Anonymized Results
|
||||
|
||||
### How do you interact with Atlantis?
|
||||
|
||||

|
||||
|
||||
Unsurprisingly, most users of Atlantis wear multiple hats, involved throughout the development process.
|
||||
|
||||
### How do you/your organization deploy Atlantis
|
||||
|
||||

|
||||
|
||||
Most users of terraform deploy using Kubernetes and/or AWS. "Other Docker" use docker but do not use EKS or Helm directly, while a minority use some other combination of technologies.
|
||||
|
||||
### What Infrastructure as Code (IaC) tool(s) do you use with Atlantis?
|
||||
|
||||

|
||||
|
||||
The vast majority of Atlantis users are still using terraform as some part of their deployment. About half of them are in addition using Terragrunt, and OpenTofu seems to be gaining some ground.
|
||||
|
||||
### How many repositories does your Atlantis manage?
|
||||
|
||||

|
||||
|
||||
Most users have relatively modest footprints to managed with Atlantis (though a few large monorepos could be obscured in the numbers).
|
||||
|
||||
### Which Version Control Systems (VCSs) do you use?
|
||||
|
||||

|
||||
|
||||
Most users of Atlantis are using GitHub, with a sizeable chunk on GitLab, followed by Bitbucket and others. This is analogous to the support and feature requests that the maintainers see for the various VCSs in the codebase.
|
||||
|
||||
### What is the most important feature you find missing from Atlantis?
|
||||
|
||||

|
||||
|
||||
This being a free form question, there was a long tail of responses, so the above only shows answers after normalizing that had three or more instances.
|
||||
|
||||
Drift Detection as well as infrastructure improvements were the obvious winners here. After that, users focused on various integrations and improvements to the UI.
|
||||
|
||||
## Conclusion
|
||||
|
||||
It is always interesting and exciting for the core team to see the breadth of the use of Atlantis, and we look forward to using this information to understand the needs of the community. Atlantis has always been a community led effort, and we hope to continue to carry that spirit forward!
|
||||
200
runatlantis.io/blog/2024/integrating-atlantis-with-opentofu.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
title: Integrating Atlantis with Opentofu
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# Integrating Atlantis with Opentofu
|
||||
|
||||
::: info
|
||||
This post was originally written on May 27nd, 2024
|
||||
Original post: <https://dev.to/jmateusousa/integrating-atlantis-with-opentofu-lnd>
|
||||
:::
|
||||
|
||||
## What was our motivation?
|
||||
|
||||
Due to the Terraform license change, many companies are migrating their IAC processes to OpenTofu, with this in mind and knowing that many of them use Atlantis and Terraform as infrastructure delivery automation, I created this documentation showing what to do to integrate Atlantis with OpenTofu.
|
||||
|
||||
Stack: Atlantis, Terragrunt, OpenTofu, Github, ALB, EKS.
|
||||
|
||||
We will implement it with your [Helm chart](https://www.runatlantis.io/docs/deployment.html#kubernetes-helm-chart):
|
||||
|
||||
**1** - Add the runatlantis repository.
|
||||
|
||||
```sh
|
||||
helm repo add runatlantis https://runatlantis.github.io/helm-charts
|
||||
```
|
||||
|
||||
**2** - Create file values.yaml and run:
|
||||
|
||||
```sh
|
||||
helm inspect values runatlantis/atlantis > values.yaml
|
||||
```
|
||||
|
||||
**3** - Edit the file values.yaml and add your credentials access and secret which will be used in the Atlantis webhook configuration:
|
||||
See as create a [GitHubApp](https://docs.github.com/pt/apps/creating-github-apps/about-creating-github-apps).
|
||||
|
||||
```yaml
|
||||
githubApp:
|
||||
id: "CHANGE ME"
|
||||
key: |
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
"CHANGE ME"
|
||||
-----END RSA PRIVATE KEY-----
|
||||
slug: atlantis
|
||||
# secret webhook Atlantis
|
||||
secret: "CHANGE ME"
|
||||
```
|
||||
|
||||
**4** - Enter the org and repository from github that Atlantis will interact in orgAllowlist:
|
||||
|
||||
```yaml
|
||||
# All repositories the org
|
||||
orgAllowlist: github.com/MY-ORG/*
|
||||
|
||||
or
|
||||
# Just one repository
|
||||
orgAllowlist: github.com/MY-ORG/MY-REPO-IAC
|
||||
|
||||
or
|
||||
# All repositories that start with MY-REPO-IAC-
|
||||
orgAllowlist: github.com/MY-ORG/MY-REPO-IAC-*
|
||||
```
|
||||
|
||||
**5** - Now let’s configure the script that will be executed upon startup of the Atlantis init pod. In this step we download and install Terragrunt and OpenTofu, as well as include their binaries in the shared dir ```/plugins```.
|
||||
|
||||
```yaml
|
||||
initConfig:
|
||||
enabled: true
|
||||
image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
# sharedDir is set as env var INIT_SHARED_DIR
|
||||
sharedDir: /plugins
|
||||
workDir: /tmp
|
||||
sizeLimit: 250Mi
|
||||
# example of how the script can be configured to install tools/providers required by the atlantis pod
|
||||
script: |
|
||||
#!/bin/sh
|
||||
set -eoux pipefail# terragrunt
|
||||
TG_VERSION="0.55.10"
|
||||
TG_SHA256_SUM="1ad609399352348a41bb5ea96fdff5c7a18ac223742f60603a557a54fc8c6cff"
|
||||
TG_FILE="${INIT_SHARED_DIR}/terragrunt"
|
||||
wget https://github.com/gruntwork-io/terragrunt/releases/download/v${TG_VERSION}/terragrunt_linux_amd64 -O "${TG_FILE}"
|
||||
echo "${TG_SHA256_SUM} ${TG_FILE}" | sha256sum -c
|
||||
chmod 755 "${TG_FILE}"
|
||||
terragrunt -v
|
||||
|
||||
# OpenTofu
|
||||
TF_VERSION="1.6.2"
|
||||
TF_FILE="${INIT_SHARED_DIR}/tofu"
|
||||
wget https://github.com/opentofu/opentofu/releases/download/v${TF_VERSION}/tofu_${TF_VERSION}_linux_amd64.zip
|
||||
unzip tofu_${TF_VERSION}_linux_amd64.zip
|
||||
mv tofu ${INIT_SHARED_DIR}
|
||||
chmod 755 "${TF_FILE}"
|
||||
tofu -v
|
||||
```
|
||||
|
||||
**6** - Here we configure the envs to avoid downloading alternative versions of Terraform and indicate to Terragrunt where it should fetch the OpenTofu binary.
|
||||
|
||||
```yaml
|
||||
# envs
|
||||
environment:
|
||||
ATLANTIS_TF_DOWNLOAD: false
|
||||
TERRAGRUNT_TFPATH: /plugins/tofu
|
||||
```
|
||||
|
||||
**7** - Last but not least, here we specify which Atlantis-side configurations we will have for the repositories.
|
||||
|
||||
```yaml
|
||||
# repository config
|
||||
repoConfig: |
|
||||
---
|
||||
repos:
|
||||
- id: /.*/
|
||||
apply_requirements: [approved, mergeable]
|
||||
allow_custom_workflows: true
|
||||
allowed_overrides: [workflow, apply_requirements, delete_source_branch_on_merge]
|
||||
```
|
||||
|
||||
**8** - Configure Atlantis webhook ingress, in the example below we are using the AWS ALB.
|
||||
|
||||
```yaml
|
||||
# ingress config
|
||||
ingress:
|
||||
annotations:
|
||||
alb.ingress.kubernetes.io/backend-protocol: HTTP
|
||||
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:certificate
|
||||
alb.ingress.kubernetes.io/group.name: external-atlantis
|
||||
alb.ingress.kubernetes.io/healthcheck-path: /healthz
|
||||
alb.ingress.kubernetes.io/healthcheck-port: "80"
|
||||
alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
|
||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
||||
alb.ingress.kubernetes.io/success-codes: "200"
|
||||
alb.ingress.kubernetes.io/target-type: ip
|
||||
apiVersion: networking.k8s.io/v1
|
||||
enabled: true
|
||||
host: atlantis.your.domain
|
||||
ingressClassName: aws-ingress-class-name
|
||||
path: /*
|
||||
pathType: ImplementationSpecific
|
||||
```
|
||||
|
||||
Save all changes made to ```values.yaml```
|
||||
|
||||
**9** - Using one of the Atlantis options custom workflows, we can create a file ```atlantis.yaml``` in the root folder of your repository, the example below should meet most scenarios, adapt as needed.
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
automerge: true
|
||||
parallel_plan: true
|
||||
parallel_apply: false
|
||||
projects:
|
||||
- name: terragrunt
|
||||
dir: .
|
||||
workspace: terragrunt
|
||||
delete_source_branch_on_merge: true
|
||||
autoplan:
|
||||
enabled: false
|
||||
apply_requirements: [mergeable, approved]
|
||||
workflow: terragrunt
|
||||
workflows:
|
||||
terragrunt:
|
||||
plan:
|
||||
steps:
|
||||
- env:
|
||||
name: TF_IN_AUTOMATION
|
||||
value: 'true'
|
||||
- run: find . -name '.terragrunt-cache' | xargs rm -rf
|
||||
- run: terragrunt init -reconfigure
|
||||
- run:
|
||||
command: terragrunt plan -input=false -out=$PLANFILE
|
||||
output: strip_refreshing
|
||||
apply:
|
||||
steps:
|
||||
- run: terragrunt apply $PLANFILE
|
||||
```
|
||||
|
||||
**10** - Now let’s go to the installation itself, search for the available versions of Atlantis:
|
||||
|
||||
```sh
|
||||
helm search repo runatlantis
|
||||
```
|
||||
|
||||
Replace ```CHART-VERSION``` with the version you want to install and run the command below:
|
||||
|
||||
```sh
|
||||
helm upgrade -i atlantis runatlantis/atlantis --version CHART-VERSION -f values.yaml --create-namespace atlantis
|
||||
```
|
||||
|
||||
Now, see as configure Atlantis [webhook on github](../../docs/configuring-webhooks.md) repository.
|
||||
|
||||
See as Atlantis [work](../../docs/using-atlantis.md).
|
||||
|
||||
Find out more at:
|
||||
|
||||
- <https://www.runatlantis.io/guide.html>.
|
||||
- <https://opentofu.org/docs/>.
|
||||
- <https://github.com/runatlantis/atlantis/issues/3741>.
|
||||
|
||||
Share it with your friends =)
|
||||
17
runatlantis.io/contributing.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
aside: false
|
||||
---
|
||||
# Atlantis Contributing Documentation
|
||||
|
||||
These docs are for users who want to contribute to the Atlantis project. This
|
||||
can vary from writing documentation, helping the community on Slack, discussing
|
||||
issues, or writing code.
|
||||
|
||||
:::tip Looking to get started or use Atlantis?
|
||||
If you're new, check out the [Guide](./guide.md) or the
|
||||
[Documentation](./docs.md).
|
||||
:::
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Events Controller](./contributing/events-controller.md) – How do the events work?
|
||||
108
runatlantis.io/contributing/events-controller.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Events Controller
|
||||
|
||||
Webhooks are the primary interaction between the Version Control System (VCS)
|
||||
and Atlantis. Each VCS sends the requests to the `/events` endpoint. The
|
||||
implementation of this endpoint can be found in the
|
||||
[events_controller.go](https://github.com/runatlantis/atlantis/blob/main/server/controllers/events/events_controller.go)
|
||||
file. This file contains the Post function `func (e *VCSEventsController)
|
||||
Post(w http.ResponseWriter, r *http.Request`)` that parses the request
|
||||
according to the configured VCS.
|
||||
|
||||
Atlantis currently handles one of the following events:
|
||||
|
||||
- Comment Event
|
||||
- Pull Request Event
|
||||
|
||||
All the other events are ignored.
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: events controller flowchart
|
||||
---
|
||||
flowchart LR
|
||||
events(/events - Endpoint) --> Comment_Event(Comment - Event)
|
||||
events --> Pull_Request_Event(Pull Request - Event)
|
||||
|
||||
Comment_Event --> pre_workflow(pre-workflow - Hook)
|
||||
pre_workflow --> plan(plan - command)
|
||||
pre_workflow --> apply(apply - command)
|
||||
pre_workflow --> approve_policies(approve policies - command)
|
||||
pre_workflow --> unlock(unlock - command)
|
||||
pre_workflow --> version(version - command)
|
||||
pre_workflow --> import(import - command)
|
||||
pre_workflow --> state(state - command)
|
||||
|
||||
plan --> post_workflow(post-workflow - Hook)
|
||||
apply --> post_workflow
|
||||
approve_policies --> post_workflow
|
||||
unlock --> post_workflow
|
||||
version --> post_workflow
|
||||
import --> post_workflow
|
||||
state --> post_workflow
|
||||
|
||||
Pull_Request_Event --> Open_Update_PR(Open / Update Pull Request)
|
||||
Pull_Request_Event --> Close_PR(Close Pull Request)
|
||||
|
||||
Open_Update_PR --> pre_workflow(pre-workflow - Hook)
|
||||
Close_PR --> plan(plan - command)
|
||||
|
||||
pre_workflow --> plan
|
||||
plan --> post_workflow(post-workflow - Hook)
|
||||
|
||||
Close_PR --> CleanUpPull(CleanUpPull)
|
||||
CleanUpPull --> post_workflow(post-workflow - Hook)
|
||||
```
|
||||
|
||||
## Comment Event
|
||||
|
||||
This event is triggered whenever a user enters a comment on the Pull Request,
|
||||
Merge Request, or whatever it's called for the respective VCS. After parsing the
|
||||
VCS-specific request, the code calls the `handleCommentEvent` function, which
|
||||
then passes the processing to the `handleCommentEvent` function in the
|
||||
[command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/command_runner.go)
|
||||
file. This function first calls the pre-workflow hooks, then executes one of the
|
||||
below-listed commands and, at last, the post-workflow hooks.
|
||||
|
||||
- [plan_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/plan_command_runner.go)
|
||||
- [apply_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/apply_command_runner.go)
|
||||
- [approve_policies_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/approve_policies_command_runner.go)
|
||||
- [unlock_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/unlock_command_runner.go)
|
||||
- [version_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/version_command_runner.go)
|
||||
- [import_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/import_command_runner.go)
|
||||
- [state_command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/state_command_runner.go)
|
||||
|
||||
## Pull Request Event
|
||||
|
||||
To handle comment events on Pull Requests, they must be created first. Atlantis
|
||||
also allows the running of commands for certain Pull Requests events.
|
||||
|
||||
<details>
|
||||
<summary>Pull Request Webhooks</summary>
|
||||
|
||||
The list below links to the supported VCSs and their Pull Request Webhook
|
||||
documentation.
|
||||
|
||||
- [Azure DevOps Pull Request Created](https://learn.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#pull-request-created)
|
||||
- [BitBucket Pull Request](https://support.atlassian.com/bitbucket-cloud/docs/event-payloads/#Pull-request-events)
|
||||
- [GitHub Pull Request](https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request)
|
||||
- [GitLab Merge Request](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events)
|
||||
- [Gitea Webhooks](https://docs.gitea.com/next/usage/webhooks)
|
||||
|
||||
</details>
|
||||
|
||||
The following list shows the supported events:
|
||||
|
||||
- Opened Pull Request
|
||||
- Updated Pull Request
|
||||
- Closed Pull Request
|
||||
- Other Pull Request event
|
||||
|
||||
The `RunAutoPlanCommand` function in the
|
||||
[command_runner.go](https://github.com/runatlantis/atlantis/blob/main/server/events/command_runner.go)
|
||||
file is called for the _Open_ and _Update_ Pull Request events. When enabled on
|
||||
the project, this automatically runs the `plan` for the specific repository.
|
||||
|
||||
Whenever a Pull Request is closed, the `CleanUpPull` function in the
|
||||
[instrumented_pull_closed_executor.go](https://github.com/runatlantis/atlantis/blob/main/server/events/instrumented_pull_closed_executor.go)
|
||||
file is called. This function cleans up all the closed Pull Request files,
|
||||
locks, and other related information.
|
||||
26
runatlantis.io/contributing/glossary.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Glossary
|
||||
|
||||
The Atlantis community uses many words and phrases to work more efficiently.
|
||||
You will find the most common ones and their meaning on this page.
|
||||
|
||||
## Pull / Merge Request Event
|
||||
|
||||
The different VCSs have different names for merging changes. Atlantis uses the
|
||||
name Pull Request as the abstraction. The VCS provider implements this
|
||||
abstraction and forwards the call to the respective function.
|
||||
|
||||
## VCS
|
||||
|
||||
VCS stands for Version Control System.
|
||||
|
||||
Atlantis supports only git as a Version Control System. However, there is
|
||||
support for multiple VCS Providers. Currently, it supports the following
|
||||
providers:
|
||||
|
||||
- [Azure DevOps](https://azure.microsoft.com/en-us/products/devops)
|
||||
- [BitBucket](https://bitbucket.org/)
|
||||
- [GitHub](https://github.com/)
|
||||
- [GitLab](https://gitlab.com/)
|
||||
- [Gitea](https://gitea.com/)
|
||||
|
||||
The term VCS is used for both git and the different VCS providers.
|
||||
18
runatlantis.io/docs.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
aside: false
|
||||
---
|
||||
# Atlantis Documentation
|
||||
|
||||
These docs are for users that are ready to get Atlantis installed and start using it.
|
||||
|
||||
:::tip Looking to get started?
|
||||
If you're new here, check out the [Guide](./guide.md)
|
||||
where you can try our [Test Drive](./guide/test-drive.md) or [Run Atlantis Locally](./guide/testing-locally.md).
|
||||
:::
|
||||
|
||||
## Next Steps
|
||||
|
||||
* [Installing Atlantis](./docs/installation-guide.md) – Get Atlantis up and running
|
||||
* [Configuring Atlantis](./docs/configuring-atlantis.md) – Configure how Atlantis works for your specific use-cases
|
||||
* [Using Atlantis](./docs/using-atlantis.md) – How do you use Atlantis?
|
||||
* [How Atlantis Works](./docs/how-atlantis-works.md) – Internals of what Atlantis is doing
|
||||
@@ -1,14 +0,0 @@
|
||||
# Atlantis Documentation
|
||||
|
||||
These docs are for users that are ready to get Atlantis installed and start using it.
|
||||
|
||||
:::tip Looking to get started?
|
||||
If you're new here, check out the [Guide](/guide/)
|
||||
where you can try our [Test Drive](/guide/test-drive.html) or [Run Atlantis Locally](/guide/testing-locally.html).
|
||||
:::
|
||||
|
||||
### Next Steps
|
||||
* [Installing Atlantis](/docs/installation-guide.html) – Get Atlantis up and running
|
||||
* [Configuring Atlantis](configuring-atlantis.html) – Configure how Atlantis works for your specific use-cases
|
||||
* [Using Atlantis](using-atlantis.html) – How do you use Atlantis?
|
||||
* [How Atlantis Works](how-atlantis-works.html) – Internals of what Atlantis is doing
|
||||
@@ -1,10 +1,11 @@
|
||||
# Git Host Access Credentials
|
||||
This page describes how to create credentials for your Git host (GitHub, GitLab, Bitbucket, or Azure DevOps)
|
||||
|
||||
This page describes how to create credentials for your Git host (GitHub, GitLab, Gitea, Bitbucket, or Azure DevOps)
|
||||
|
||||
that Atlantis will use to make API calls.
|
||||
[[toc]]
|
||||
|
||||
## Create an Atlantis user (optional)
|
||||
|
||||
We recommend creating a new user named **@atlantis** (or something close) or using a dedicated CI user.
|
||||
|
||||
This isn't required (you can use an existing user or github app credentials), however all the comments that Atlantis writes
|
||||
@@ -14,19 +15,23 @@ will come from that user so it might be confusing if its coming from a personal
|
||||
<p align="center"><i>An example comment coming from the @atlantisbot user</i></p>
|
||||
|
||||
## Generating an Access Token
|
||||
|
||||
Once you've created a new user (or decided to use an existing one), you need to
|
||||
generate an access token. Read on for the instructions for your specific Git host:
|
||||
|
||||
* [GitHub](#github-user)
|
||||
* [GitHub app](#github-app)
|
||||
* [GitLab](#gitlab)
|
||||
* [Gitea](#gitea)
|
||||
* [Bitbucket Cloud (bitbucket.org)](#bitbucket-cloud-bitbucket-org)
|
||||
* [Bitbucket Server (aka Stash)](#bitbucket-server-aka-stash)
|
||||
* [Azure DevOps](#azure-devops)
|
||||
|
||||
### GitHub user
|
||||
- Create a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-fine-grained-personal-access-token)
|
||||
- Create the token with **repo** scope
|
||||
- Record the access token
|
||||
|
||||
* Create a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-fine-grained-personal-access-token)
|
||||
* Create the token with **repo** scope
|
||||
* Record the access token
|
||||
::: warning
|
||||
Your Atlantis user must also have "Write permissions" (for repos in an organization) or be a "Collaborator" (for repos in a user account) to be able to set commit statuses:
|
||||

|
||||
@@ -35,36 +40,36 @@ Your Atlantis user must also have "Write permissions" (for repos in an organizat
|
||||
### GitHub app
|
||||
|
||||
#### Create the GitHub App Using Atlantis
|
||||
|
||||
::: warning
|
||||
Available in Atlantis versions **newer** than 0.13.0.
|
||||
:::
|
||||
|
||||
* Start Atlantis with fake github username and token (`atlantis server --gh-user fake --gh-token fake --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`). If installing as an **Organization**, remember to add `--gh-org your-github-org` to this command.
|
||||
* Visit `https://$ATLANTIS_HOST/github-app/setup` and click on **Setup** to create the app on GitHub. You'll be redirected back to Atlantis
|
||||
* A link to install your app, along with its secrets, will be shown on the screen. Record your app's credentials and install your app for your user/org by following said link.
|
||||
* Create a file with the contents of the GitHub App Key, e.g. `atlantis-app-key.pem`
|
||||
* Restart Atlantis with new flags: `atlantis server --gh-app-id <your id> --gh-app-key-file atlantis-app-key.pem --gh-webhook-secret <your secret> --write-git-creds --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`.
|
||||
|
||||
- Start Atlantis with fake github username and token (`atlantis server --gh-user fake --gh-token fake --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`). If installing as an **Organization**, remember to add `--gh-org your-github-org` to this command.
|
||||
- Visit `https://$ATLANTIS_HOST/github-app/setup` and click on **Setup** to create the app on GitHub. You'll be redirected back to Atlantis
|
||||
- A link to install your app, along with its secrets, will be shown on the screen. Record your app's credentials and install your app for your user/org by following said link.
|
||||
- Create a file with the contents of the GitHub App Key, e.g. `atlantis-app-key.pem`
|
||||
- Restart Atlantis with new flags: `atlantis server --gh-app-id <your id> --gh-app-key-file atlantis-app-key.pem --gh-webhook-secret <your secret> --write-git-creds --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`.
|
||||
|
||||
NOTE: Instead of using a file for the GitHub App Key you can also pass the key value directly using `--gh-app-key`. You can also create a config file instead of using flags. See [Server Configuration](/docs/server-configuration.html#config-file).
|
||||
NOTE: Instead of using a file for the GitHub App Key you can also pass the key value directly using `--gh-app-key`. You can also create a config file instead of using flags. See [Server Configuration](server-configuration.md#config-file).
|
||||
|
||||
::: warning
|
||||
Only a single installation per GitHub App is supported at the moment.
|
||||
:::
|
||||
|
||||
::: tip NOTE
|
||||
GitHub App handles the webhook calls by itself, hence there is no need to create wehbooks separately. If webhooks were created manually, those should be removed when using GitHub App. Otherwise, there would be 2 calls to Atlantis resulting in locking errors on path/workspace.
|
||||
GitHub App handles the webhook calls by itself, hence there is no need to create webhooks separately. If webhooks were created manually, those should be removed when using GitHub App. Otherwise, there would be 2 calls to Atlantis resulting in locking errors on path/workspace.
|
||||
:::
|
||||
|
||||
#### Manually Creating the GitHub app
|
||||
|
||||
- Create the GitHub app as an Administrator
|
||||
- Ensure the app is registered / installed with the organization / user
|
||||
- See the GitHub app [documentation](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps)
|
||||
- Create a file with the contents of the GitHub App Key, e.g. `atlantis-app-key.pem`
|
||||
- Start Atlantis with the following flags: `atlantis server --gh-app-id <your id> --gh-installation-id <installation id> --gh-app-key-file atlantis-app-key.pem --gh-webhook-secret <your secret> --write-git-creds --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`.
|
||||
* Create the GitHub app as an Administrator
|
||||
* Ensure the app is registered / installed with the organization / user
|
||||
* See the GitHub app [documentation](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps)
|
||||
* Create a file with the contents of the GitHub App Key, e.g. `atlantis-app-key.pem`
|
||||
* Start Atlantis with the following flags: `atlantis server --gh-app-id <your id> --gh-installation-id <installation id> --gh-app-key-file atlantis-app-key.pem --gh-webhook-secret <your secret> --write-git-creds --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`.
|
||||
|
||||
NOTE: Instead of using a file for the GitHub App Key you can also pass the key value directly using `--gh-app-key`. You can also create a config file instead of using flags. See [Server Configuration](/docs/server-configuration.html#config-file).
|
||||
NOTE: Instead of using a file for the GitHub App Key you can also pass the key value directly using `--gh-app-key`. You can also create a config file instead of using flags. See [Server Configuration](server-configuration.md#config-file).
|
||||
|
||||
::: tip NOTE
|
||||
Manually installing the GitHub app means that the credentials can be shared by many Atlantis installations. This has the benefit of centralizing repository access for shared modules / code.
|
||||
@@ -90,49 +95,67 @@ GitHub App needs these permissions. These are automatically set when a GitHub ap
|
||||
Since v0.19.7, a new permission for `Administration` has been added. If you have already created a GitHub app, updating Atlantis to v0.19.7 will not automatically add this permission, so you will need to set it manually.
|
||||
|
||||
Since v0.22.3, a new permission for `Members` has been added, which is required for features that apply permissions to an organizations team members rather than individual users. Like the `Administration` permission above, updating Atlantis will not automatically add this permission, so if you wish to use features that rely on checking team membership you will need to add this manually.
|
||||
|
||||
A new permission for `Actions` has been added, which is required for checking if a pull request is mergbeably bypassing the apply check. Updating Atlantis will not automatically add this permission, so you will need to add this manually.
|
||||
:::
|
||||
|
||||
| Type | Access |
|
||||
| --------------- | ------------------- |
|
||||
| Administration | Read-only |
|
||||
| Checks | Read and write |
|
||||
| Commit statuses | Read and write |
|
||||
| Contents | Read and write |
|
||||
| Issues | Read and write |
|
||||
| Metadata | Read-only (default) |
|
||||
| Pull requests | Read and write |
|
||||
| Webhooks | Read and write |
|
||||
| Members | Read-only |
|
||||
| Type | Access |
|
||||
| --------------- | ------------------- |
|
||||
| Administration | Read-only |
|
||||
| Checks | Read and write |
|
||||
| Commit statuses | Read and write |
|
||||
| Contents | Read and write |
|
||||
| Issues | Read and write |
|
||||
| Metadata | Read-only (default) |
|
||||
| Pull requests | Read and write |
|
||||
| Webhooks | Read and write |
|
||||
| Members | Read-only |
|
||||
| Actions | Read-only |
|
||||
|
||||
### GitLab
|
||||
- Follow: [https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html#create-a-personal-access-token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html#create-a-personal-access-token)
|
||||
- Create a token with **api** scope
|
||||
- Record the access token
|
||||
|
||||
* Follow: [GitLab: Create a personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html#create-a-personal-access-token)
|
||||
* Create a token with **api** scope
|
||||
* Record the access token
|
||||
|
||||
### Gitea
|
||||
|
||||
* Go to "Profile and Settings" > "Settings" in Gitea (top-right)
|
||||
* Go to "Applications" under "User Settings" in Gitea
|
||||
* Create a token under the "Manage Access Tokens" with the following permissions:
|
||||
* issue: Read and Write
|
||||
* repository: Read and Write
|
||||
* user: Read
|
||||
* Record the access token
|
||||
|
||||
### Bitbucket Cloud (bitbucket.org)
|
||||
- Create an App Password by following [https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/](https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/)
|
||||
- Label the password "atlantis"
|
||||
- Select **Pull requests**: **Read** and **Write** so that Atlantis can read your pull requests and write comments to them
|
||||
- Record the access token
|
||||
|
||||
* Create an App Password by following [BitBucket Cloud: Create an app password](https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/)
|
||||
* Label the password "atlantis"
|
||||
* Select **Pull requests**: **Read** and **Write** so that Atlantis can read your pull requests and write comments to them
|
||||
* Record the access token
|
||||
|
||||
### Bitbucket Server (aka Stash)
|
||||
- Click on your avatar in the top right and select **Manage account**
|
||||
- Click **Personal access tokens** in the sidebar
|
||||
- Click **Create a token**
|
||||
- Name the token **atlantis**
|
||||
- Give the token **Read** Project permissions and **Write** Pull request permissions
|
||||
- Click **Create** and record the access token
|
||||
|
||||
* Click on your avatar in the top right and select **Manage account**
|
||||
* Click **Personal access tokens** in the sidebar
|
||||
* Click **Create a token**
|
||||
* Name the token **atlantis**
|
||||
* Give the token **Read** Project permissions and **Write** Pull request permissions
|
||||
* Click **Create** and record the access token
|
||||
|
||||
NOTE: Atlantis will send the token as a [Bearer Auth to the Bitbucket API](https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html#HTTPaccesstokens-UsingHTTPaccesstokens) instead of using Basic Auth.
|
||||
|
||||
### Azure DevOps
|
||||
- Create a Personal access token by following [https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops)
|
||||
- Label the password "atlantis"
|
||||
- The minimum scopes required for this token are:
|
||||
- Code (Read & Write)
|
||||
- Code (Status)
|
||||
- Member Entitlement Management (Read)
|
||||
- Record the access token
|
||||
|
||||
* Create a Personal access token by following [Azure DevOps: Use personal access tokens to authenticate](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops)
|
||||
* Label the password "atlantis"
|
||||
* The minimum scopes required for this token are:
|
||||
* Code (Read & Write)
|
||||
* Code (Status)
|
||||
* Member Entitlement Management (Read)
|
||||
* Record the access token
|
||||
|
||||
## Next Steps
|
||||
Once you've got your user and access token, you're ready to create a webhook secret. See [Creating a Webhook Secret](webhook-secrets.html).
|
||||
|
||||
Once you've got your user and access token, you're ready to create a webhook secret. See [Creating a Webhook Secret](webhook-secrets.md).
|
||||
|
||||
@@ -9,7 +9,7 @@ To enable the API endpoints, `api-secret` should be configured.
|
||||
|
||||
:::tip Prerequisites
|
||||
|
||||
* Set `api-secret` as part of the [Server Configuration](server-configuration.html#api-secret)
|
||||
* Set `api-secret` as part of the [Server Configuration](server-configuration.md#api-secret)
|
||||
* Pass `X-Atlantis-Token` with the same secret in the request header
|
||||
:::
|
||||
|
||||
@@ -17,22 +17,22 @@ To enable the API endpoints, `api-secret` should be configured.
|
||||
|
||||
#### Description
|
||||
|
||||
Execute [atlantis plan](using-atlantis.html#atlantis-plan) on the specified repository.
|
||||
Execute [atlantis plan](using-atlantis.md#atlantis-plan) on the specified repository.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|------------|-------------------------------------|----------|------------------------------------------|
|
||||
| Repository | string | Yes | Name of the Terraform repository |
|
||||
| Ref | string | Yes | Git reference, like a branch name |
|
||||
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
|
||||
| Paths | [ [Path](api-endpoints.html#path) ] | Yes | Paths to the projects to run the plan |
|
||||
| PR | int | No | Pull Request number |
|
||||
| Name | Type | Required | Description |
|
||||
|------------|---------|----------|------------------------------------------|
|
||||
| Repository | string | Yes | Name of the Terraform repository |
|
||||
| Ref | string | Yes | Git reference, like a branch name |
|
||||
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
|
||||
| Paths | Path | Yes | Paths to the projects to run the plan |
|
||||
| PR | int | No | Pull Request number |
|
||||
|
||||
##### Path
|
||||
#### Path
|
||||
|
||||
Similar to the [Options](using-atlantis.html#options) of `atlantis plan`. Path specifies which directory/workspace
|
||||
within the repository to run the plan.
|
||||
Similar to the [Options](using-atlantis.md#options) of `atlantis plan`. Path specifies which directory/workspace
|
||||
within the repository to run the plan.
|
||||
At least one of `Directory` or `Workspace` should be specified.
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
@@ -92,22 +92,22 @@ curl --request POST 'https://<ATLANTIS_HOST_NAME>/api/plan' \
|
||||
|
||||
#### Description
|
||||
|
||||
Execute [atlantis apply](using-atlantis.html#atlantis-apply) on the specified repository.
|
||||
Execute [atlantis apply](using-atlantis.md#atlantis-apply) on the specified repository.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|------------|---------------------------------------|----------|------------------------------------------|
|
||||
| Repository | string | Yes | Name of the Terraform repository |
|
||||
| Ref | string | Yes | Git reference, like a branch name |
|
||||
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
|
||||
| Paths | [ [Path](api-endpoints.html#path-1) ] | Yes | Paths to the projects to run the apply |
|
||||
| PR | int | No | Pull Request number |
|
||||
| Name | Type | Required | Description |
|
||||
|------------|--------|----------|------------------------------------------|
|
||||
| Repository | string | Yes | Name of the Terraform repository |
|
||||
| Ref | string | Yes | Git reference, like a branch name |
|
||||
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
|
||||
| Paths | Path | Yes | Paths to the projects to run the apply |
|
||||
| PR | int | No | Pull Request number |
|
||||
|
||||
##### Path
|
||||
#### Path
|
||||
|
||||
Similar to the [Options](using-atlantis.html#options-1) of `atlantis apply`. Path specifies which directory/workspace
|
||||
within the repository to run the apply.
|
||||
Similar to the [Options](using-atlantis.md#options-1) of `atlantis apply`. Path specifies which directory/workspace
|
||||
within the repository to run the apply.
|
||||
At least one of `Directory` or `Workspace` should be specified.
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Apply Requirements
|
||||
|
||||
:::warning REDIRECT
|
||||
This page is moved to [Command Requirements](/docs/command-requirements.html).
|
||||
This page is moved to [Command Requirements](command-requirements.md).
|
||||
:::
|
||||
|
||||
@@ -1,47 +1,64 @@
|
||||
# Automerging
|
||||
|
||||
Atlantis can be configured to automatically merge a pull request after all plans have
|
||||
been successfully applied.
|
||||
|
||||
|
||||

|
||||
|
||||
## How To Enable
|
||||
|
||||
Automerging can be enabled either by:
|
||||
|
||||
1. Passing the `--automerge` flag to `atlantis server`. This sets the parameter globally; however, explicit declaration in the repo config will be respected and take priority.
|
||||
1. Setting `automerge: true` in the repo's `atlantis.yaml` file:
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
automerge: true
|
||||
projects:
|
||||
- dir: .
|
||||
```
|
||||
|
||||
:::tip NOTE
|
||||
If a repo has an `atlantis.yaml` file, then each project in the repo needs
|
||||
to be configured under the `projects` key.
|
||||
:::
|
||||
|
||||
## How to Disable
|
||||
|
||||
If automerge is enabled, you can disable it for a single `atlantis apply`
|
||||
command with the `--auto-merge-disabled` option.
|
||||
|
||||
## All Plans Must Succeed
|
||||
## Requirements
|
||||
|
||||
### All Plans Must Succeed
|
||||
|
||||
When automerge is enabled, **all plans** in a pull request **must succeed** before
|
||||
**any** plans can be applied.
|
||||
|
||||
For example, imagine this scenario:
|
||||
|
||||
1. I open a pull request that makes changes to two Terraform projects, in `dir1/`
|
||||
and `dir2/`.
|
||||
1. The plan for `dir2/` fails because my Terraform syntax is wrong.
|
||||
|
||||
In this scenario, I can't run
|
||||
```
|
||||
|
||||
```shell
|
||||
atlantis apply -d dir1
|
||||
```
|
||||
|
||||
Even though that plan succeeded, because **all** plans must succeed for **any** plans
|
||||
to be saved.
|
||||
|
||||
Once I fix the issue in `dir2`, I can push a new commit which will trigger an
|
||||
autoplan. Then I will be able to apply both plans.
|
||||
|
||||
### All Plans must be applied
|
||||
|
||||
If multiple projects/dirs/workspaces are configured to be planned automatically,
|
||||
then they should all be applied before Atlantis automatically merges the PR.
|
||||
|
||||
## Permissions
|
||||
|
||||
The Atlantis VCS user must have the ability to merge pull requests.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Autoplanning
|
||||
|
||||
On any **new** pull request or **new commit** to an existing pull request, Atlantis will attempt to
|
||||
run `terraform plan` in the directories it thinks hold modified Terraform projects.
|
||||
|
||||
The algorithm it uses is as follows:
|
||||
|
||||
1. Get list of all modified files in pull request
|
||||
1. Filter to those containing `.tf`
|
||||
1. Get the directories that those files are in
|
||||
@@ -11,8 +13,10 @@ The algorithm it uses is as follows:
|
||||
contains a `main.tf` run plan in that directory, otherwise ignore the change (see below for exceptions).
|
||||
|
||||
## Example
|
||||
|
||||
Given the directory structure:
|
||||
```
|
||||
|
||||
```plain
|
||||
.
|
||||
├── modules
|
||||
│ └── module1
|
||||
@@ -26,21 +30,25 @@ Given the directory structure:
|
||||
|
||||
* If `project1/main.tf` were modified, we would run `plan` in `project1`
|
||||
* If `modules/module1/main.tf` were modified, we would not automatically run `plan` because we couldn't determine the location of the terraform project
|
||||
* You could use an [atlantis.yaml](repo-level-atlantis-yaml.html#configuring-planning) file to specify which projects to plan when this module changed
|
||||
* You could enable [module autoplanning](server-configuration.html#autoplan-modules) which indexes projects to their local module dependencies.
|
||||
* Or you could manually plan with `atlantis plan -d <dir>`
|
||||
* You could use an [atlantis.yaml](repo-level-atlantis-yaml.md#configuring-planning) file to specify which projects to plan when this module changed
|
||||
* You could enable [module autoplanning](server-configuration.md#autoplan-modules) which indexes projects to their local module dependencies.
|
||||
* Or you could manually plan with `atlantis plan -d <dir>`
|
||||
* If `project1/modules/module1/main.tf` were modified, we would look one level above `project1/modules`
|
||||
into `project1/`, see that there was a `main.tf` file and so run plan in `project1/`
|
||||
|
||||
## Bitbucket-Specific Notes
|
||||
|
||||
Bitbucket does not have a webhook that triggers only upon a new PR or commit. To fix this we cache the last commit to see if it has changed. If the cache is emptied, Atlantis will think your commit is new and you may see extra plans.
|
||||
This scenario can happen if:
|
||||
|
||||
* Atlantis restarts
|
||||
* You are running multiple Atlantis instances behind a load balancer
|
||||
|
||||
## Customizing
|
||||
|
||||
If you would like to customize how Atlantis determines which directory to run in
|
||||
or disable it all together you need to create an `atlantis.yaml` file.
|
||||
See
|
||||
* [Disabling Autoplanning](repo-level-atlantis-yaml.html#disabling-autoplanning)
|
||||
* [Configuring Planning](repo-level-atlantis-yaml.html#configuring-planning)
|
||||
|
||||
* [Disabling Autoplanning](repo-level-atlantis-yaml.md#disabling-autoplanning)
|
||||
* [Configuring Planning](repo-level-atlantis-yaml.md#configuring-planning)
|
||||
|
||||
@@ -7,6 +7,7 @@ variable that get passed to the `atlantis server` command.
|
||||
Atlantis supports `branch` and `merge` strategies.
|
||||
|
||||
## Branch
|
||||
|
||||
If set to `branch` (the default), Atlantis will check out the source branch
|
||||
of the pull request.
|
||||
|
||||
@@ -17,6 +18,7 @@ If the pull request was asking to merge `branch` into `main`,
|
||||
Atlantis would check out `branch` at commit `C3`.
|
||||
|
||||
## Merge
|
||||
|
||||
The problem with the `branch` strategy, is that if users push branches that are
|
||||
out of date with `main`, then their `terraform plan` could be deleting
|
||||
some resources that were configured in the main branch.
|
||||
@@ -49,9 +51,9 @@ commit is pushed to `main` **after** Atlantis runs `plan`, nothing will happen.
|
||||
|
||||
To optimize cloning time, Atlantis can perform a shallow clone by specifying the `--checkout-depth` flag. The cloning is performed in a following manner:
|
||||
|
||||
- Shallow clone of the default branch is performed with depth of `--checkout-depth` value of zero (full clone).
|
||||
- `branch` is retrieved, including the same amount of commits.
|
||||
- Merge base of the default branch and `branch` is checked for existence in the shallow clone.
|
||||
- If the merge base is not present, it means that either of the branches are ahead of the merge base by more than `--checkout-depth` commits. In this case full repo history is fetched.
|
||||
* Shallow clone of the default branch is performed with depth of `--checkout-depth` value of zero (full clone).
|
||||
* `branch` is retrieved, including the same amount of commits.
|
||||
* Merge base of the default branch and `branch` is checked for existence in the shallow clone.
|
||||
* If the merge base is not present, it means that either of the branches are ahead of the merge base by more than `--checkout-depth` commits. In this case full repo history is fetched.
|
||||
|
||||
If the commit history often diverges by more than the default checkout depth then the `--checkout-depth` flag should be tuned to avoid full fetches.
|
||||
If the commit history often diverges by more than the default checkout depth then the `--checkout-depth` flag should be tuned to avoid full fetches.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Command Requirements
|
||||
[[toc]]
|
||||
|
||||
## Intro
|
||||
|
||||
Atlantis requires certain conditions be satisfied **before** `atlantis apply` and `atlantis import`
|
||||
commands can be run:
|
||||
|
||||
@@ -10,32 +10,41 @@ commands can be run:
|
||||
* [UnDiverged](#undiverged) - requires pull requests to be ahead of the base branch
|
||||
|
||||
## What Happens If The Requirement Is Not Met?
|
||||
|
||||
If the requirement is not met, users will see an error if they try to run `atlantis apply`:
|
||||

|
||||
|
||||
## Supported Requirements
|
||||
|
||||
### Approved
|
||||
|
||||
The `approved` requirement will prevent applies unless the pull request is approved
|
||||
by at least one person other than the author.
|
||||
|
||||
#### Usage
|
||||
|
||||
The `approved` requirement by:
|
||||
1. Passing the `--require-approval` flag to `atlantis server` or
|
||||
|
||||
1. Creating a `repos.yaml` file with the `apply_requirements` key:
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
apply_requirements: [approved]
|
||||
```
|
||||
|
||||
1. Or by allowing an `atlantis.yaml` file to specify the `apply_requirements` key in the `repos.yaml` config:
|
||||
#### repos.yaml
|
||||
|
||||
**repos.yaml**
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
allowed_overrides: [apply_requirements]
|
||||
```
|
||||
|
||||
#### atlantis.yaml
|
||||
**atlantis.yaml**
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -44,7 +53,9 @@ The `approved` requirement by:
|
||||
```
|
||||
|
||||
#### Meaning
|
||||
|
||||
Each VCS provider has different rules around who can approve:
|
||||
|
||||
* **GitHub** – **Any user with read permissions** to the repo can approve a pull request
|
||||
* **GitLab** – The user who can approve can be set in the [repo settings](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
* **Bitbucket Cloud (bitbucket.org)** – A user can approve their own pull request but
|
||||
@@ -58,12 +69,15 @@ To require **certain people** to approve the pull request, look at the
|
||||
:::
|
||||
|
||||
### Mergeable
|
||||
|
||||
The `mergeable` requirement will prevent applies unless a pull request is able to be merged.
|
||||
|
||||
#### Usage
|
||||
|
||||
Set the `mergeable` requirement by:
|
||||
1. Passing the `--require-mergeable` flag to `atlantis server` or
|
||||
|
||||
1. Creating a `repos.yaml` file with the `apply_requirements` key:
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
@@ -71,14 +85,17 @@ Set the `mergeable` requirement by:
|
||||
```
|
||||
|
||||
1. Or by allowing an `atlantis.yaml` file to specify `plan_requirements`, `apply_requirements` and `import_requirements` keys in the `repos.yaml` config:
|
||||
#### repos.yaml
|
||||
|
||||
**repos.yaml**
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
|
||||
```
|
||||
|
||||
#### atlantis.yaml
|
||||
**atlantis.yaml**
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -89,25 +106,28 @@ Set the `mergeable` requirement by:
|
||||
```
|
||||
|
||||
#### Meaning
|
||||
|
||||
Each VCS provider has a different concept of "mergeability":
|
||||
|
||||
::: warning
|
||||
Some VCS providers have a feature for branch protection to control "mergeability". To use it,
|
||||
limit the base branch so to not bypass the branch protection.
|
||||
See also the `branch` keyword in [Server Side Repo Config](server-side-repo-config.html#reference) for more details.
|
||||
See also the `branch` keyword in [Server Side Repo Config](server-side-repo-config.md#reference) for more details.
|
||||
:::
|
||||
|
||||
#### GitHub
|
||||
|
||||
In GitHub, if you're not using [Protected Branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches) then
|
||||
all pull requests are mergeable unless there is a conflict.
|
||||
|
||||
If you set up Protected Branches then you can enforce:
|
||||
|
||||
* Requiring certain status checks to be passing
|
||||
* Requiring certain people to have reviewed and approved the pull request
|
||||
* Requiring `CODEOWNERS` to have reviewed and approved the pull request
|
||||
* Requiring that the branch is up to date with `main`
|
||||
* Requiring that the branch is up-to-date with `main`
|
||||
|
||||
See [https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches)
|
||||
See [GitHub: About protected branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches)
|
||||
for more details.
|
||||
|
||||
::: warning
|
||||
@@ -121,6 +141,7 @@ If you set `atlantis/apply` to the mergeable requirement, use the `--gh-allow-me
|
||||
:::
|
||||
|
||||
#### GitLab
|
||||
|
||||
For GitLab, a merge request will be merged if there are no conflicts, no unresolved discussions if it is a project requirement and if all necessary approvers have approved the pull request.
|
||||
|
||||
For pipelines, if the project requires that pipelines must succeed, all builds except the apply command status will be checked.
|
||||
@@ -128,6 +149,7 @@ For pipelines, if the project requires that pipelines must succeed, all builds e
|
||||
For Jobs with allow_failure setting set to true, will be ignored. If the pipeline has been skipped and the project allows merging, it will be marked as mergeable.
|
||||
|
||||
#### Bitbucket.org (Bitbucket Cloud) and Bitbucket Server (Stash)
|
||||
|
||||
For Bitbucket, we just check if there is a conflict that is preventing a
|
||||
merge. We don't check anything else because Bitbucket's API doesn't support it.
|
||||
|
||||
@@ -135,9 +157,11 @@ If you need a specific check, please
|
||||
[open an issue](https://github.com/runatlantis/atlantis/issues/new).
|
||||
|
||||
#### Azure DevOps
|
||||
|
||||
In Azure DevOps, all pull requests are mergeable unless there is a conflict. You can set a pull request to "Complete" right away, or set "Auto-Complete", which will merge after all branch policies are met. See [Review code with pull requests](https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-requests?view=azure-devops).
|
||||
|
||||
[Branch policies](https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops) can:
|
||||
|
||||
* Require a minimum number of reviewers
|
||||
* Allow users to approve their own changes
|
||||
* Allow completion even if some reviewers vote "Waiting" or "Reject"
|
||||
@@ -149,12 +173,16 @@ At this time, the Azure DevOps client only supports merging using the default 'n
|
||||
:::
|
||||
|
||||
### UnDiverged
|
||||
|
||||
Prevent applies if there are any changes on the base branch since the most recent plan.
|
||||
Applies to `merge` checkout strategy only.
|
||||
Applies to `merge` checkout strategy only which you need to set via `--checkout-strategy` flag.
|
||||
|
||||
#### Usage
|
||||
|
||||
You can set the `undiverged` requirement by:
|
||||
|
||||
1. Creating a `repos.yaml` file with `plan_requirements`, `apply_requirements` and `import_requirements` keys:
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
@@ -162,15 +190,19 @@ You can set the `undiverged` requirement by:
|
||||
apply_requirements: [undiverged]
|
||||
import_requirements: [undiverged]
|
||||
```
|
||||
|
||||
1. Or by allowing an `atlantis.yaml` file to specify the `plan_requirements`, `apply_requirements` and `import_requirements` keys in your `repos.yaml` config:
|
||||
#### repos.yaml
|
||||
|
||||
**repos.yaml**
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
|
||||
```
|
||||
|
||||
#### atlantis.yaml
|
||||
**atlantis.yaml**
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -179,7 +211,9 @@ You can set the `undiverged` requirement by:
|
||||
apply_requirements: [undiverged]
|
||||
import_requirements: [undiverged]
|
||||
```
|
||||
|
||||
#### Meaning
|
||||
|
||||
The `merge` checkout strategy creates a temporary merge commit and runs the `plan` on the Atlantis local version of the PR
|
||||
source and destination branch. The local destination branch can become out of date since changes to the destination branch are not fetched
|
||||
if there are no changes to the source branch. `undiverged` enforces that Atlantis local version of main is up to date
|
||||
@@ -187,18 +221,21 @@ with remote so that the state of the source during the `apply` is identical to t
|
||||
time.
|
||||
|
||||
## Setting Command Requirements
|
||||
|
||||
As mentioned above, you can set command requirements via flags, in `repos.yaml`, or in `atlantis.yaml` if `repos.yaml`
|
||||
allows the override.
|
||||
|
||||
### Flags Override
|
||||
|
||||
Flags **override** any `repos.yaml` or `atlantis.yaml` settings so they are equivalent to always
|
||||
having that apply requirement set.
|
||||
|
||||
### Project-Specific Settings
|
||||
|
||||
If you only want some projects/repos to have apply requirements, then you must
|
||||
1. Not set the `--require-approval` or `--require-mergeable` flags, since those
|
||||
will override any `repos.yaml` or `atlantis.yaml` settings
|
||||
|
||||
1. Specifying which repos have which requirements via the `repos.yaml` file.
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
@@ -225,7 +262,9 @@ If you only want some projects/repos to have apply requirements, then you must
|
||||
config.
|
||||
|
||||
For example if I have two directories, `staging` and `production`, I might use:
|
||||
#### repos.yaml
|
||||
|
||||
**repos.yaml:**
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- id: /.*/
|
||||
@@ -233,7 +272,8 @@ If you only want some projects/repos to have apply requirements, then you must
|
||||
# Allow any repo to specify apply_requirements in atlantis.yaml
|
||||
```
|
||||
|
||||
#### atlantis.yaml
|
||||
**atlantis.yaml:**
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -252,14 +292,17 @@ If you only want some projects/repos to have apply requirements, then you must
|
||||
```
|
||||
|
||||
### Multiple Requirements
|
||||
|
||||
You can set any or all of `approved`, `mergeable`, and `undiverged` requirements.
|
||||
|
||||
## Who Can Apply?
|
||||
|
||||
Once the apply requirement is satisfied, **anyone** that can comment on the pull
|
||||
request can run the actual `atlantis apply` command.
|
||||
|
||||
## Next Steps
|
||||
* For more information on GitHub pull request reviews and approvals see: [https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews)
|
||||
* For more information on GitLab merge request reviews and approvals (only supported on GitLab Enterprise) see: [https://docs.gitlab.com/ee/user/project/merge_requests/approvals/](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/).
|
||||
* For more information on Bitbucket pull request reviews and approvals see: [https://confluence.atlassian.com/bitbucket/pull-requests-and-code-review-223220593.html](https://confluence.atlassian.com/bitbucket/pull-requests-and-code-review-223220593.html)
|
||||
* For more information on Azure DevOps pull request reviews and approvals see: [https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-requests?view=azure-devops&tabs=browser](https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-requests?view=azure-devops&tabs=browser)
|
||||
|
||||
* For more information on GitHub pull request reviews and approvals see: [GitHub: About pull request reviews](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews)
|
||||
* For more information on GitLab merge request reviews and approvals (only supported on GitLab Enterprise) see: [GitLab: Merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/).
|
||||
* For more information on Bitbucket pull request reviews and approvals see: [BitBucket: Use pull requests for code review](https://confluence.atlassian.com/bitbucket/pull-requests-and-code-review-223220593.html)
|
||||
* For more information on Azure DevOps pull request reviews and approvals see: [Azure DevOps: Create pull requests](https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-requests?view=azure-devops&tabs=browser)
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
# Configuring Atlantis
|
||||
|
||||
There are three methods for configuring Atlantis:
|
||||
|
||||
1. Passing flags to the `atlantis server` command
|
||||
1. Creating a server-side repo config file and using the `--repo-config` flag
|
||||
1. Placing an `atlantis.yaml` file at the root of your Terraform repositories
|
||||
|
||||
## Flags
|
||||
|
||||
Flags to `atlantis server` are used to configure the global operation of
|
||||
Atlantis, for example setting credentials for your Git Host
|
||||
or configuring SSL certs.
|
||||
|
||||
See [Server Configuration](server-configuration.html) for more details.
|
||||
See [Server Configuration](server-configuration.md) for more details.
|
||||
|
||||
## Server-Side Repo Config
|
||||
|
||||
A Server-Side Repo Config file is used to control per-repo behaviour
|
||||
and what users can do in repo-level `atlantis.yaml` files.
|
||||
|
||||
See [Server-Side Repo Config](server-side-repo-config.html) for more details.
|
||||
See [Server-Side Repo Config](server-side-repo-config.md) for more details.
|
||||
|
||||
## Repo-Level `atlantis.yaml` Files
|
||||
|
||||
`atlantis.yaml` files placed at the root of your Terraform repos can be used to
|
||||
change the default Atlantis behaviour for each repo.
|
||||
|
||||
See [Repo-Level atlantis.yaml Files](repo-level-atlantis-yaml.html) for more details.
|
||||
See [Repo-Level atlantis.yaml Files](repo-level-atlantis-yaml.md) for more details.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
# Configuring Webhooks
|
||||
|
||||
Atlantis needs to receive Webhooks from your Git host so that it can respond to pull request events.
|
||||
|
||||
:::tip Prerequisites
|
||||
|
||||
* You have created an [access credential](access-credentials.md)
|
||||
* You have created a [webhook secret](webhook-secrets.md)
|
||||
* You have [deployed](deployment.md) Atlantis and have a url for it
|
||||
:::
|
||||
|
||||
See the instructions for your specific provider below.
|
||||
[[toc]]
|
||||
|
||||
## GitHub/GitHub Enterprise
|
||||
|
||||
You can install your webhook at the [organization](https://docs.github.com/en/get-started/learning-about-github/types-of-github-accounts) level, or for each individual repository.
|
||||
|
||||
::: tip NOTE
|
||||
@@ -22,101 +24,130 @@ When authenticating as a GitHub App, Webhooks are automatically created and need
|
||||
|
||||
If you're installing on the organization, navigate to your organization's page and click **Settings**.
|
||||
If installing on a single repository, navigate to the repository home page and click **Settings**.
|
||||
- Select **Webhooks** or **Hooks** in the sidebar
|
||||
- Click **Add webhook**
|
||||
- set **Payload URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
- double-check you added `/events` to the end of your URL.
|
||||
- set **Content type** to `application/json`
|
||||
- set **Secret** to the Webhook Secret you generated previously
|
||||
- **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
- select **Let me select individual events**
|
||||
- check the boxes
|
||||
- **Pull request reviews**
|
||||
- **Pushes**
|
||||
- **Issue comments**
|
||||
- **Pull requests**
|
||||
- leave **Active** checked
|
||||
- click **Add webhook**
|
||||
- See [Next Steps](#next-steps)
|
||||
|
||||
* Select **Webhooks** or **Hooks** in the sidebar
|
||||
* Click **Add webhook**
|
||||
* set **Payload URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
* double-check you added `/events` to the end of your URL.
|
||||
* set **Content type** to `application/json`
|
||||
* set **Secret** to the Webhook Secret you generated previously
|
||||
* **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
* select **Let me select individual events**
|
||||
* check the boxes
|
||||
* **Pull request reviews**
|
||||
* **Pushes**
|
||||
* **Issue comments**
|
||||
* **Pull requests**
|
||||
* leave **Active** checked
|
||||
* click **Add webhook**
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## GitLab
|
||||
|
||||
If you're using GitLab, navigate to your project's home page in GitLab
|
||||
- Click **Settings > Webhooks** in the sidebar
|
||||
- set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
- double-check you added `/events` to the end of your URL.
|
||||
- set **Secret Token** to the Webhook Secret you generated previously
|
||||
- **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
- check the boxes
|
||||
- **Push events**
|
||||
- **Comments**
|
||||
- **Merge Request events**
|
||||
- leave **Enable SSL verification** checked
|
||||
- click **Add webhook**
|
||||
- See [Next Steps](#next-steps)
|
||||
|
||||
* Click **Settings > Webhooks** in the sidebar
|
||||
* set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
* double-check you added `/events` to the end of your URL.
|
||||
* set **Secret Token** to the Webhook Secret you generated previously
|
||||
* **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
* check the boxes
|
||||
* **Push events**
|
||||
* **Comments**
|
||||
* **Merge Request events**
|
||||
* leave **Enable SSL verification** checked
|
||||
* click **Add webhook**
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## Gitea
|
||||
|
||||
If you're using Gitea, navigate to your project's home page in Gitea
|
||||
|
||||
* Click **Settings > Webhooks** in the top- and then sidebar
|
||||
* Click **Add webhook > Gitea** (Gitea webhooks are service specific, but this works)
|
||||
* set **Target URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
* double-check you added `/events` to the end of your URL.
|
||||
* set **Secret** to the Webhook Secret you generated previously
|
||||
* **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
* Select **Custom Events...**
|
||||
* Check the boxes
|
||||
* **Repository events > Push**
|
||||
* **Issue events > Issue Comment**
|
||||
* **Pull Request events > Pull Request**
|
||||
* **Pull Request events > Pull Request Comment**
|
||||
* **Pull Request events > Pull Request Reviewed**
|
||||
* **Pull Request events > Pull Request Synchronized**
|
||||
* Leave **Active** checked
|
||||
* Click **Add Webhook**
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## Bitbucket Cloud (bitbucket.org)
|
||||
- Go to your repo's home page
|
||||
- Click **Settings** in the sidebar
|
||||
- Click **Webhooks** under the **WORKFLOW** section
|
||||
- Click **Add webhook**
|
||||
- Enter "Atlantis" for **Title**
|
||||
- set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
- double-check you added `/events` to the end of your URL.
|
||||
- Keep **Status** as Active
|
||||
- Don't check **Skip certificate validation** because NGROK has a valid cert.
|
||||
- Select **Choose from a full list of triggers**
|
||||
- Under **Repository** **un**check everything
|
||||
- Under **Issues** leave everything **un**checked
|
||||
- Under **Pull Request**, select: Created, Updated, Merged, Declined and Comment created
|
||||
- Click **Save**
|
||||
|
||||
* Go to your repo's home page
|
||||
* Click **Settings** in the sidebar
|
||||
* Click **Webhooks** under the **WORKFLOW** section
|
||||
* Click **Add webhook**
|
||||
* Enter "Atlantis" for **Title**
|
||||
* set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
* double-check you added `/events` to the end of your URL.
|
||||
* Keep **Status** as Active
|
||||
* Don't check **Skip certificate validation** because NGROK has a valid cert.
|
||||
* Select **Choose from a full list of triggers**
|
||||
* Under **Repository** **un**check everything
|
||||
* Under **Issues** leave everything **un**checked
|
||||
* Under **Pull Request**, select: Created, Updated, Merged, Declined and Comment created
|
||||
* Click **Save**
|
||||
<img src="../guide/images/bitbucket-webhook.png" alt="Bitbucket Webhook" style="max-height: 500px">
|
||||
- See [Next Steps](#next-steps)
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## Bitbucket Server (aka Stash)
|
||||
- Go to your repo's home page
|
||||
- Click **Settings** in the sidebar
|
||||
- Click **Webhooks** under the **WORKFLOW** section
|
||||
- Click **Create webhook**
|
||||
- Enter "Atlantis" for **Name**
|
||||
- set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
- Double-check you added `/events` to the end of your URL.
|
||||
- Set **Secret** to the Webhook Secret you generated previously
|
||||
- **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
- Under **Pull Request**, select: Opened, Source branch updated, Merged, Declined, Deleted and Comment added
|
||||
- Click **Save**<img src="../guide/images/bitbucket-server-webhook.png" alt="Bitbucket Webhook" style="max-height: 600px;">
|
||||
- See [Next Steps](#next-steps)
|
||||
|
||||
* Go to your repo's home page
|
||||
* Click **Settings** in the sidebar
|
||||
* Click **Webhooks** under the **WORKFLOW** section
|
||||
* Click **Create webhook**
|
||||
* Enter "Atlantis" for **Name**
|
||||
* set **URL** to `http://$URL/events` (or `https://$URL/events` if you're using SSL) where `$URL` is where Atlantis is hosted. **Be sure to add `/events`**
|
||||
* Double-check you added `/events` to the end of your URL.
|
||||
* Set **Secret** to the Webhook Secret you generated previously
|
||||
* **NOTE** If you're adding a webhook to multiple repositories, each repository will need to use the **same** secret.
|
||||
* Under **Pull Request**, select: Opened, Source branch updated, Merged, Declined, Deleted and Comment added
|
||||
* Click **Save**<img src="../guide/images/bitbucket-server-webhook.png" alt="Bitbucket Webhook" style="max-height: 600px;">
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## Azure DevOps
|
||||
|
||||
Webhooks are installed at the [team project](https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops) level, but may be restricted to only fire based on events pertaining to [specific repos](https://docs.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops) within the team project.
|
||||
|
||||
- Navigate anywhere within a team project, ie: `https://dev.azure.com/orgName/projectName/_git/repoName`
|
||||
- Select **Project settings** in the lower-left corner
|
||||
- Select **Service hooks**
|
||||
- If you see the message "You do not have sufficient permissions to view or configure subscriptions." you need to ensure your user is a member of either the organization's "Project Collection Administrators" group or the project's "Project Administrators" group.
|
||||
- To add your user to the Project Collection Build Administrators group, navigate to the organization level, click **Organization Settings** and then click **Permissions**. You should be at `https://dev.azure.com/<organization>/_settings/groups`. Now click on the **\<organization\>/Project Collection Administrators** group and add your user as a member.
|
||||
- To add your user to the Project Administrators group, navigate to the project level, click **Project Settings** and then click **Permissions**. You should be at `https://dev.azure.com/<organization>/<project>/_settings/permissions`. Now click on the **\<project\>/Project Administrators** group and add your user as a member.
|
||||
- Click **Create subscription** or the green plus icon to add a new webhook
|
||||
- Scroll to the bottom of the list and select **Web Hooks**
|
||||
- Click **Next**
|
||||
- Under "Trigger on this type of event", select **Pull request created**
|
||||
- Optionally, select a repository under **Filters** to restrict the scope of this webhook subscription to a specific repository
|
||||
- Click **Next**
|
||||
- Set **URL** to `http://$URL/events` where `$URL` is where Atlantis is hosted. Note that SSL, or `https://$URL/events`, is required if you set a Basic username and password for the webhook). **Be sure to add `/events`**
|
||||
- It is strongly recommended to set a Basic Username and Password for all webhooks
|
||||
- Leave all three drop-down menus for `...to send` set to **All**
|
||||
- Resource version should be set to **1.0** for `Pull request created` and `Pull request updated` event types and **2.0** for `Pull request commented on`
|
||||
- **NOTE** If you're adding a webhook to multiple team projects or repositories (using filters), each repository will need to use the **same** basic username and password.
|
||||
- Click **Finish**
|
||||
* Navigate anywhere within a team project, ie: `https://dev.azure.com/orgName/projectName/_git/repoName`
|
||||
* Select **Project settings** in the lower-left corner
|
||||
* Select **Service hooks**
|
||||
* If you see the message "You do not have sufficient permissions to view or configure subscriptions." you need to ensure your user is a member of either the organization's "Project Collection Administrators" group or the project's "Project Administrators" group.
|
||||
* To add your user to the Project Collection Build Administrators group, navigate to the organization level, click **Organization Settings** and then click **Permissions**. You should be at `https://dev.azure.com/<organization>/_settings/groups`. Now click on the **\<organization\>/Project Collection Administrators** group and add your user as a member.
|
||||
* To add your user to the Project Administrators group, navigate to the project level, click **Project Settings** and then click **Permissions**. You should be at `https://dev.azure.com/<organization>/<project>/_settings/permissions`. Now click on the **\<project\>/Project Administrators** group and add your user as a member.
|
||||
* Click **Create subscription** or the green plus icon to add a new webhook
|
||||
* Scroll to the bottom of the list and select **Web Hooks**
|
||||
* Click **Next**
|
||||
* Under "Trigger on this type of event", select **Pull request created**
|
||||
* Optionally, select a repository under **Filters** to restrict the scope of this webhook subscription to a specific repository
|
||||
* Click **Next**
|
||||
* Set **URL** to `http://$URL/events` where `$URL` is where Atlantis is hosted. Note that SSL, or `https://$URL/events`, is required if you set a Basic username and password for the webhook). **Be sure to add `/events`**
|
||||
* It is strongly recommended to set a Basic Username and Password for all webhooks
|
||||
* Leave all three drop-down menus for `...to send` set to **All**
|
||||
* Resource version should be set to **1.0** for `Pull request created` and `Pull request updated` event types and **2.0** for `Pull request commented on`
|
||||
* **NOTE** If you're adding a webhook to multiple team projects or repositories (using filters), each repository will need to use the **same** basic username and password.
|
||||
* Click **Finish**
|
||||
|
||||
Repeat the process above until you have webhook subscriptions for the following event types that will trigger on all repositories Atlantis will manage:
|
||||
|
||||
- Pull request created (you just added this one)
|
||||
- Pull request updated
|
||||
- Pull request commented on
|
||||
* Pull request created (you just added this one)
|
||||
* Pull request updated
|
||||
* Pull request commented on
|
||||
|
||||
- See [Next Steps](#next-steps)
|
||||
* See [Next Steps](#next-steps)
|
||||
|
||||
## Next Steps
|
||||
|
||||
* To verify that Atlantis is receiving your webhooks, create a test pull request to your repo.
|
||||
* You should see the request show up in the Atlantis logs at an `INFO` level.
|
||||
* You'll now need to configure Atlantis to add your [Provider Credentials](provider-credentials.md)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Custom Policy Checks
|
||||
If you want to run custom policy tools or scripts instead of the built-in Conftest integration, you can do so by setting the `custom_policy_check` option and running it in a custom workflow. Note: custom policy tool output is simply parsed for "fail" substrings to determine if the policy set passed.
|
||||
|
||||
This option can be configured either at the server-level in a [repos.yaml config file](server-configuration.md) or at the repo-level in an [atlantis.yaml file.](repo-level-atlantis-yaml.md).
|
||||
If you want to run custom policy tools or scripts instead of the built-in Conftest integration, you can do so by setting the `custom_policy_check` option and running it in a custom workflow. Note: custom policy tool output is simply parsed for "fail" substrings to determine if the policy set passed.
|
||||
|
||||
This option can be configured either at the server-level in a [repos.yaml config file](server-configuration.md) or at the repo-level in an [atlantis.yaml file.](repo-level-atlantis-yaml.md).
|
||||
|
||||
## Server-side config example
|
||||
|
||||
Set the `policy_check` and `custom_policy_check` options to true, and run the custom tool in the policy check steps as seen below.
|
||||
|
||||
```yaml
|
||||
@@ -30,8 +32,8 @@ policies:
|
||||
source: local
|
||||
```
|
||||
|
||||
|
||||
## Repo-level atlantis.yaml example
|
||||
|
||||
First, you will need to ensure `custom_policy_check` is within the `allowed_overrides` field of the server-side config. Next, just set the custom option to true on the specific project you want as shown in the example `atlantis.yaml` below:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -3,23 +3,25 @@
|
||||
Custom workflows can be defined to override the default commands that Atlantis
|
||||
runs.
|
||||
|
||||
[[toc]]
|
||||
|
||||
## Usage
|
||||
|
||||
Custom workflows can be specified in the Server-Side Repo Config or in the Repo-Level
|
||||
`atlantis.yaml` files.
|
||||
|
||||
**Notes**
|
||||
* If you want to allow repos to select their own workflows, they must have the
|
||||
`allowed_overrides: [workflow]` setting. See [server-side repo config use cases](server-side-repo-config.html#allow-repos-to-choose-a-server-side-workflow) for more details.
|
||||
* If in addition you also want to allow repos to define their own workflows, they must have the
|
||||
`allow_custom_workflows: true` setting. See [server-side repo config use cases](server-side-repo-config.html#allow-repos-to-define-their-own-workflows) for more details.
|
||||
**Notes:**
|
||||
|
||||
* If you want to allow repos to select their own workflows, they must have the
|
||||
`allowed_overrides: [workflow]` setting. See [server-side repo config use cases](server-side-repo-config.md#allow-repos-to-choose-a-server-side-workflow) for more details.
|
||||
* If in addition you also want to allow repos to define their own workflows, they must have the
|
||||
`allow_custom_workflows: true` setting. See [server-side repo config use cases](server-side-repo-config.md#allow-repos-to-define-their-own-workflows) for more details.
|
||||
|
||||
## Use Cases
|
||||
|
||||
### .tfvars files
|
||||
|
||||
Given the structure:
|
||||
```
|
||||
|
||||
```plain
|
||||
.
|
||||
└── project1
|
||||
├── main.tf
|
||||
@@ -29,6 +31,7 @@ Given the structure:
|
||||
|
||||
If you wanted Atlantis to automatically run plan with `-var-file staging.tfvars` and `-var-file production.tfvars`
|
||||
you could define two workflows:
|
||||
|
||||
```yaml
|
||||
# repos.yaml or atlantis.yaml
|
||||
workflows:
|
||||
@@ -40,7 +43,7 @@ workflows:
|
||||
extra_args: ["-var-file", "staging.tfvars"]
|
||||
# NOTE: no need to define the apply stage because it will default
|
||||
# to the normal apply stage.
|
||||
|
||||
|
||||
production:
|
||||
plan:
|
||||
steps:
|
||||
@@ -62,7 +65,9 @@ workflows:
|
||||
- state_rm:
|
||||
extra_args: ["-lock=false"]
|
||||
```
|
||||
|
||||
Then in your repo-level `atlantis.yaml` file, you would reference the workflows:
|
||||
|
||||
```yaml
|
||||
# atlantis.yaml
|
||||
version: 3
|
||||
@@ -80,20 +85,27 @@ workflows:
|
||||
# If you didn't define the workflows in your server-side repos.yaml config,
|
||||
# you would define them here instead.
|
||||
```
|
||||
|
||||
When you want to apply the plans, you can comment
|
||||
```
|
||||
|
||||
```shell
|
||||
atlantis apply -p project1-staging
|
||||
```
|
||||
|
||||
and
|
||||
```
|
||||
|
||||
```shell
|
||||
atlantis apply -p project1-production
|
||||
```
|
||||
|
||||
Where `-p` refers to the project name.
|
||||
|
||||
### Adding extra arguments to Terraform commands
|
||||
|
||||
If you need to append flags to `terraform plan` or `apply` temporarily, you can
|
||||
append flags on a comment following `--`, for example commenting:
|
||||
```
|
||||
|
||||
```shell
|
||||
atlantis plan -- -lock=false
|
||||
```
|
||||
|
||||
@@ -117,7 +129,7 @@ workflows:
|
||||
extra_args: ["-lock=false"]
|
||||
```
|
||||
|
||||
If [policy checking](/docs/policy-checking.html#how-it-works) is enabled, `extra_args` can also be used to change the default behaviour of conftest.
|
||||
If [policy checking](policy-checking.md#how-it-works) is enabled, `extra_args` can also be used to change the default behaviour of conftest.
|
||||
|
||||
```yaml
|
||||
workflows:
|
||||
@@ -130,6 +142,7 @@ workflows:
|
||||
```
|
||||
|
||||
### Custom init/plan/apply Commands
|
||||
|
||||
If you want to customize `terraform init`, `plan` or `apply` in ways that
|
||||
aren't supported by `extra_args`, you can completely override those commands.
|
||||
|
||||
@@ -147,11 +160,11 @@ workflows:
|
||||
- run:
|
||||
command: terraform init -input=false
|
||||
output: hide
|
||||
|
||||
|
||||
# If you're using workspaces you need to select the workspace using the
|
||||
# $WORKSPACE environment variable.
|
||||
- run: terraform workspace select $WORKSPACE
|
||||
|
||||
|
||||
# You MUST output the plan using -out $PLANFILE because Atlantis expects
|
||||
# plans to be in a specific location.
|
||||
- run: terraform plan -input=false -refresh -out $PLANFILE
|
||||
@@ -162,14 +175,15 @@ workflows:
|
||||
```
|
||||
|
||||
### CDKTF
|
||||
|
||||
Here are the requirements to enable [CDKTF](https://developer.hashicorp.com/terraform/cdktf)
|
||||
|
||||
- A custom image with `CDKTF` installed
|
||||
- Add `**/cdk.tf.json` to the list of Atlantis autoplan files.
|
||||
- Set the `atlantis-include-git-untracked-files` flag so that the Terraform files dynamically generated
|
||||
* A custom image with `CDKTF` installed
|
||||
* Add `**/cdk.tf.json` to the list of Atlantis autoplan files.
|
||||
* Set the `atlantis-include-git-untracked-files` flag so that the Terraform files dynamically generated
|
||||
by CDKTF will be add to the Atlantis modified file list.
|
||||
- Use `pre_workflow_hooks` to run `cdktf synth`
|
||||
- Optional: There isn't a requirement to use a repo `atlantis.yaml` but one can be leveraged if needed.
|
||||
* Use `pre_workflow_hooks` to run `cdktf synth`
|
||||
* Optional: There isn't a requirement to use a repo `atlantis.yaml` but one can be leveraged if needed.
|
||||
|
||||
#### Custom Image
|
||||
|
||||
@@ -177,6 +191,7 @@ by CDKTF will be add to the Atlantis modified file list.
|
||||
# Dockerfile
|
||||
FROM ghcr.io/runatlantis/atlantis:v0.19.7
|
||||
|
||||
USER root
|
||||
RUN apk add npm && npm i -g cdktf-cli
|
||||
```
|
||||
|
||||
@@ -191,6 +206,7 @@ ATLANTIS_INCLUDE_GIT_UNTRACKED_FILES=true
|
||||
OR
|
||||
|
||||
`atlantis server --config config.yaml`
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
autoplan-file-list: "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/cdk.tf.json"
|
||||
@@ -202,6 +218,7 @@ include-git-untracked-files: true
|
||||
Use `pre_workflow_hooks`
|
||||
|
||||
`atlantis server --repo-config="repos.yaml"`
|
||||
|
||||
```yaml
|
||||
# repos.yaml
|
||||
repos:
|
||||
@@ -233,7 +250,7 @@ $ tree --gitignore
|
||||
|
||||
1. Container orchestrator (k8s/fargate/ecs/etc) uses the custom docker image of atlantis with `cdktf` installed with
|
||||
the `--autoplan-file-list` to trigger on `cdk.tf.json` files and `--include-git-untracked-files` set to include the
|
||||
CDKTF dynamically generated Terraform files in the Atlantis plan.
|
||||
CDKTF dynamically generated Terraform files in the Atlantis plan.
|
||||
1. PR branch is pushed up containing `cdktf` code changes.
|
||||
1. Atlantis checks out the branch in the repo.
|
||||
1. Atlantis runs the `npm i && cdktf get && cdktf synth` command in the repo root as a step in `pre_workflow_hooks`,
|
||||
@@ -242,6 +259,7 @@ generating the `cdk.tf.json` Terraform files.
|
||||
1. Atlantis then runs `terraform` workflows in the respective directories as usual.
|
||||
|
||||
### Terragrunt
|
||||
|
||||
Atlantis supports running custom commands in place of the default Atlantis
|
||||
commands. We can use this functionality to enable
|
||||
[Terragrunt](https://github.com/gruntwork-io/terragrunt).
|
||||
@@ -249,7 +267,8 @@ commands. We can use this functionality to enable
|
||||
You can either use your repo's `atlantis.yaml` file or the Atlantis server's `repos.yaml` file.
|
||||
|
||||
Given a directory structure:
|
||||
```
|
||||
|
||||
```plain
|
||||
.
|
||||
└── live
|
||||
├── prod
|
||||
@@ -314,6 +333,7 @@ workflows:
|
||||
```
|
||||
|
||||
If using the repo's `atlantis.yaml` file you would use the following config:
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -349,10 +369,9 @@ workflows:
|
||||
|
||||
**NOTE:** If using the repo's `atlantis.yaml` file, you will need to specify each directory that is a Terragrunt project.
|
||||
|
||||
|
||||
::: warning
|
||||
Atlantis will need to have the `terragrunt` binary in its PATH.
|
||||
If you're using Docker you can build your own image, see [Customization](/docs/deployment.html#customization).
|
||||
If you're using Docker you can build your own image, see [Customization](deployment.md#customization).
|
||||
:::
|
||||
|
||||
If you don't want to create/manage the repo's `atlantis.yaml` file yourself, you can use the tool [terragrunt-atlantis-config](https://github.com/transcend-io/terragrunt-atlantis-config) to generate it.
|
||||
@@ -360,6 +379,7 @@ If you don't want to create/manage the repo's `atlantis.yaml` file yourself, you
|
||||
The `terragrunt-atlantis-config` tool is a community project and not maintained by the Atlantis team.
|
||||
|
||||
### Running custom commands
|
||||
|
||||
Atlantis supports running completely custom commands. In this example, we want to run
|
||||
a script after every `apply`:
|
||||
|
||||
@@ -374,17 +394,19 @@ workflows:
|
||||
```
|
||||
|
||||
::: tip Notes
|
||||
|
||||
* We don't need to write a `plan` key under `myworkflow`. If `plan`
|
||||
isn't set, Atlantis will use the default plan workflow which is what we want in this case.
|
||||
* A custom command will only terminate if all output file descriptors are closed.
|
||||
Therefore a custom command can only be sent to the background (e.g. for an SSH tunnel during
|
||||
the terraform run) when its output is redirected to a different location. For example, Atlantis
|
||||
will execute a custom script containing the following code to create a SSH tunnel correctly:
|
||||
will execute a custom script containing the following code to create a SSH tunnel correctly:
|
||||
`ssh -f -M -S /tmp/ssh_tunnel -L 3306:database:3306 -N bastion 1>/dev/null 2>&1`. Without
|
||||
the redirect, the script would block the Atlantis workflow.
|
||||
:::
|
||||
|
||||
### Custom Backend Config
|
||||
|
||||
If you need to specify the `-backend-config` flag to `terraform init` you'll need to use a custom workflow.
|
||||
In this example, we're using custom backend files to configure two remote states, one for each environment.
|
||||
We're then using `.tfvars` files to load different variables for each environment.
|
||||
@@ -409,12 +431,14 @@ workflows:
|
||||
- plan:
|
||||
extra_args: [-var-file=production.tfvars]
|
||||
```
|
||||
|
||||
::: warning NOTE
|
||||
We have to use a custom `run` step to `rm -rf .terraform` because otherwise Terraform
|
||||
will complain in-between commands since the backend config has changed.
|
||||
:::
|
||||
|
||||
You would then reference the workflows in your repo-level `atlantis.yaml`:
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
projects:
|
||||
@@ -426,8 +450,69 @@ projects:
|
||||
workflow: production
|
||||
```
|
||||
|
||||
### Add directory and repo context for aws resources using default tags
|
||||
|
||||
This is only available in AWS provider version [5.62.0](https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.62.0) and higher.
|
||||
|
||||
This configuration will create the following tags
|
||||
|
||||
* `repository` equal to `github.com/<owner>/<repo>` which can be changed for gitlab or other VCS
|
||||
* `repository_dir` equal to the relative directory
|
||||
|
||||
Other default variables can be added such as for workspace. See below for more available environment variables.
|
||||
|
||||
```yaml
|
||||
workflows:
|
||||
terraform:
|
||||
plan:
|
||||
steps:
|
||||
# These env vars TF_AWS_DEFAULT_TAGS_ will work for aws provider 5.62.0+
|
||||
# https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.62.0
|
||||
- &env_default_tags_repository
|
||||
env:
|
||||
name: TF_AWS_DEFAULT_TAGS_repository
|
||||
command: 'echo "github.com/${BASE_REPO_OWNER}/${BASE_REPO_NAME}"'
|
||||
- &env_default_tags_repository_dir
|
||||
env:
|
||||
name: TF_AWS_DEFAULT_TAGS_repository_dir
|
||||
command: 'echo "${REPO_REL_DIR}"'
|
||||
apply:
|
||||
steps:
|
||||
- *env_default_tags_repository
|
||||
- *env_default_tags_repository_dir
|
||||
```
|
||||
|
||||
NOTE:
|
||||
|
||||
* Appending tags to every resource may regenerate data sources such as `aws_iam_policy_document` which will cause many resources to be modified. See known issue in aws provider [#29421](https://github.com/hashicorp/terraform-provider-aws/issues/29421).
|
||||
|
||||
* To run a local plan outside of terraform, the same environment variables will need to be created.
|
||||
|
||||
```bash
|
||||
tfvars () {
|
||||
export terraform_repository=$(git config --get remote.origin.url | sed 's,^git@,,g' | tr ':' '/' | sed 's,.git$,,g')
|
||||
export terraform_repository_dir=$(git rev-parse --show-prefix | sed 's,\/$,,g')
|
||||
}
|
||||
export TF_AWS_DEFAULT_TAGS_repository=$terraform_repository
|
||||
export TF_AWS_DEFAULT_TAGS_repository_dir=$terraform_repository_dir
|
||||
tfvars
|
||||
terraform plan
|
||||
```
|
||||
|
||||
If a colon is used in the tag name, use the `env` command instead of `export`.
|
||||
|
||||
```bash
|
||||
tfvars
|
||||
env \
|
||||
TF_AWS_DEFAULT_TAGS_org:repository=$terraform_repository \
|
||||
TF_AWS_DEFAULT_TAGS_org:repository_dir=$terraform_repository_dir \
|
||||
terraform plan
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Workflow
|
||||
|
||||
```yaml
|
||||
plan:
|
||||
apply:
|
||||
@@ -443,6 +528,7 @@ state_rm:
|
||||
| state_rm | [Stage](#stage) | `steps: [init, state_rm]` | no | How to run state rm for this project. |
|
||||
|
||||
### Stage
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- run: custom-command
|
||||
@@ -456,8 +542,11 @@ steps:
|
||||
| steps | array[[Step](#step)] | `[]` | no | List of steps for this stage. If the steps key is empty, no steps will be run for this stage. |
|
||||
|
||||
### Step
|
||||
|
||||
#### Built-In Commands
|
||||
|
||||
Steps can be a single string for a built-in command.
|
||||
|
||||
```yaml
|
||||
- init
|
||||
- plan
|
||||
@@ -465,12 +554,15 @@ Steps can be a single string for a built-in command.
|
||||
- import
|
||||
- state_rm
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|---------------------------------|--------|---------|----------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
| init/plan/apply/import/state_rm | string | none | no | Use a built-in command without additional configuration. Only `init`, `plan`, `apply`, `import` and `state_rm` are supported |
|
||||
|
||||
#### Built-In Command With Extra Args
|
||||
|
||||
A map from string to `extra_args` for a built-in command with extra arguments.
|
||||
|
||||
```yaml
|
||||
- init:
|
||||
extra_args: [arg1, arg2]
|
||||
@@ -483,79 +575,88 @@ A map from string to `extra_args` for a built-in command with extra arguments.
|
||||
- state_rm:
|
||||
extra_args: [arg1, arg2]
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|---------------------------------|------------------------------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| init/plan/apply/import/state_rm | map[`extra_args` -> array[string]] | none | no | Use a built-in command and append `extra_args`. Only `init`, `plan`, `apply`, `import` and `state_rm` are supported as keys and only `extra_args` is supported as a value |
|
||||
| init/plan/apply/import/state_rm | map\[`extra_args` -> array\[string\]\] | none | no | Use a built-in command and append `extra_args`. Only `init`, `plan`, `apply`, `import` and `state_rm` are supported as keys and only `extra_args` is supported as a value |
|
||||
|
||||
#### Custom `run` Command
|
||||
|
||||
A custom command can be written in 2 ways
|
||||
|
||||
Compact:
|
||||
|
||||
```yaml
|
||||
- run: custom-command arg1 arg2
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|-----|--------|---------|----------|----------------------|
|
||||
| run | string | none | no | Run a custom command |
|
||||
|
||||
Full
|
||||
|
||||
```yaml
|
||||
- run:
|
||||
- run:
|
||||
command: custom-command arg1 arg2
|
||||
output: show
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|-----|--------------------------------------------------------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| run | map[string -> string] | none | no | Run a custom command |
|
||||
| run | map\[string -> string\] | none | no | Run a custom command |
|
||||
| run.command | string | none | yes | Shell command to run |
|
||||
| run.output | string | "show" | no | How to post-process the output of this command when posted in the PR comment. The options are<br/>* `show` - preserve the full output<br/>* `hide` - hide output from comment (still visible in the real-time streaming output)<br/> * `strip_refreshing` - hide all output up until and including the last line containing "Refreshing...". This matches the behavior of the built-in `plan` command |
|
||||
| run.output | string | "show" | no | How to post-process the output of this command when posted in the PR comment. The options are<br/>*`show` - preserve the full output<br/>* `hide` - hide output from comment (still visible in the real-time streaming output)<br/> * `strip_refreshing` - hide all output up until and including the last line containing "Refreshing...". This matches the behavior of the built-in `plan` command |
|
||||
|
||||
::: tip Notes
|
||||
* `run` steps in the main `workflow` are executed with the following environment variables:
|
||||
|
||||
* `run` steps in the main `workflow` are executed with the following environment variables:
|
||||
note: these variables are not available to `pre` or `post` workflows
|
||||
* `WORKSPACE` - The Terraform workspace used for this project, ex. `default`.
|
||||
* `WORKSPACE` - The Terraform workspace used for this project, ex. `default`.
|
||||
NOTE: if the step is executed before `init` then Atlantis won't have switched to this workspace yet.
|
||||
* `ATLANTIS_TERRAFORM_VERSION` - The version of Terraform used for this project, ex. `0.11.0`.
|
||||
* `DIR` - Absolute path to the current directory.
|
||||
* `PLANFILE` - Absolute path to the location where Atlantis expects the plan to
|
||||
* `ATLANTIS_TERRAFORM_VERSION` - The version of Terraform used for this project, ex. `0.11.0`.
|
||||
* `DIR` - Absolute path to the current directory.
|
||||
* `PLANFILE` - Absolute path to the location where Atlantis expects the plan to
|
||||
either be generated (by plan) or already exist (if running apply). Can be used to
|
||||
override the built-in `plan`/`apply` commands, ex. `run: terraform plan -out $PLANFILE`.
|
||||
* `SHOWFILE` - Absolute path to the location where Atlantis expects the plan in json format to
|
||||
* `SHOWFILE` - Absolute path to the location where Atlantis expects the plan in json format to
|
||||
either be generated (by show) or already exist (if running policy checks). Can be used to
|
||||
override the built-in `plan`/`apply` commands, ex. `run: terraform show -json $PLANFILE > $SHOWFILE`.
|
||||
* `POLICYCHECKFILE` - Absolute path to the location of policy check output if Atlantis runs policy checks.
|
||||
See [policy checking](/docs/policy-checking.html#data-for-custom-run-steps) for information of data structure.
|
||||
* `BASE_REPO_NAME` - Name of the repository that the pull request will be merged into, ex. `atlantis`.
|
||||
* `BASE_REPO_OWNER` - Owner of the repository that the pull request will be merged into, ex. `runatlantis`.
|
||||
* `HEAD_REPO_NAME` - Name of the repository that is getting merged into the base repository, ex. `atlantis`.
|
||||
* `HEAD_REPO_OWNER` - Owner of the repository that is getting merged into the base repository, ex. `acme-corp`.
|
||||
* `HEAD_BRANCH_NAME` - Name of the head branch of the pull request (the branch that is getting merged into the base)
|
||||
* `HEAD_COMMIT` - The sha256 that points to the head of the branch that is being pull requested into the base. If the pull request is from Bitbucket Cloud the string will only be 12 characters long because Bitbucket Cloud truncates its commit IDs.
|
||||
* `BASE_BRANCH_NAME` - Name of the base branch of the pull request (the branch that the pull request is getting merged into)
|
||||
* `PROJECT_NAME` - Name of the project configured in `atlantis.yaml`. If no project name is configured this will be an empty string.
|
||||
* `PULL_NUM` - Pull request number or ID, ex. `2`.
|
||||
* `PULL_URL` - Pull request URL, ex. `https://github.com/runatlantis/atlantis/pull/2`.
|
||||
* `PULL_AUTHOR` - Username of the pull request author, ex. `acme-user`.
|
||||
* `REPO_REL_DIR` - The relative path of the project in the repository. For example if your project is in `dir1/dir2/` then this will be set to `"dir1/dir2"`. If your project is at the root this will be `"."`.
|
||||
* `USER_NAME` - Username of the VCS user running command, ex. `acme-user`. During an autoplan, the user will be the Atlantis API user, ex. `atlantis`.
|
||||
* `COMMENT_ARGS` - Any additional flags passed in the comment on the pull request. Flags are separated by commas and
|
||||
* `POLICYCHECKFILE` - Absolute path to the location of policy check output if Atlantis runs policy checks.
|
||||
See [policy checking](policy-checking.md#data-for-custom-run-steps) for information of data structure.
|
||||
* `BASE_REPO_NAME` - Name of the repository that the pull request will be merged into, ex. `atlantis`.
|
||||
* `BASE_REPO_OWNER` - Owner of the repository that the pull request will be merged into, ex. `runatlantis`.
|
||||
* `HEAD_REPO_NAME` - Name of the repository that is getting merged into the base repository, ex. `atlantis`.
|
||||
* `HEAD_REPO_OWNER` - Owner of the repository that is getting merged into the base repository, ex. `acme-corp`.
|
||||
* `HEAD_BRANCH_NAME` - Name of the head branch of the pull request (the branch that is getting merged into the base)
|
||||
* `HEAD_COMMIT` - The sha256 that points to the head of the branch that is being pull requested into the base. If the pull request is from Bitbucket Cloud the string will only be 12 characters long because Bitbucket Cloud truncates its commit IDs.
|
||||
* `BASE_BRANCH_NAME` - Name of the base branch of the pull request (the branch that the pull request is getting merged into)
|
||||
* `PROJECT_NAME` - Name of the project configured in `atlantis.yaml`. If no project name is configured this will be an empty string.
|
||||
* `PULL_NUM` - Pull request number or ID, ex. `2`.
|
||||
* `PULL_URL` - Pull request URL, ex. `https://github.com/runatlantis/atlantis/pull/2`.
|
||||
* `PULL_AUTHOR` - Username of the pull request author, ex. `acme-user`.
|
||||
* `REPO_REL_DIR` - The relative path of the project in the repository. For example if your project is in `dir1/dir2/` then this will be set to `"dir1/dir2"`. If your project is at the root this will be `"."`.
|
||||
* `USER_NAME` - Username of the VCS user running command, ex. `acme-user`. During an autoplan, the user will be the Atlantis API user, ex. `atlantis`.
|
||||
* `COMMENT_ARGS` - Any additional flags passed in the comment on the pull request. Flags are separated by commas and
|
||||
every character is escaped, ex. `atlantis plan -- arg1 arg2` will result in `COMMENT_ARGS=\a\r\g\1,\a\r\g\2`.
|
||||
* A custom command will only terminate if all output file descriptors are closed.
|
||||
Therefore a custom command can only be sent to the background (e.g. for an SSH tunnel during
|
||||
the terraform run) when its output is redirected to a different location. For example, Atlantis
|
||||
will execute a custom script containing the following code to create a SSH tunnel correctly:
|
||||
will execute a custom script containing the following code to create a SSH tunnel correctly:
|
||||
`ssh -f -M -S /tmp/ssh_tunnel -L 3306:database:3306 -N bastion 1>/dev/null 2>&1`. Without
|
||||
the redirect, the script would block the Atlantis workflow.
|
||||
* If a workflow step returns a non-zero exit code, the workflow will stop.
|
||||
* If a workflow step returns a non-zero exit code, the workflow will stop.
|
||||
:::
|
||||
|
||||
#### Environment Variable `env` Command
|
||||
|
||||
The `env` command allows you to set environment variables that will be available
|
||||
to all steps defined **below** the `env` step.
|
||||
|
||||
You can set hard coded values via the `value` key, or set dynamic values via
|
||||
the `command` key which allows you to run any command and uses the output
|
||||
as the environment variable value.
|
||||
|
||||
```yaml
|
||||
- env:
|
||||
name: ENV_NAME
|
||||
@@ -564,34 +665,56 @@ as the environment variable value.
|
||||
name: ENV_NAME_2
|
||||
command: 'echo "dynamic-value-$(date)"'
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|-----------------|-----------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| env | map[string -> string] | none | no | Set environment variables for subsequent steps |
|
||||
| env | map\[string -> string\] | none | no | Set environment variables for subsequent steps |
|
||||
| env.name | string | none | yes | Name of the environment variable |
|
||||
| env.value | string | none | no | Set the value of the environment variable to a hard-coded string. Cannot be set at the same time as `command` |
|
||||
| env.command | string | none | no | Set the value of the environment variable to the output of a command. Cannot be set at the same time as `value` |
|
||||
|
||||
::: tip Notes
|
||||
|
||||
* `env` `command`'s can use any of the built-in environment variables available
|
||||
to `run` commands.
|
||||
to `run` commands.
|
||||
:::
|
||||
|
||||
#### Multiple Environment Variables `multienv` Command
|
||||
|
||||
The `multienv` command allows you to set dynamic number of multiple environment variables that will be available
|
||||
to all steps defined **below** the `multienv` step.
|
||||
|
||||
Compact:
|
||||
|
||||
```yaml
|
||||
- multienv: custom-command
|
||||
```
|
||||
| Key | Type | Default | Required | Description |
|
||||
|----------|--------|---------|----------|--------------------------------------------------------------------------------|
|
||||
| multienv | string | none | no | Run a custom command and add set environment variables according to the result |
|
||||
|
||||
The result of the executed command must have a fixed format:
|
||||
EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3
|
||||
| Key | Type | Default | Required | Description |
|
||||
|----------|--------|---------|----------|------------------------------------------------------------|
|
||||
| multienv | string | none | no | Run a custom command and add printed environment variables |
|
||||
|
||||
The name-value pairs in the result are added as environment variables if success is true otherwise the workflow execution stops with error and the errorMessage is getting displayed.
|
||||
Full:
|
||||
|
||||
```yaml
|
||||
- multienv:
|
||||
command: custom-command
|
||||
output: show
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|------------------|-----------------------|---------|----------|-------------------------------------------------------------------------------------|
|
||||
| multienv | map[string -> string] | none | no | Run a custom command and add printed environment variables |
|
||||
| multienv.command | string | none | yes | Name of the custom script to run |
|
||||
| multienv.output | string | "show" | no | Setting output to "hide" will supress the message obout added environment variables |
|
||||
|
||||
The output of the command execution must have the following format:
|
||||
`EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3`
|
||||
|
||||
The name-value pairs in the output are added as environment variables if command execution is successful, otherwise the workflow execution is interrupted with an error and the errorMessage is returned.
|
||||
|
||||
::: tip Notes
|
||||
|
||||
* `multienv` `command`'s can use any of the built-in environment variables available
|
||||
to `run` commands.
|
||||
to `run` commands.
|
||||
:::
|
||||
|
||||