[
  {
    "path": ".circleci/config.yml",
    "content": "---\nversion: 2.1\norbs:\n  slack-message: commitdev/slack-message@0.0.3\n  version-tag: commitdev/version-tag@0.0.3\n\nvariables:\n  - &workspace /home/circleci/project\n  - &build-image circleci/golang:1.16.8\n\naliases:\n  # Shallow Clone - this allows us to cut the 2 minute repo clone down to about 10 seconds for repos with 50,000 commits+\n  - &checkout-shallow\n    name: Checkout (Shallow)\n    command: |\n      #!/bin/sh\n      set -e\n\n      # Workaround old docker images with incorrect $HOME\n      # check https://github.com/docker/docker/issues/2968 for details\n      if [ \"${HOME}\" = \"/\" ]\n      then\n        export HOME=$(getent passwd $(id -un) | cut -d: -f6)\n      fi\n\n      mkdir -p ~/.ssh\n\n      echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n      bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==' >> ~/.ssh/known_hosts\n\n      (umask 077; touch ~/.ssh/id_rsa)\n      chmod 0600 ~/.ssh/id_rsa\n      (cat \\<<EOF > ~/.ssh/id_rsa\n      $CHECKOUT_KEY\n      EOF\n      )\n\n      # use git+ssh instead of https\n      git config --global url.\"ssh://git@github.com\".insteadOf \"https://github.com\" || true\n\n      if [ -e /home/circleci/project/.git ]\n      then\n          cd /home/circleci/project\n          git remote set-url origin \"$CIRCLE_REPOSITORY_URL\" || true\n      else\n          mkdir -p /home/circleci/project\n          cd /home/circleci/project\n          git clone --depth=1 \"$CIRCLE_REPOSITORY_URL\" .\n      fi\n\n      if [ -n \"$CIRCLE_TAG\" ]\n      then\n        git fetch --depth=10 --force origin \"refs/tags/${CIRCLE_TAG}\"\n      elif [[ \"$CIRCLE_BRANCH\" =~ ^pull\\/* ]]\n      then\n      # For PR from Fork\n        git fetch --depth=10 --force origin \"$CIRCLE_BRANCH/head:remotes/origin/$CIRCLE_BRANCH\"\n      else\n        git fetch --depth=10 --force origin \"$CIRCLE_BRANCH:remotes/origin/$CIRCLE_BRANCH\"\n      fi\n\n      if [ -n \"$CIRCLE_TAG\" ]\n      then\n          git reset --hard \"$CIRCLE_SHA1\"\n          git checkout -q \"$CIRCLE_TAG\"\n      elif [ -n \"$CIRCLE_BRANCH\" ]\n      then\n          git reset --hard \"$CIRCLE_SHA1\"\n          git checkout -q -B \"$CIRCLE_BRANCH\"\n      fi\n\n      git reset --hard \"$CIRCLE_SHA1\"\n      pwd\n\njobs:\n  checkout_code:\n    docker:\n      - image: *build-image\n    steps:\n      - run: *checkout-shallow\n      - persist_to_workspace:\n          root: /home/circleci/project\n          paths:\n            - .\n\n  unit_test:\n    docker:\n      - image: *build-image\n    working_directory: *workspace\n    steps: # steps that comprise the `build` job\n      - attach_workspace:\n          at: *workspace\n\n      - restore_cache: # restores saved cache if no changes are detected since last run\n          keys:\n            - v1-pkg-cache-{{ checksum \"go.sum\" }}\n            - v1-pkg-cache-\n\n      - run:\n          name: Run unit tests\n          # store the results of our tests in the $TEST_RESULTS directory\n          command: |\n            go get -u github.com/jstemmer/go-junit-report\n            mkdir -p test-reports\n            PACKAGE_NAMES=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname)\n            echo \"Tests: $PACKAGE_NAMES\"\n            go test -v $PACKAGE_NAMES | go-junit-report > test-reports/junit.xml\n\n      - save_cache: # Store cache in the /go/pkg directory\n          key: v1-pkg-cache-{{ checksum \"go.sum\" }}\n          paths:\n            - \"/go/pkg\"\n\n      - store_test_results:\n          path: test-reports\n\n      - store_artifacts:\n          path: test-reports\n\n      # Requires the SLACK_WEBHOOK\n      # - slack/notify-on-failure\n\n  build_and_push:\n    machine:\n      image: circleci/classic:latest\n      # docker_layer_caching: true # only for performance plan circleci accounts\n    steps:\n      - attach_workspace:\n          at: *workspace\n      - run: *checkout-shallow\n      - version-tag/create\n      - run:\n          name: Update Version command\n          command: |\n            ./updateVersion.sh \"$VERSION_TAG\"\n      - run:\n          name: Build Docker image\n          command: |\n            make ci-docker-build\n      - run:\n          name: Push to Docker Hub\n          command: |\n            make ci-docker-push\n\n\nworkflows:\n    version: 2\n    # The main workflow. Check out the code, build it, push it, deploy to staging, test, deploy to production\n    build_test_and_deploy:\n      jobs:\n        - checkout_code\n\n        - unit_test:\n            requires:\n              - checkout_code\n\n        - build_and_push:\n            requires:\n              - unit_test\n            filters:\n              branches:\n                only:  # only branches matching the below regex filters will run\n                  - /^master$/\n\n"
  },
  {
    "path": ".dockerignore",
    "content": "Dockerfile\nREADME.md\n.history/\n.travis.yml\n.gitignore\n.git\nexample/\ntmp/\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [main]\n  schedule:\n    - cron: '0 4 * * 1'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v3\n      with:\n        # We must fetch at least the immediate parents so that if this is\n        # a pull request then we can checkout the head.\n        fetch-depth: 2\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v2\n      with:\n        languages: go\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v2\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v2\n"
  },
  {
    "path": ".github/workflows/config.yml",
    "content": "on:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  unit_test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-go@v2\n        with:\n          go-version: 1.16\n      - uses: actions/cache@v2\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}\n          restore-keys: |\n            ${{ runner.os }}-go-\n      - name: Run Go Tests\n        run: |\n          make check\n"
  },
  {
    "path": ".github/workflows/doc-site.yml",
    "content": "## The is a combination of sites where\n## Zero serves on the root of the domain /\n## and module serves on /docs/modules/<path>/\n# from the same S3 bucket\n\nname: \"Build Documentation Site\"\non:\n  push:\n    branches:\n      - main\n    paths:\n      - doc-site/**\n\nenv:\n  region: us-west-2\n  s3_sync_path_to_exclude: docs/modules/*\n  s3_sync_path: \"\"\n  BUILD_DOMAIN: ${{ secrets.ZERO_DOC_SITE_DOMAIN_NAME }}\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Setup node.js\n        uses: actions/setup-node@v3\n        with:\n          node-version: 16\n      # - name: Documentaiton site folder\n      #   run: cd doc-site\n      - name: Install Dependencies\n        working-directory: doc-site\n        run: npm install\n      - name: Build website\n        working-directory: doc-site\n        run: |\n          npm run build\n          pwd\n          ls -la\n      - name: Upload build artifact to Github\n        uses: actions/upload-artifact@v2\n        with:\n          name: build-artifact\n          path: doc-site/build\n\n  deploy:\n    name: Deploy\n    runs-on: ubuntu-latest\n    needs: build\n    # These permissions are needed to interact with GitHub's OIDC Token endpoint.\n    permissions:\n      id-token: write\n      contents: read\n      \n    steps:\n    # Once github action supports nested composite actions (anything `uses` is a composite action)\n    # Therefore we cannot reuse the code as a separate composite action until it supports it,\n    # current the deploy logic is in this file twice because of it\n    ## https://github.com/actions/runner/issues/862\n    - uses: actions/checkout@v2\n    - name: Configure AWS credentials for S3 sync\n      uses: aws-actions/configure-aws-credentials@v1\n      with:\n        aws-access-key-id: ${{ secrets.ZERO_DOC_SITE_AWS_ACCESS_KEY_ID }}\n        aws-secret-access-key: ${{ secrets.ZERO_DOC_SITE_AWS_SECRET_ACCESS_KEY }}\n        aws-region: ${{ env.region }}\n    - name: Download build artifact from Github\n      uses: actions/download-artifact@v1\n      with:\n        name: build-artifact\n        path: build/\n    - name: Sync with S3\n      shell: bash\n      run: |\n        cd build\n        aws s3 sync . \"s3://${{ secrets.ZERO_DOC_SITE_BUCKET_NAME }}${{ env.s3_sync_path }}\" --exclude \"${{ env.s3_sync_path_to_exclude }}\" --delete\n    - name: Invalidate Cloudfront\n      shell: bash\n      run: |\n        export DIST_ID=$(aws cloudfront list-distributions --query \"DistributionList.Items[?Aliases.Items[?@=='${{ secrets.ZERO_DOC_SITE_BUCKET_NAME }}']].Id | [0]\" | tr -d '\"')\n        aws cloudfront create-invalidation --distribution-id ${DIST_ID} --paths \"/*\"\n"
  },
  {
    "path": ".gitignore",
    "content": "main-packr.go\npackrd\n/zero\n.history/\ntmp\n.vscode\nexample/\ntest-reports/\n.circleci/config-compiled.yml\n.idea/"
  },
  {
    "path": ".goreleaser.yml",
    "content": "# http://goreleaser.com\nbefore:\n  hooks:\n    - go mod download\nbuilds:\n- env:\n  - CGO_ENABLED=0\n  ldflags:\n    - -X github.com/commitdev/zero/version.AppVersion={{.Version}} -X github.com/commitdev/zero/version.AppBuild={{.ShortCommit}}\n  goarch:\n    - amd64\n    - arm64\n    - 386\narchives:\n- replacements:\n    darwin: Darwin\n    linux: Linux\n    386: i386\n    amd64: x86_64\nchecksum:\n  name_template: 'checksums.txt'\nsnapshot:\n  name_template: \"{{ .Tag }}-next\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n    - '^docs:'\n    - '^test:'\n\nbrews:\n  - name: zero\n    tap:\n      owner: commitdev\n      name: homebrew-zero\n    commit_author:\n      name: Commit.dev\n      email: contact@commit.dev\n    homepage: \"https://github.com/commitdev/zero\"\n    description: \"Allow startup developers to ship to production on day 1.\"\n    dependencies:\n      - name: git\n        type: optional\n      - name: terraform\n        type: optional\n      - name: jq\n        type: optional\n      - name: awscli\n        type: optional\n      - name: kubectl\n        type: optional\n      - name: wget\n        type: optional\n    test: |\n      system \"#{bin}/zero version\"\n\nnfpms:\n  -\n    id: zero\n    package_name: zero\n    vendor: Commit.dev\n    homepage: https://github.com/commitdev/zero\n    maintainer: commitdev <contact@commit.dev>\n    description: \"Allow startup developers to ship to production on day 1.\"\n    formats:\n      - apk\n      - deb\n      - rpm\n\n    recommends:\n      - awscli\n      - git\n      - jq\n      - wget\n\n    overrides:\n      apk:\n        recommends:\n          - aws-cli\n          - git\n          - jq\n          - wget\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Zero\n\nThanks for your interest in Zero!\n\nZero is a fully open source project started by [Commit](https://commit.dev) but we are happy to be receiving support from our users and community.\n\nIf you have used Zero or are just interested in helping out there are a few key ways to contribute:\n\n## Contribute some code\nZero is made up of a number of different, modular components. Eventually the idea is to make these more composable and discoverable with a module repository for anyone to supply their own, but for now we mostly organize the modules into predefined \"stacks\" combining the layers of infrastructure, backend, frontend, and static site.\n\nEach module is in its own repo and has its own focus, language, etc. so there's plenty to contribute, regardless of your language of choice.\nHere's a list of the core repositories:\n|_Repo_|_Language_|_Description_|\n|--|--|--|\n| [zero](https://github.com/commitdev/zero) | Go | This repo - the application used to do prompting, module fetching, template rendering, and executing the commands of each module |\n| [zero-aws-eks-stack](https://github.com/commitdev/zero-aws-eks-stack) | Terraform | The terraform code to create all infrastructure required to host the backend and frontend  applications in AWS (primarily using EKS) |\n| [zero-backend-go](https://github.com/commitdev/zero-backend-go) | Go | A deployable backend service providing an API written in Go |\n| [zero-backend-node](https://github.com/commitdev/zero-backend-node) | Node.js | A deployable backend service providing an API written in Node |\n| [zero-frontend-react](https://github.com/commitdev/zero-frontend-react) | Javascript / React | A deployable web frontend meant to communicate with one of the zero backend services |\n| [zero-static-site-gatsby](https://github.com/commitdev/zero-static-site-gatsby) | Javascript / Gatsby | A deployable static site / marketing site for your application |\n| [zero-notification-service](https://github.com/commitdev/zero-notification-service) | Go | A service to abstract away some concepts around sending notifications (via Email, SMS, Slack, etc.) |\n| [terraform-aws-zero](https://github.com/commitdev/zero-notification-service) | Terraform | Terraform modules that are exposed via the [Terrform Registry](https://registry.terraform.io/modules/commitdev/zero/aws/latest) and used in Zero modules. Typically functionality that is reusable and standalone |\n\nThere is a [GitHub Projects board](https://github.com/orgs/commitdev/projects/6/views/2) to aggregate the issues across all these repositories. This is a great way to get a sense of the work that is available and in the backlog across all the various modules and languages.\n\nWe are trying to make sure to keep a good amount of issues in the backlog with the \"[good first issue](https://github.com/orgs/commitdev/projects/6/views/2?filterQuery=label%3A%22good+first+issue%22)\" label and any issues with this label could be a good place to start either because they are relatively easy or have few dependencies. We also try to include an estimate (Fibonacci where 1 is trivial, probably just a couple lines of code, and 8 or 13 would be a huge undertaking that likely needs to be split up into smaller issues.)\n\n### Pull Requests\nIf you're interested in taking on an issue please comment on it to let other people know that someone is working on it. Then you can fork the repo and start local development.\n\nWhen committing code, please sign your commits and try to include relevant commit messages, starting with a tag indicating what type of change you are making. Some of the repositories use these tags to automatically generate changelogs. (`feat`, `fix`, `enhancement`, `docs`, etc.)\nFor example:\n`fix: add proper encoding of billing parameters`\nor\n`enhancement: support new terraform kubernetes provider`\n\nWhen submitting a pull request to one of the projects please try to follow any PR and style guidelines, and make sure any relevant GitHub Actions tests are passing. If one of the tests is failing and you don't think it's related to your change, please let us know.\n\n\n\n## Contribute documentation\nAny place you find the documentation to be lacking or incorrect we would be happy for someone to contribute a change. The documentation at our [public docs site](https://getzero.dev/docs/) is auto-generated using [Docusaurus](https://docusaurus.io/), and is updated automatically when any PR is merged into the main branch in one of the repositories.\n\n## Write up a bug report\nIf you run into a problem when using Zero, please feel free to create a ticket in the relevant repository. Ideally it will be clear which repo the issue should be in, but if you're not sure you can create it in the main zero repo and we will move it around if necessary. Please include as much detail as possible and any reproduction steps that might be necessary.\n\n## Request a feature\nIf there's something you think should be part of Zero but you don't see it yet, you can join the conversation in the [Zero community Slack](https://slack.getzero.dev) or [GitHub Discussions](https://github.com/commitdev/zero/discussions) and if we think it fits and it's not already covered in our roadmap we will add an issue for it.\n\n## Tell a friend\nIf you like what we're doing, please share it with your network!\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:1.12.12-alpine3.10 as builder\n\nENV GO111MODULE=on\nENV TERRAFORM_VERSION=0.12.13\n\nRUN apk add --update --no-cache build-base curl git upx && \\\n  rm -rf /var/cache/apk/*\n\nRUN apk add --update nodejs npm\n\nRUN curl -sSL \\\n  https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/aws-iam-authenticator \\\n  -o /usr/local/bin/aws-iam-authenticator\n\nRUN GO111MODULE=off go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\n\nRUN curl -sSLo /tmp/terraform.zip \"https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip\" && \\\nunzip -d /usr/local/bin/ /tmp/terraform.zip\n\nRUN chmod +x /usr/local/bin/* && \\\n  upx --lzma /usr/local/bin/*\n\n# Hydrate the dependency cache. This way, if the go.mod or go.sum files do not\n# change we can cache the depdency layer without having to reinstall them.\nWORKDIR /tmp/zero\nCOPY go.mod go.sum ./\nRUN go mod download\n\nCOPY . .\n\nRUN make build && \\\n  mv zero /usr/local/bin && \\\n  upx --lzma /usr/local/bin/zero\n\nFROM alpine:3.10\nENV \\\n  PROTOBUF_VERSION=3.6.1-r1 \\\n  GOPATH=/proto-libs\n\nRUN apk add --update bash ca-certificates git python && \\\n  apk add --update -t deps make py-pip\n\nRUN mkdir ${GOPATH}\nCOPY --from=builder /usr/local/bin /usr/local/bin\nCOPY --from=builder /go/src/github.com/grpc-ecosystem/grpc-gateway ${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway\nWORKDIR /project\n\nENTRYPOINT [\"/usr/local/bin/zero\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "LICENSE.spdx",
    "content": "SPDXVersion: SPDX-2.1\nDataLicense: CC0-1.0\nPackageName: Zero\nPackageOriginator: Commit\nPackageHomePage: https://github.com/commitdev/zero/\nPackageLicenseDeclared: MPL-2.0"
  },
  {
    "path": "Makefile",
    "content": "VERSION = 0.0.1\nBUILD ?=$(shell git rev-parse --short HEAD)\nPKG ?=github.com/commitdev/zero\nBUILD_ARGS=-v -trimpath -ldflags=all=\"-X ${PKG}/version.AppVersion=${VERSION} -X ${PKG}/version.AppBuild=${BUILD}\"\n\ndeps:\n\tgo mod download\n\ncheck:\n\tgo list -f '{{.Dir}}' ./... | grep -v /tmp/ | xargs go test -v\n\nfmt:\n\tgo fmt ./...\n\nbuild-docker-local:\n\tdocker build . -t zero:v0\n\nbuild-example-docker: clean-example\n\tmkdir -p example\n\tdocker run -v \"$(shell pwd)/example:/project\" --user $(shell id -u):$(shell id -g) zero:v0 create \"hello-world\"\n\tdocker run -v \"$(shell pwd)/example/hello-world:/project\" --user $(shell id -u):$(shell id -g) zero:v0 generate -l go\n\nbuild:\n\tgo build ${BUILD_ARGS}\n\n# Installs the CLI int your GOPATH\ninstall-go:\n\tgo build -o ${GOPATH}/bin/zero\n\n# CI Commands used on CircleCI\nci-docker-build:\n\tdocker build . -t commitdev/zero:${VERSION_TAG} -t commitdev/zero:latest\n\nci-docker-push:\n\techo \"${DOCKERHUB_PASS}\" | docker login -u commitdev --password-stdin\n\tdocker push commitdev/zero:${VERSION_TAG}\n"
  },
  {
    "path": "README.md",
    "content": "[![Tests](https://circleci.com/gh/commitdev/zero.svg?style=shield)](https://app.circleci.com/pipelines/github/commitdev/zero)\n[![Go Report Card](https://goreportcard.com/badge/commitdev/zero)](https://goreportcard.com/report/commitdev/zero)\n[![Slack](https://img.shields.io/badge/slack-join-brightgreen?logo=slack&style=social)](https://slack.getzero.dev)\n\n<p align=\"center\" width=\"100%\">\n    <img width=\"66%\" src=\"https://raw.githubusercontent.com/commitdev/zero/main/doc-site/logo.png\">\n</p>\n\n## What is Zero\n\nZero is an open source tool which makes it quick and easy for startup technical founders and developers to build everything they need to launch and grow high-quality SaaS applications faster and more cost-effectively.\n\nZero sets up everything you need so you can immediately start building your product.\n\nZero was created by [Commit](https://commit.dev).\n## Why is Zero good for startups\n\nAs a technical founder or the first technical hire at a startup, your sole focus is to build the logic for your application and get it into customers’ hands as quickly and reliably as possible. Yet you immediately face multiple hurdles before even writing the first line of code. You’re forced to make many tech trade-offs, leading to decision fatigue. You waste countless hours building boilerplate SaaS features not adding direct value to your customers. You spend precious time picking up unfamiliar tech, make wrong choices that result in costly refactoring or rebuilding in the future, and are unaware of tools and best practices that would speed up your product iteration.\n\nZero was built by a team of engineers with many years of experience in building and scaling startups. We have faced all the problems you will and want to provide a way for new startups to avoid all those pitfalls. We also want to help you learn about the tech choices we made so your team can become proficient in some of the great tools we have included. The system you get starts small but allows you to scale well into the future when you need to.\n\nEverything built by Zero is yours. After using Zero to generate your infrastructure, backend, and frontend, all the code is checked into your source control repositories and becomes the basis for your new system. We provide constant updates and new modules that you can pull in on an ongoing basis, but you can also feel free to customize as much as you like with no strings attached. If you do happen to make a change to core functionality and feel like contributing it back to the project, we'd love that too!\n\nIt's easy to get started, the only thing you'll need is an AWS account. Just enter your AWS CLI tokens or choose your existing profile during the setup process and everything is built for you automatically using infrastructure-as-code so you can see exactly what's happening and easily modify it if necessary.\n\n[Read about the day-to-day experience of using a system set up using Zero](https://getzero.dev/docs/zero/about/real-world-usage)\n\n\n## Why is Zero Reliable, Scalable, Performant, and Secure\n\nReliability: Your infrastructure will be set up in multiple availability zones making it highly available and fault tolerant. All production workloads will run with multiple instances by default, using AWS ELB and Nginx to load balance traffic. All infrastructure is represented with code using [HashiCorp Terraform][terraform] so your environments are reproducible, auditable, and easy to configure.\n\nScalability: Your services will be running in Kubernetes, with the EKS nodes running in an AWS [Auto Scaling Group][asg]. Both the application workloads and cluster size are ready to scale whenever the need arises. Your frontend assets will be stored in S3 and served from AWS' Cloudfront CDN which operates at global scale.\n\nSecurity: Properly configured access-control to resources/security groups, using secret storage systems (AWS Secret Manager, Kubernetes secrets), and following best practices provides great security out of the box. Our practices are built on top of multiple security audits and penetration tests. Automatic certificate management using [Let's Encrypt][letsencrypt], database encryption, VPN support, and more means your traffic will always be encrypted. Built-in application features like user authentication help you bullet-proof your application by using existing, tested tools rather than reinventing the wheel when it comes to features like user management and auth.\n\n\n## What do you get out of the box?\n[Read about why we made these technology choices and where they are most applicable.](https://getzero.dev/docs/zero/about/technology-choices)\n\n[Check out some resources for learning more about these technologies.](https://getzero.dev/docs/zero/reference/learning-resources)\n\n### Infrastructure\n- Fully configured infrastructure-as-code AWS environment including:\n  - VPCs per environment (staging, production) with pre-configured subnets, security groups, etc.\n  - EKS Kubernetes cluster per environment, pre-configured with helpful tools like cert-manager, external-dns, nginx-ingress-controller\n  - RDS database for your application (Postgres or MySQL)\n  - S3 buckets and Cloudfront distributions to serve your assets\n- Logging and Metrics collected automatically using either Cloudwatch or Prometheus + Grafana, Elasticsearch + Kibana\n- VPN using [Wireguard][wireguard] (Optional)\n- User management and Identity / Access Proxy using Ory [Kratos][kratos] and [Oathkeeper][oathkeeper] (Optional)\n- Tooling to make it easy to set up secure access for your dev team\n- Local/Cloud Hybrid developer environment using Telepresence (Optional)\n\n### Backend\n- Golang or Node.js example project automatically set up, Dockerized, and deployed to your new Kubernetes cluster\n- CI pipeline built with [CircleCI][circleci] or GitHub Actions. Just merge a PR and a deploy will start. Your code will be built and tested, deployed to staging, then prompt you to push to production\n- File upload / download support using signed Cloudfront URLs (Optional)\n- Email support using [SendGrid][sendgrid] or AWS SES (Optional)\n- Notification support for sending and receiving messages in your application (web, mobile, SMS, Email, etc.) (Optional) (In Progress)\n- User management integration with Kratos and Oathkeeper - No need to handle login, signup, authentication yourself (Optional)\n\n### Frontend\n- React example project automatically set up, deployed and served securely to your customers\n- CI pipeline built with CircleCI or GitHub Actions. Just merge a PR and a deploy will start. Your code will be built and tested, deployed to staging, then prompt you to push to production\n- File upload / download support using signed Cloudfront URLs (Optional)\n- User management integration with Kratos - Just style the example login / signup flow to look the way you want (Optional)\n- Static site example project using Gatsby to easily make a landing page, also set up with a CI Pipeline using CircleCI (Optional)\n\n___\n\n## Getting Started\n\n[See the getting started guide at the Zero docs site.](https://getzero.dev/docs/zero/getting-started/installation)\n\n### Building blocks of Zero\n\n### Project Definition:\nEach project is defined by this project definition file, this manifest contains your project details, and is the source of truth for the templating(`zero create`) and provision(`zero apply`) steps.\n\nSee [`zero-project.yml` reference](https://getzero.dev/docs/zero/reference/project-definition) for details.\n### Module Definition\nModule definition defines the information needed for the module to run (`zero apply`).\nAlso declares dependency used to determine the order of execution with other modules.\n\nSee [`zero-module.yml` reference](https://getzero.dev/docs/zero/reference/module-definition) for details.\n___\n\n\n## Zero Default Stack\n\n[System Architecture Diagram](https://raw.githubusercontent.com/commitdev/zero-aws-eks-stack/main/docs/architecture-overview.svg)\n\nThe core zero modules currently available are:\n| Project | URL |\n|---|---|\n| AWS Infrastructure | [https://github.com/commitdev/zero-aws-eks-stack](https://github.com/commitdev/zero-aws-eks-stack) |\n| Backend (Go)  | [https://github.com/commitdev/zero-backend-go](https://github.com/commitdev/zero-backend-go) |\n| Backend (Node.js)  | [https://github.com/commitdev/zero-backend-node](https://github.com/commitdev/zero-backend-node) |\n| Frontend (React) | [https://github.com/commitdev/zero-frontend-react](https://github.com/commitdev/zero-frontend-react) |\n| Static Site (Gatsby) | [https://github.com/commitdev/zero-static-site-gatsby](https://github.com/commitdev/zero-static-site-gatsby) |\n\n___\n\n## Contributing to Zero\n\nZero welcomes collaboration from the community; you can open new issues in our GitHub repo, Submit PRs' for bug fixes or browse through the tickets currently open to see what you can contribute too.\n\nWe use Zenhub to show us the entire project across all repositories, so if you are interested in seeing that or participating, you can can [check out our workspace](https://app.zenhub.com/workspaces/commit-zero-5da8decc7046a60001c6db44/board?repos=203630543,247773730,257676371,258369081,291818252,293942410,285931648,317656612)\n\n### Building the tool\n\n```shell\n$ git clone git@github.com:commitdev/zero.git\n$ cd zero && make build\n```\n\n### Running the tool locally\n\nTo install the CLI into your GOPATH and test it, run:\n\n```shell\n$ make install-go\n$ zero --help\n```\n\n### Releasing a new version on GitHub and Brew\n\nWe are using a tool called `goreleaser` which you can get from brew if you're on MacOS:\n`brew install goreleaser`\n\nAfter you have the tool, you can follow these steps:\n```\nexport GITHUB_TOKEN=<your token with access to write to the zero repo>\ngit tag -s -a <version number like v0.0.1> -m \"Some message about this release\"\ngit push origin <version number>\ngoreleaser release\n```\n\nThis will create a new release in GitHub and automatically collect all the commits since the last release into a changelog.\nIt will also build binaries for various OSes and attach them to the release and push them to brew.\nThe configuration for goreleaser is in [.goreleaser.yml](.goreleaser.yml)\n\n\n___\n## FAQ\n\nWhy is my deployed application not yet accessible?\n\n- It takes about 20 - 35 mins for your deployed application to be globally available through AWS CloudFront CDN.\n\n<!-- links -->\n[acw]:      https://aws.amazon.com/cloudwatch/\n[vpc]:      https://aws.amazon.com/vpc/\n[iam]:      https://aws.amazon.com/iam/\n[asg]:      https://aws.amazon.com/autoscaling/\n[zero binary]: https://github.com/commitdev/zero/releases/\n[and more]: https://github.com/commitdev/zero-aws-eks-stack/blob/master/docs/resources.md\n[terraform]: https://terraform.io\n[letsencrypt]: https://letsencrypt.org/\n[kratos]: https://www.ory.sh/kratos/\n[oathkeeper]: https://www.ory.sh/oathkeeper/\n[wireguard]: https://wireguard.com/\n[circleci]: https://circleci.com/\n[sendgrid]: https://sendgrid.com/\n[launchdarkly]: https://launchdarkly.com/\n"
  },
  {
    "path": "cmd/apply.go",
    "content": "package cmd\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/commitdev/zero/internal/apply\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar applyConfigPath string\nvar applyEnvironments []string\n\nfunc init() {\n\tapplyCmd.PersistentFlags().StringVarP(&applyConfigPath, \"config\", \"c\", constants.ZeroProjectYml, \"config path\")\n\tapplyCmd.PersistentFlags().StringSliceVarP(&applyEnvironments, \"env\", \"e\", []string{}, \"environments to set up (staging, production) - specify multiple times for multiple\")\n\n\trootCmd.AddCommand(applyCmd)\n}\n\nvar applyCmd = &cobra.Command{\n\tUse:   \"apply\",\n\tShort: \"Execute modules to create projects, infrastructure, etc.\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\trootDir, err := os.Getwd()\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\trootDir = projectconfig.RootDir\n\t\t}\n\t\tapplyErr := apply.Apply(rootDir, applyConfigPath, applyEnvironments)\n\t\tif applyErr != nil {\n\t\t\tlog.Fatal(applyErr)\n\t\t}\n\t},\n}\n"
  },
  {
    "path": "cmd/check.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/coreos/go-semver/semver\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\trootCmd.AddCommand(checkCmd)\n}\n\ntype requirement struct {\n\tname       string\n\tcommand    string\n\targs       []string\n\tminVersion string\n\tregexStr   string\n\tdocsURL    string\n}\n\ntype versionError struct {\n\terrorText string\n}\n\ntype commandError struct {\n\tCommand    string\n\tErrorText  string\n\tSuggestion string\n}\n\nfunc (e *versionError) Error() string {\n\treturn fmt.Sprintf(\"%s\", e.errorText)\n}\n\nfunc (e *commandError) Error() string {\n\treturn fmt.Sprintf(\"%s\", e.ErrorText)\n}\n\nfunc printErrors(errors []commandError) {\n\t// initialize tabwriter\n\tw := new(tabwriter.Writer)\n\n\t// minwidth, tabwidth, padding, padchar, flags\n\tw.Init(os.Stdout, 10, 12, 2, ' ', 0)\n\n\tdefer w.Flush()\n\n\tfmt.Fprintf(w, \"\\n %s\\t%s\\t%s\\t\", \"Command\", \"Error\", \"Info\")\n\tfmt.Fprintf(w, \"\\n %s\\t%s\\t%s\\t\", \"---------\", \"---------\", \"---------\")\n\n\tfor _, e := range errors {\n\t\tfmt.Fprintf(w, \"\\n%s\\t%s\\t%s\\t\", e.Command, e.ErrorText, e.Suggestion)\n\t}\n}\n\n// getSemver uses the regular expression from the requirement to parse the\n// output of a command and extract the version from it. Returns the version\n// or an error if the version string could not be parsed.\nfunc getSemver(req requirement, out []byte) (*semver.Version, error) {\n\tre := regexp.MustCompile(req.regexStr)\n\tv := re.FindStringSubmatch(string(out))\n\tif len(v) < 4 {\n\t\treturn nil, &commandError{\n\t\t\treq.command,\n\t\t\t\"Could not find version number in output\",\n\t\t\tfmt.Sprintf(\"Try running %s %s locally and checking it works.\", req.command, strings.Join(req.args, \" \")),\n\t\t}\n\t}\n\n\t// Default patch version number to 0 if it doesn't exist\n\tif v[3] == \"\" {\n\t\tv[3] = \"0\"\n\t}\n\n\tversionString := fmt.Sprintf(\"%s.%s.%s\", v[1], v[2], v[3])\n\tversion, err := semver.NewVersion(versionString)\n\tif err != nil {\n\t\treturn version, err\n\t}\n\treturn version, nil\n}\n\n// checkSemver validates that the version of a tool meets the minimum required\n// version listed in your requirement. Returns a boolean.\n// For more information on parsing semver, see semver.org\n// If your tool doesn't do full semver then you may need to add custom logic\n// to support it.\nfunc checkSemver(req requirement, actualVersion *semver.Version) bool {\n\trequiredVersion := semver.New(req.minVersion)\n\treturn actualVersion.LessThan(*requiredVersion)\n}\n\nvar checkCmd = &cobra.Command{\n\tUse:   \"check\",\n\tShort: \"Check Zero requirements\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\t// Add any new requirements to this slice.\n\t\trequired := []requirement{\n\t\t\t{\n\t\t\t\tname:       \"AWS CLI\\t\\t\",\n\t\t\t\tcommand:    \"aws\",\n\t\t\t\targs:       []string{\"--version\"},\n\t\t\t\tregexStr:   `aws-cli\\/(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)`,\n\t\t\t\tminVersion: \"1.16.0\",\n\t\t\t\tdocsURL:    \"https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"Kubectl\\t\\t\",\n\t\t\t\tcommand:    \"kubectl\",\n\t\t\t\targs:       []string{\"version\", \"--client=true\", \"--short\"},\n\t\t\t\tregexStr:   `Client Version: v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)`,\n\t\t\t\tminVersion: \"1.15.2\",\n\t\t\t\tdocsURL:    \"https://kubernetes.io/docs/tasks/tools/install-kubectl/\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"Docker\\t\\t\",\n\t\t\t\tcommand:    \"docker\",\n\t\t\t\targs:       []string{\"--version\"},\n\t\t\t\tregexStr:   `Docker version (0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*), build ([a-f0-9]{7})`,\n\t\t\t\tminVersion: \"18.0.0\",\n\t\t\t\tdocsURL:    \"https://docs.docker.com/get-docker/\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"Terraform\\t\",\n\t\t\t\tcommand:    \"terraform\",\n\t\t\t\targs:       []string{\"version\"},\n\t\t\t\tregexStr:   `Terraform v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)`,\n\t\t\t\tminVersion: \"0.13.0\",\n\t\t\t\tdocsURL:    \"https://www.terraform.io/downloads.html\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"jq\\t\\t\",\n\t\t\t\tcommand:    \"jq\",\n\t\t\t\targs:       []string{\"--version\"},\n\t\t\t\tregexStr:   `jq-(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\-?(0|[1-9]\\d*)?`,\n\t\t\t\tminVersion: \"1.5.0\",\n\t\t\t\tdocsURL:    \"https://stedolan.github.io/jq/download/\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"Git\\t\\t\",\n\t\t\t\tcommand:    \"git\",\n\t\t\t\targs:       []string{\"version\"},\n\t\t\t\tregexStr:   `^git version (0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)`,\n\t\t\t\tminVersion: \"2.17.1\",\n\t\t\t\tdocsURL:    \"https://git-scm.com/book/en/v2/Getting-Started-Installing-Git\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:       \"Wget\\t\\t\",\n\t\t\t\tcommand:    \"wget\",\n\t\t\t\targs:       []string{\"--version\"},\n\t\t\t\tregexStr:   `^GNU Wget (0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)`,\n\t\t\t\tminVersion: \"1.14.0\",\n\t\t\t\tdocsURL:    \"https://www.gnu.org/software/wget/\",\n\t\t\t},\n\t\t}\n\n\t\t// Store and errors from the commands we run.\n\t\terrors := []commandError{}\n\n\t\tfmt.Println(\"Checking Zero Requirements...\")\n\t\tfor _, r := range required {\n\t\t\tfmt.Printf(\"%s\", r.name)\n\t\t\t// In future we could parse the stderr and stdout separately, but for now it's nice to see\n\t\t\t// the full output on a failure.\n\t\t\tout, err := exec.Command(r.command, r.args...).CombinedOutput()\n\t\t\tif err != nil {\n\t\t\t\tcerr := commandError{\n\t\t\t\t\tfmt.Sprintf(\"%s %s\", r.command, strings.Join(r.args, \" \")),\n\t\t\t\t\terr.Error(),\n\t\t\t\t\tr.docsURL,\n\t\t\t\t}\n\t\t\t\terrors = append(errors, cerr)\n\t\t\t\tfmt.Printf(\"\\033[0;31mFAIL\\033[0m\\t\\t%s\\n\", \"-\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tversion, err := getSemver(r, out)\n\t\t\tif err != nil {\n\t\t\t\tcerr := commandError{\n\t\t\t\t\tr.command,\n\t\t\t\t\terr.Error(),\n\t\t\t\t\tr.docsURL,\n\t\t\t\t}\n\t\t\t\terrors = append(errors, cerr)\n\t\t\t\tfmt.Printf(\"\\033[0;31mFAIL\\033[0m\\t\\t%s\\n\", version)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif checkSemver(r, version) {\n\t\t\t\tcerr := commandError{\n\t\t\t\t\tr.command,\n\t\t\t\t\tfmt.Sprintf(\"Version does not meet required. Want: %s; Got: %s\", r.minVersion, version),\n\t\t\t\t\tr.docsURL,\n\t\t\t\t}\n\t\t\t\terrors = append(errors, cerr)\n\t\t\t\tfmt.Printf(\"\\033[0;31mFAIL\\033[0m\\t\\t%s\\n\", version)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"\\033[0;32mPASS\\033[0m\\t\\t%s\\n\", version)\n\t\t\t}\n\t\t}\n\n\t\tif len(errors) > 0 {\n\t\t\tprintErrors(errors)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tfmt.Println()\n\t},\n}\n"
  },
  {
    "path": "cmd/create.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/internal/generate\"\n\t\"github.com/commitdev/zero/internal/vcs\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tcreateConfigPath string\n\toverwriteFiles   bool\n)\n\nfunc init() {\n\tcreateCmd.PersistentFlags().StringVarP(&createConfigPath, \"config\", \"c\", constants.ZeroProjectYml, \"The project.yml file to load. The default is the one in the current directory.\")\n\tcreateCmd.PersistentFlags().BoolVarP(&overwriteFiles, \"overwrite\", \"o\", false, \"overwrite pre-existing files\")\n\n\trootCmd.AddCommand(createCmd)\n}\n\nvar createCmd = &cobra.Command{\n\tUse:   \"create\",\n\tShort: fmt.Sprintf(\"Create projects for modules and configuration specified in %s\", constants.ZeroProjectYml),\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tCreate(projectconfig.RootDir, createConfigPath)\n\t},\n}\n\nfunc Create(dir string, createConfigPath string) {\n\tif strings.Trim(createConfigPath, \" \") == \"\" {\n\t\texit.Fatal(\"config path cannot be empty!\")\n\t}\n\tconfigFilePath := path.Join(dir, createConfigPath)\n\tprojectConfig := projectconfig.LoadConfig(configFilePath)\n\n\tgenerate.Generate(*projectConfig, overwriteFiles)\n\n\tif projectConfig.ShouldPushRepositories {\n\t\tflog.Infof(\":up_arrow: Done Rendering - committing repositories to version control.\")\n\n\t\tfor _, module := range projectConfig.Modules {\n\t\t\terr, githubApiKey := projectconfig.ReadVendorCredentialsFromModule(module, \"github\")\n\t\t\tif err != nil {\n\t\t\t\tflog.Errorf(err.Error())\n\t\t\t}\n\t\t\tvcs.InitializeRepository(module.Files.Repository, githubApiKey)\n\t\t}\n\t} else {\n\t\tflog.Infof(\":up_arrow: Done Rendering - you will need to commit the created projects to version control.\")\n\t}\n\n\tflog.Infof(\":check_mark_button: Done - run zero apply to create any required infrastructure or execute any other remote commands to prepare your environments.\")\n}\n"
  },
  {
    "path": "cmd/init.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\tinitPrompts \"github.com/commitdev/zero/internal/init\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar localModulePath string\nvar registryFilePath string\n\nfunc init() {\n\tinitCmd.PersistentFlags().StringVarP(&localModulePath, \"local-module-path\", \"m\", \"github.com/commitdev\", \"local module path - for using local modules instead of downloading from github\")\n\tinitCmd.PersistentFlags().StringVarP(&registryFilePath, \"registry-file-path\", \"r\", \"https://raw.githubusercontent.com/commitdev/zero/main/registry.yaml\", \"registry file path - for using a custom list of stacks\")\n\trootCmd.AddCommand(initCmd)\n}\n\nvar initCmd = &cobra.Command{\n\tUse:   \"init\",\n\tShort: \"Create new project with provided name and initialize configuration based on user input.\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tflog.Debugf(\"Root directory is %s\", projectconfig.RootDir)\n\t\tprojectContext := initPrompts.Init(projectconfig.RootDir, localModulePath, registryFilePath)\n\t\tprojectConfigErr := projectconfig.CreateProjectConfigFile(projectconfig.RootDir, projectContext.Name, projectContext)\n\n\t\tif projectConfigErr != nil {\n\t\t\texit.Fatal(fmt.Sprintf(\" Init failed while creating the zero project config file %s\", projectConfigErr.Error()))\n\t\t} else {\n\t\t\tflog.Infof(`:tada: Done - Your project definition file has been initialized with your choices. Please review it, make any required changes and then create your project.\ncd %s\ncat zero-project.yml\nzero create`, projectContext.Name)\n\t\t}\n\t},\n}\n"
  },
  {
    "path": "cmd/version.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/commitdev/zero/version\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\trootCmd.AddCommand(versionCmd)\n}\n\nvar versionCmd = &cobra.Command{\n\tUse:   \"version\",\n\tShort: \"Print the version number of zero\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tfmt.Printf(\"version: %v\\n\", version.AppVersion)\n\t\tfmt.Printf(\"build: %v\\n\", version.AppBuild)\n\t},\n}\n"
  },
  {
    "path": "cmd/zero.go",
    "content": "package cmd\n\nimport (\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar rootCmd = &cobra.Command{\n\tUse:   \"zero\",\n\tShort: \"zero gets you to writing code quicker.\",\n\tLong:  \"Zero is an open-source CLI tool which makes it quick and easy for technical founders & developers \\nto build high-quality, reliable infrastructure to launch, grow, and scale production-ready SaaS applications faster and more cost-effectively.\\n https://getzero.dev\\n\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t},\n}\n\nfunc Execute() {\n\tif len(os.Args) > 1 {\n\t\tif err := rootCmd.Execute(); err != nil {\n\t\t\tos.Exit(1)\n\t\t}\n\t} else { // If no arguments were provided, print the usage message.\n\t\trootCmd.Help()\n\t}\n}\n"
  },
  {
    "path": "doc-site/.gitignore",
    "content": "# Dependencies\n/node_modules\n\n# Production\n/build\n\n# Generated files\n.docusaurus\n.cache-loader\n\n# Misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# to test theme color for elements locally\ndocs/about/color-test.md*\n"
  },
  {
    "path": "doc-site/README.md",
    "content": "# Website\n\nThis website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.\n\n## Installation\n\n```console\nyarn install\n```\n\n## Local Development\n\n```console\nyarn start\n```\n\nThis command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.\n\n## Build\n\n```console\nyarn build\n```\n\nThis command generates static content into the `build` directory and can be served using any static contents hosting service.\n\n## Deployment\n\n```console\nGIT_USER=<Your GitHub username> USE_SSH=true yarn deploy\n```\n\nIf you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.\n"
  },
  {
    "path": "doc-site/babel.config.js",
    "content": "module.exports = {\n  presets: [require.resolve('@docusaurus/core/lib/babel/preset')],\n};\n"
  },
  {
    "path": "doc-site/docs/about/opensource.md",
    "content": "---\ntitle: Opensource\nsidebar_label: Opensource\nsidebar_position: 2\n---\n\n\n## Contributing to Zero\n\nZero welcomes collaboration from the community; you can open new issues in our GitHub repo, Submit PRs' for bug fixes or browse through the tickets currently open to see what you can contribute too.\n\nWe use Zenhub to show us the entire project across all repositories, so if you are interested in seeing that or participating, you can can [check out our workspace](https://app.zenhub.com/workspaces/commit-zero-5da8decc7046a60001c6db44/board?repos=203630543,247773730,257676371,258369081,291818252,293942410,285931648,317656612)"
  },
  {
    "path": "doc-site/docs/about/overview.md",
    "content": "---\ntitle: Overview\nsidebar_label: Overview\nsidebar_position: 1\n---\n\n\n## What is Zero\n\nZero is an open source tool which makes it quick and easy for startup technical founders and developers to build everything they need to launch and grow high-quality SaaS applications faster and more cost-effectively.\n\nZero sets up everything you need so you can immediately start building your product.\n\nZero was created by [Commit](https://commit.dev).\n## Why is Zero good for startups\n\nAs a technical founder or the first technical hire at a startup, your sole focus is to build the logic for your application and get it into customers’ hands as quickly and reliably as possible. Yet you immediately face multiple hurdles before even writing the first line of code. You’re forced to make many tech trade-offs, leading to decision fatigue. You waste countless hours building boilerplate SaaS features not adding direct value to your customers. You spend precious time picking up unfamiliar tech, make wrong choices that result in costly refactoring or rebuilding in the future, and are unaware of tools and best practices that would speed up your product iteration.\n\nZero was built by a team of engineers with many years of experience in building and scaling startups. We have faced all the problems you will and want to provide a way for new startups to avoid all those pitfalls. We also want to help you learn about the tech choices we made so your team can become proficient in some of the great tools we have included. The system you get starts small but allows you to scale well into the future when you need to.\n\nEverything built by Zero is yours. After using Zero to generate your infrastructure, backend, and frontend, all the code is checked into your source control repositories and becomes the basis for your new system. We provide constant updates and new modules that you can pull in on an ongoing basis, but you can also feel free to customize as much as you like with no strings attached. If you do happen to make a change to core functionality and feel like contributing it back to the project, we'd love that too!\n\nIt's easy to get started, the only thing you'll need is an AWS account. Just enter your AWS CLI tokens or choose your existing profile during the setup process and everything is built for you automatically using infrastructure-as-code so you can see exactly what's happening and easily modify it if necessary.\n\n[Read about the day-to-day experience of using a system set up using Zero][real-world-usage]\n\n\n## Why is Zero Reliable, Scalable, Performant, and Secure\n\nReliability: Your infrastructure will be set up in multiple availability zones making it highly available and fault tolerant. All production workloads will run with multiple instances by default, using AWS ELB and Nginx to load balance traffic. All infrastructure is represented with code using [HashiCorp Terraform][terraform] so your environments are reproducible, auditable, and easy to configure.\n\nScalability: Your services will be running in Kubernetes, with the EKS nodes running in an AWS [Auto Scaling Group][asg]. Both the application workloads and cluster size are ready to scale whenever the need arises. Your frontend assets will be stored in S3 and served from AWS' Cloudfront CDN which operates at global scale.\n\nSecurity: Properly configured access-control to resources/security groups, using secret storage systems (AWS Secret Manager, Kubernetes secrets), and following best practices provides great security out of the box. Our practices are built on top of multiple security audits and penetration tests. Automatic certificate management using [Let's Encrypt][letsencrypt], database encryption, VPN support, and more means your traffic will always be encrypted. Built-in application features like user authentication help you bullet-proof your application by using existing, tested tools rather than reinventing the wheel when it comes to features like user management and auth.\n\n\n## What do you get out of the box?\n[Read about why we made these technology choices and where they are most applicable.][technology-choices]\n\n[Check out some resources for learning more about these technologies.][learning-resources]\n\n### [Infrastructure][docs-infra]\n- Fully configured infrastructure-as-code AWS environment including:\n  - VPCs per environment (staging, production) with pre-configured subnets, security groups, etc.\n  - EKS Kubernetes cluster per environment, pre-configured with helpful tools like cert-manager, external-dns, nginx-ingress-controller\n  - RDS database for your application (Postgres or MySQL)\n  - S3 buckets and Cloudfront distributions to serve your assets\n- Logging and Metrics collected automatically using either Cloudwatch or Prometheus + Grafana, Elasticsearch + Kibana\n- VPN using [Wireguard][wireguard] (Optional)\n- User management and Identity / Access Proxy using Ory [Kratos][kratos] and [Oathkeeper][oathkeeper] (Optional)\n- Tooling to make it easy to set up secure access for your dev team\n- Local/Cloud Hybrid developer environment using Telepresence (Optional)\n\n### [Backend][docs-backend-go] ([Go][docs-backend-go] / [Node.js][docs-backend-nodejs])\n- Golang or Node.js example project automatically set up, Dockerized, and deployed to your new Kubernetes cluster\n- CI pipeline built with [CircleCI][circleci] or GitHub Actions. Just merge a PR and a deploy will start. Your code will be built and tested, deployed to staging, then prompt you to push to production\n- File upload / download support using signed Cloudfront URLs (Optional)\n- Email support using [SendGrid][sendgrid] or AWS SES (Optional)\n- Notification support for sending and receiving messages in your application (web, mobile, SMS, Email, etc.) (Optional) (In Progress)\n- User management integration with Kratos and Oathkeeper - No need to handle login, signup, authentication yourself (Optional)\n\n### [Frontend][docs-frontend-react]\n- React example project automatically set up, deployed and served securely to your customers\n- CI pipeline built with CircleCI or GitHub Actions. Just merge a PR and a deploy will start. Your code will be built and tested, deployed to staging, then prompt you to push to production\n- File upload / download support using signed Cloudfront URLs (Optional)\n- User management integration with Kratos - Just style the example login / signup flow to look the way you want (Optional)\n- Static site example project using Gatsby to easily make a landing page, also set up with a CI Pipeline using CircleCI (Optional)\n\n<!-- internal links -->\n[real-world-usage]: ./real-world-usage\n[technology-choices]: ./technology-choices\n[learning-resources]: ../reference/learning-resources\n[docs-infra]: https://getzero.dev/docs/modules/aws-eks-stack/\n[docs-backend-go]: https://getzero.dev/docs/modules/backend-go/\n[docs-backend-nodejs]: https://getzero.dev/docs/modules/backend-nodejs/\n[docs-frontend-react]: https://getzero.dev/docs/modules/frontend-react/\n<!-- links -->\n\n\n[git]:      https://git-scm.com\n[kubectl]:  https://kubernetes.io/docs/tasks/tools/install-kubectl/\n[terraform]:https://www.terraform.io/downloads.html\n[jq]:       https://github.com/stedolan/jq\n[AWS CLI]:  https://aws.amazon.com/cli/\n[acw]:      https://aws.amazon.com/cloudwatch/\n[vpc]:      https://aws.amazon.com/vpc/\n[iam]:      https://aws.amazon.com/iam/\n[asg]:      https://aws.amazon.com/autoscaling/\n[zero binary]: https://github.com/commitdev/zero/releases/\n[Wget]: https://stackoverflow.com/questions/33886917/how-to-install-wget-in-macos\n[and more]: https://github.com/commitdev/zero-aws-eks-stack/blob/master/docs/resources.md\n[terraform]: https://terraform.io\n[letsencrypt]: https://letsencrypt.org/\n[kratos]: https://www.ory.sh/kratos/\n[oathkeeper]: https://www.ory.sh/oathkeeper/\n[wireguard]: https://wireguard.com/\n[circleci]: https://circleci.com/\n[sendgrid]: https://sendgrid.com/\n[launchdarkly]: https://launchdarkly.com/\n"
  },
  {
    "path": "doc-site/docs/about/real-world-usage.md",
    "content": "---\ntitle: Real-world Usage Scenarios\nsidebar_label: Real-world Usage\nsidebar_position: 4\n---\n\n## Developing and deploying application changes\n1. Clone your git repository.\n2. Make a branch, start working on your code.\n3. If using the Telepresence dev experience, run the `start-dev-env.sh` script to allow you to use the hybrid cloud environment as you work, to run and test your code in a realistic environment.\n3. Commit your finished code, make a PR, have it reviewed. Lightweight tests will run against your branch and prevent merging if they fail.\n4. Merge your branch to the main branch. A build will start automatically.\n5. The pipeline will build an artifact, run tests, deploy your change to staging, then wait for your input to deploy to production.\n\n## Debugging a problem on production\n1. Check the logs of your service:\n    - If using cloudwatch, log into the AWS console and go to the [Logs Insights tool](https://us-west-2.console.aws.amazon.com/cloudwatch/home#logsV2:logs-insights). Choose the log group for your production environment ending in `/application` and hit the \"Run query\" button.\n    - If using kibana, make sure you are on the VPN and open the Kibana URL in your browser. Click the \"Discover\" tab and try searching for logs based on the name of your service.\n    - Alternatively, watch the logs in realtime via the CLI using the command `kubectl logs -f -l app=<your service name>` or `stern <your service name>`\n2. Check the state of your application pods. Look for strange events or errors from the pods:\n```shell\n$ kubectl get pods\n$ kubectl get events\n$ kubectl describe pods\n```\n3. Exec into your application pod. From here you can check connectivity with `ping` or `nc`, or inspect anything else application-specific.\n```shell\n$ kubectl get pods\nNAME                             READY   STATUS    RESTARTS   AGE\nyour-service-6c5f6b56b7-2w447    1/1     Running   0          30m49s\n$ kubectl exec -it your-service-6c5f6b56b7-2w447 sh\n```\n\n\n## Adding support for a new subdomain or site\n1. Check the currently configured ingresses in your cluster:\n```shell\n$ kubectl get ingress -A\nNAMESPACE      NAME               CLASS    HOSTS                   ADDRESS                                                                   PORTS     AGE\nyour-service   your-service       <none>   api.your-service.dev         abcd1234-1234.us-west-2.elb.amazonaws.com   80, 443   130d\n```\n2. If this is for a new service entirely, make sure there is an ingress defined in the `kubernetes/` directory of your repo. If you want to add a new domain pointing to an existing service, just go into the file `kubernetes/overlays/<environment>/ingress.yml` and add a section to `spec:` and `tls:`, specifying your new domain.\n    - `spec` is where you can define the hostname, any special path rules, and which service you want traffic to be sent to\n    - if your hostname is in the `tls` section, a TLS certificate will automatically be provisioned for it using Let's Encrypt\n3. A number of things will happen once this is deployed to the cluster:\n    - Routing rules will be created to let traffic in to the cluster and send it to the service based on the hostname and path\n    - An AWS load balancer will be created if one doesn't already exist and it will be pointed to the cluster\n    - In the case of a subdomain, a DNS record will be automatically created for you\n    - A certificate will be provisioned using Let's Encrypt for the domain you specified\n"
  },
  {
    "path": "doc-site/docs/about/roadmap.md",
    "content": "---\ntitle: Roadmap\nsidebar_label: Roadmap\nsidebar_position: 5\n---\n\n:::info\nComing soon\n:::\n"
  },
  {
    "path": "doc-site/docs/about/technology-choices.md",
    "content": "---\ntitle: Technology Choices\nsidebar_label: Technology Choices\nsidebar_position: 4\n---\n\nAs we add features to Zero, we rely heavily on our years of experience with founding and growing startups, and judge tools and technologies based on the axes of:\n- Quality - Is it the best tool for the job? Will it allow a project to start small and scale big?\n- Simplicity - Is it easy to set up automatically and in a way that is easy to understand and maintain for the developers and operators of the project?\n- Price - Ideally we look for open source tools, but there are some tools we integrate with that have a cost if we see it as providing enough value to justify that cost. In this case we will often try to also include an open source alternative.\n\nWhen there are multiple technologies that we consider to be front-runners, we try to add multiple options, along with documentation and use cases to describe in which situations each tool might be the right choice for a project.\n\n### Infrastructure\n#### **Cloud Provider**\n\n[AWS](https://aws.amazon.com)\n\nAWS has been around much longer than the other large cloud providers, and while GCP or Azure may have some additional niceties in terms of developer experience, ML functionality, etc., most of the features and functionality are pretty common across all platforms. AWS is fairly low-cost, has many features, good API and CLI support, easy to set up, and with full support for infrastructure-as-code using Terraform. It can also be quite easy for startups to get credits to offset some of their initial costs.\n\nGCP and Azure support are planned but not implemented.\n\n#### **Orchestration**\n\n[Kubernetes](https://kubernetes.io)\n\nInitially developed by Google as a scaled-down, open source version of the Borg system that runs all their applications. It has become the de facto choice for container orchestration due to its huge community adoption, flexibility of deployment, and giant feature set. Between its native functionality, and various tools that we include by default, it's an incredibly powerful platform for easily deploying secure, stable, highly-available applications. It also has first-class support for managed clusters in all the major cloud providers.\n\nWe have used Kubernetes at startups in the past and have found it to be an incredible tool to allow you to start small, but scale up a huge amount. It offers a lot of native functionality that enables great developer flows, zero-downtime deploys, auto scaling, high availability, easy visibility, and more.\n\nCompared to something like EC2, there are lots of benefits to deploying containers as immutable artifacts, good integration with load balancers, fast deploys, etc.\n\nCompared to serverless, it is much easier to control, monitor, and have visibility, in addition to having much more flexibility, while giving you quite a similar experience in many ways.\n\n#### **Database**\n\n[RDS MySQL](https://aws.amazon.com/rds/mysql/) / [RDS Postgresql](https://aws.amazon.com/rds/postgresql/)\n\nRDS MySQL and Postgres give you all the benefits of these RDBMS tools without the burden of managing them yourself. Either one of these database technologies are great for most startups, and allow you to scale quite large, depending on your schema design and data set.\n\nFor large or complex data you can also potentially move to [RDS Aurora](https://aws.amazon.com/rds/aurora/) which has additional functionality which allows you to scale further.\n\n#### **Logging**\n\n[Elasticsearch + Kibana](https://aws.amazon.com/elasticsearch-service/)\n\nAWS Managed Elasticsearch with Kibana is a great tool for centralized logging from your system. We use a tool called [Fluentd](https://www.fluentd.org/) to collect all the logs from your system and send them to Elasticsearch. Then Kibana is used to run queries against that data, set up alerts, create dashboards, etc.\n\nThis is a great choice if you want more flexibility and more advanced features. It adds some complexity due to requiring an Elasticsearch cluster, but AWS' managed ES offering allows you to offload a lot of work that would typically be required.\n\n_- OR -_\n\n[Cloudwatch](https://aws.amazon.com/cloudwatch/)\n\nPotentially a cheaper and lighter-weight option, Cloudwatch is an AWS tool that supports many (but not all) of the features of Kibana.\n\nThis is a good choice if you don't have too many requirements about logging yet.\n\n#### **Monitoring**\n\n[Prometheus](https://prometheus.io/) + [Grafana](https://grafana.com/)\n\nPrometheus and Grafana run in the Kubernetes cluster. Prometheus provides metrics collection from your applications and Grafana provides graphing, dashboards, alerting, etc. on that data. It can also pull in data from many other sources.\n\nThis is a great choice if you want more flexibility and more advanced features. It adds some complexity due to requiring these tools to run in your cluster but does add a significant amount of features over Cloudwatch.\n\n_- OR -_\n\n[Cloudwatch](https://aws.amazon.com/cloudwatch/)\n\nPotentially a lighter-weight option, Cloudwatch is an AWS tool that supports many (but not all) of the features of Grafana.\n\nThis is a good choice if you don't have too many requirements about monitoring yet.\n\n#### **Access**\n[VPN (Wireguard)](https://www.wireguard.com/)\n\nIt is a good policy to keep as much of your infrastructure as possible private, exposing only what is necessary to the public internet. When we set up the Kubernetes cluster and other resources, they are all on private VPC Subnets so they can reach each other but are not otherwise reachable. When dealing with Kubernetes there is already great tooling for being able to access your application (via [`kubectl exec`](https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/)) but you may find cases where you want to access other resources as if you were on the private network yourself.\n\nTo this end we are using Wireguard for VPN to allow your personal computer to act as if it were on the private network, having access to the things running inside your Kubernetes cluster, and within AWS.\n\nWireguard is a great secure, light-weight tool that runs well on Kubernetes, so it doesn't require us to set up any other infrastructure. You may be able to get by without it if you are comfortable with the Kubernetes tooling, but it would be very useful if you are using something like Kibana above, so you can access it directly through your web browser.\n\n### Application\n\n#### **User Management**\n[Ory Kratos](https://github.com/ory/kratos)\n\nKratos is an open-source identity and user management tool. It can take the place of something like Auth0, or the user management that many startups end up writing themselves. By delegating this work to Kratos, you can save time and effort that you would otherwise have to put in to build a stable, secure user management system.\n\nIf you are building a system that will require authenticated users, it may make sense to use Kratos with Oathkeeper (below) to save a lot of effort that may otherwise be spent building user management and auth features that have been built a million times before.\n\n#### **Authentication / Authorization**\n[Ory Oathkeeper](https://github.com/ory/oathkeeper)\n\nOathkeeper is an open-source Identity & Access Proxy that can securely act as a gateway to your system, preventing unauthorized access. It uses Oauth & JWT to log a user in and maintain a session, and proxies traffic to your services for users with valid sessions.\n\nThis allows you to save time and effort by not having to write auth and session management code. If a specific header exists, the specified user is logged in. That's it.\n\n#### **Dev Experience**\n[Telepresence](https://www.telepresence.io/)\n\nTelepresence is a great tool for creating a fast and effective \"hybrid cloud/local\" developer experience using Kubernetes. You can have your services, databases, and other dependencies running in the cloud, and then run the service you are working on locally, and have it override the one running in the cloud. In this way you can use all the same IDEs, debuggers, and local tools you usually do, but have your code running in a realistic environment much more like staging or production, complete with all the dependencies that can make local dev a pain.\n\nIt's a great tool even when starting a project to quickly and easily test things in a realistic environment but can also really shine if you are working in a system that has many dependencies (multiple state stores, microservices, etc.)\n\n### Backend\nWhile we offer ways to bootstrap your backend application in various languages, you can also choose to use whichever language you like and just make use of the infrastructure we set up, or use your custom backend with a bootstrapped frontend.\n\nThe backend application will have a build and deploy pipeline that will build a docker image and push it to AWS' ECR image repository. The image will be used to deploy containers in the Kubernetes cluster.\n\n**Language**\n\nWhichever language you choose will be automatically set up, Dockerized, and deployed into the Kubernetes cluster.\n\n[Golang](https://golang.org/)\n\nGo is a great language for backend application development, especially in a microservices environment. It is a typed, compiled language with powerful support for parallel processing, and can create small, self-contained binaries for any operating system. It has a huge community, and is used by some very significant open source projects such as Kubernetes, and the HashiCorp tools.\n\nBeing a compiled language may slightly raise the barrier to entry for newer developers. Lack of generic types and traditional object-oriented features, and preference for code generation may deter more experienced but unfamiliar developers.\n\n_- OR -_\n\n[Node.js](https://nodejs.org/en/)\n\nNode.js is an extremely popular language for backend development. It has a huge community and tons of libraries available, since it is based on JavaScript. It can also be beneficial to use JavaScript for both the backend and frontend, in case developers are not familiar with other backend languages.\n\nBeing a [single-threaded](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/), interpreted language, Node may not be ideal for high-performance applications.\n\n\n### Frontend\nWhile we offer ways to bootstrap your frontend application in various languages, you can also choose to use whichever language you like and just make use of the infrastructure we set up, or use your custom frontend with a bootstrapped backend.\n\nThe frontend application will have a build and deploy pipeline that will build an artifact that will be uploaded to S3, and then hosted through AWS' Cloudfront CDN. This allows your application to be stable and fast from all over the world.\n\n**Language**\n\n[React.js](https://reactjs.org/)\n\nOne of the most popular modern libraries for building dynamic frontend applications. React is hugely popular, powerful, and is an obvious choice for most frontends.\n\n\n### CI\n\nWe advocate for (and configure by default) build pipelines for deploying your application. As opposed to deploying manually via the command line which may work fine for a single developer, when you are working on a team there are huge benefits to making GitHub your source of truth, and relying on your version control system to trigger builds and deployments. Our preferred pipeline looks something like this:\n```\nmerge to main branch -> build artifacts -> run tests -> deploy to staging -> ask for user input -> deploy to production\n```\nBut you can also configure it to fit your desired workflow.\n\n[CircleCI](https://circleci.com/)\n\nCircleCI is a powerful tool for creating CI/CD pipelines. They have a free tier which is fully featured, with paid plans that add more concurrency and speed for builds. It requires a bit more setup at the beginning, as you need to create an account with them, but it does have some additional features compared to GitHub Actions.\n\n_- OR -_\n\n[GitHub Actions](https://github.com/features/actions)\n\nGitHub Actions is newer than CircleCI, so it's features are a bit more limited, but the fact that it is integrated with GitHub makes it much easier to get started with. Also similar, it has a free tier with paid versions that add more concurrency. GHA would be useful for projects that don't require a lot of advanced features, visualization, etc.\n\n### Fundamentals\n**[Twelve-Factor App](https://12factor.net/)**\n\nThe Twelve-Factor App is a methodology for building Software-as-a-Service that encourages building applications in a way that inherently makes them more stable, secure, failure-safe, easier to measure, and easier to reason about. As much as possible we try to stick with these principles when building Zero, as we have seen it to be a very effective way to build scalable, manageable applications.\n\n**[CNCF](https://www.cncf.io/)**\n\nThe Cloud Native Computing Foundation hosts and promotes a bunch of great open source software, including Kubernetes, Prometheus, Helm, Fluentd, and Envoy. Their principles and the design of many of the CNCF projects really resonate with the team behind Zero, and we try to make use of CNCF-backed tools whenever possible.\n"
  },
  {
    "path": "doc-site/docs/concepts/core-concepts.md",
    "content": "---\ntitle: Core Concepts\nsidebar_label: Core Concepts\nsidebar_position: 1\n---\n\n## Project\nA project defines a set of **modules** to use, along with a full set of config parameters for each module which were entered by the user during the `zero init` step. These config options are stored in `zero-project.yml`.\n\nThe project manifest (`zero-project.yml`) is the source of truth for the commands `create` and `apply`. It determines from where to fetch the modules, the execution order of modules, whether it will push your project to version control, module configuration, and other project information. Both staging and production environments are provisioned using the same manifest to ensure the environments are reproducible and controlled.\n\n## Module\nA module is a useful bundle of code and/or resources that can be templated out during the `create` step, then executes a provisioning flow. This could be templating out terraform infrastructure as code then provisioning the resources, or creating a backend application, making API calls to set up a build pipeline, and then deploying it.\n\nA module is defined by the **module manifest** (`zero-module.yml`) in its root folder. It contains all the parameters required to render the templated files (during `zero create`) and execute any provisioning steps, and declares it's requirements and the commands to execute during provisioning (`zero apply`).\n\nModules can declare their dependencies, for example a backend that will be deployed can declare its dependency on the infrastructure repository so that the infrastructure will already exist by the time we want to deploy to it.\n"
  },
  {
    "path": "doc-site/docs/concepts/project-life-cycle.md",
    "content": "---\ntitle: Project Life Cycle\nsidebar_label: Project Life Cycle\nsidebar_position: 2\n---\n\n## zero init\nThe goal of the `init` step is to create the project manifest (`zero-project.yml`).\n\n`zero init` will fetch each **module** defined in `zero-project.yml` from their remote repository, and prompt the user through a series of questions to fill in parameters required by each module. In this phase, the module definition will be parsed and provide defaults, options, and extra context to guide users through filling in their project details.\n\n:::note\nIt's recommended to review the `zero-project.yml` and make adjustments as needed before running `zero create` and `zero apply`.\n:::\n\n## zero create\n`zero create` is run in the same folder as `zero-project.yml`. It will template out each module specified in the project manifest as the basis of your repositories, then push them to your version control repository (Github).\n\nDuring the `create` step, Zero will also conditionally include or exclude certain sets of files, as defined by each module. For example, it will not scaffold the authentication examples if you opted not to use this feature.\n\n## zero apply\n`zero apply` is the provisioning step that starts to make real-world changes. By default, it runs a command defined by the module to check for any dependencies, and then runs a command to actually apply any changes.\n\n### Check\n`check` is run for all the modules in your project before attempting to do the `run` step, so if a dependent's `check` fails it will not start the actual provisioning for any of the modules. This is useful to check for missing binaries or API token permissions before starting to apply any changes.\n\n### Apply\nBy default, the run step is to execute `make` in the root of the module, but that can be overridden in the module definition. Run should be the one-time setup commands that allow the module to function.\nFor example, in the infrastructure repository, this would be to **run terraform**, and for the backend repository, this could be to  **make API calls to CircleCI to set up your build pipeline**.\n"
  },
  {
    "path": "doc-site/docs/getting-started/installation.md",
    "content": "---\ntitle: Installation\nsidebar_label: Installation\nsidebar_position: 1\n---\n\n## How to Install and Configure Zero\n\nThere are multiple ways to install Zero:\n\n- Install Zero using your systems package manager.\n\n```shell\n# MacOS\nbrew tap commitdev/zero\nbrew install zero\n```\n\n- Install Zero by downloading the binary.\n\nDownload the latest [Zero binary](https://github.com/commitdev/zero/releases) for your systems architecture. Unzip your downloaded package and copy the Zero binary to the desired location and add it to your system PATH.\n\nZero currently supports:\n\n| System | Support|  Package Manager |\n|---------|:-----:|:------:|\n| MacOS   |  ✅   | `brew` |\n| Linux   |  ✅   |   `deb, rpm, apk`  |\n| Windows |  ❌   |   n/a  |\n"
  },
  {
    "path": "doc-site/docs/getting-started/prerequisites.md",
    "content": "---\ntitle: Prerequisites\nsidebar_label: Prerequisites\nsidebar_position: 2\n---\n\n\nUsing Zero to spin up your infrastructure and application is easy and straightforward. Using just a few commands, you can configure and deploy your very own scalable, high-performance, production-ready infrastructure.\n\nA few caveats before getting started:\n\n- For Zero to provision resources, you will need to be [authenticated with the AWS CLI tool ](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-methods).\n\n- It is recommended practice to [create a GitHub org](https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/creating-a-new-organization-from-scratch) where your code is going to live. If you choose, after creating your codebases, Zero will automatically create repositories and check in your code for you. You will need to [create a Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) to enable this.\n\n<details>\n  <summary>If using CircleCI as your build pipeline...</summary>\n  <ul>\n    <li>\n    Grant <a href=\"https://github.com/settings/connections/applications/78a2ba87f071c28e65bb\">CircleCi Organization access</a> to your repositories to allow pulling the code during the build pipeline.\n    </li>\n    <li>\n    You will need to <a href=\"https://circleci.com/docs/2.0/managing-api-tokens/\">create a CircleCi access token</a> and enter it during the setup process; you should store your generated tokens securely.\n    </li>\n    <li>\n    For your CI build to work, you need to opt into the use of third-party orbs. You can find this in your CircleCi Org Setting &gt; Security &gt; Allow Uncertified Orbs.\n    </li>\n  </ul>\n</details>\n\n\n### `zero check`\nIn order to use Zero, run the `zero check` command on your system to find out which other tools / dependencies you might need to install.\n\n<img src=\"/img/docs/zero-check.png\" width=\"400\" />\n\n[AWS CLI], [Kubectl], [Terraform], [jq], [Git], [Wget]\n\nYou need to [register a new domain](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html) / [host a registered domain](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/MigratingDNS.html) you will use to access your infrastructure on [Amazon Route 53](https://aws.amazon.com/route53/).\n\n:::tip\n We recommended you have two domains - one for staging and another for production. For example, mydomain.com and mydomain-staging.com. This will lead to environments that are more similar, rather than trying to use a subdomain like staging.mydomain.com for staging which may cause issues in your app later on.\n:::\n\n[AWS CLI]:  https://aws.amazon.com/cli/\n[git]:      https://git-scm.com\n[kubectl]:  https://kubernetes.io/docs/tasks/tools/install-kubectl/\n[terraform]:https://www.terraform.io/downloads.html\n[jq]:       https://github.com/stedolan/jq\n[Wget]: https://stackoverflow.com/questions/33886917/how-to-install-wget-in-macos\n"
  },
  {
    "path": "doc-site/docs/getting-started/zero-apply.md",
    "content": "---\ntitle: zero apply\nsidebar_label: zero apply\nsidebar_position: 5\n---\n\nThe `zero apply` command takes the templated modules generated based on your input and spins up a scalable & performant infrastructure for you!\n\n:::note\nThis can take 20 minutes or more depending on your choices, as it must wait for all the provisioned infrastructure to be created\n:::\n\n```shell\n$ zero apply\n\n# Sample Output\nChoose the environments to apply. This will create infrastructure, CI pipelines, etc.\nAt this point, real things will be generated that may cost money!\nOnly a single environment may be suitable for an initial test, but for a real system we suggest setting up both staging and production environments.\n✔ Production\n🎉  Bootstrapping project zero-init. Please use the zero-project.yml file to modify the project as needed.\nCloud provider: AWS\nRuntime platform: Kubernetes\nInfrastructure executor: Terraform\n\n...\n...\n\n\n✅  Done.\nYour projects and infrastructure have been successfully created.  Here are some useful links and commands to get you started:\nzero-aws-eks-stack:\n- Repository URL: github.com/myapp-org/infrastructure\n- To see your kubernetes clusters, run: 'kubectl config get-contexts'\n- To switch to a cluster, use the following commands:\n- for production use: kubectl config use-context arn:aws:eks:us-west-2:123456789:cluster/myapp-infra-production-us-west-2\n\n- To inspect the selected cluster, run 'kubectl get node,service,deployment,pods'\nzero-frontend-react:\n- Repository URL: github.com/myapp-org/frontend\n- Deployment Pipeline URL: https://app.circleci.com/pipelines/github/myapp-org/frontend\n- Production Landing Page: app.commitzero.com\n\nzero-backend-go:\n- Repository URL: github.com/myapp-org/backend-service\n- Deployment Pipeline URL: https://app.circleci.com/pipelines/github/myapp-org/backend-service\n- Production API: api.commitzero.com\n```\n\n***Your stack is now up and running, follow the links in your terminal to visit your application 🎉***\n"
  },
  {
    "path": "doc-site/docs/getting-started/zero-create.md",
    "content": "---\ntitle: zero create\nsidebar_label: zero create\nsidebar_position: 4\n---\n\nThe `zero create` command renders the infrastructure modules you've configured into your project folder and pushes your code to GitHub.\n\n```shell\n# Template the selected modules and configuration specified in zero-project.yml and push to the repository.\n$ cd zero-init   # change your working dir to YOUR_PROJECT_NAME\n$ zero create\n\n## Sample Output\n🕰  Fetching Modules\n📝  Rendering Modules\n  Finished templating : backend-service/.circleci/README.md\n✅  Finished templating : backend-service/.circleci/config.yml\n✅  Finished templating : backend-service/.gitignore\n...\n...\n✅  Finished templating : infrastructure/terraform/modules/vpc/versions.tf\n⬆  Done Rendering - committing repositories to version control.\n✅  Repository created: github.com/myapp-org/infrastructure\n✅  Repository created: github.com/myapp-org/backend-service\n✅  Repository created: github.com/myapp-org/frontend\n✅  Done - run zero apply to create any required infrastructure or execute any other remote commands to prepare your environments.\n\n\n```\n\nAfter this step you will be able to examine the created repositories before proceeding to `zero apply`. If you chose not to have zero create a repository for you, you can still use the `zero apply` command to create the infrastructure but you will need to check these repositories into your version control system of choice.\n"
  },
  {
    "path": "doc-site/docs/getting-started/zero-init.md",
    "content": "---\ntitle: zero init\nsidebar_label: zero init\nsidebar_position: 3\n---\n\n\nThe `zero init` command creates a new project and outputs an infrastructure configuration file with user input prompted responses into a file.  -> 📁 `YOUR_PROJECT_NAME/zero-project.yml`\n\n```shell\n# To create and customize a new project you run\n$ zero init\n\n## Sample project initialization\n✔ Project Name: myapp-infra\n🎉  Initializing project\n✔ EKS + Go + React + Gatsby\n✔ Should the created projects be checked into github automatically? (y/n): y\n✔ What's the root of the github org to create repositories in?: github.com/myapp-org\n✔ Existing AWS Profiles\n✔ default\n\nGithub personal access token: used for creating repositories for your project\nRequires the following permissions: [repo::public_repo, admin::orgread:org]\nThe token can be created at https://github.com/settings/tokens\n✔ Github Personal Access Token with access to the above organization: <MY_GITHUB_ORG_ACCESS_TOKEN>\n\nCircleCI api token: used for setting up CI/CD for your project\nThe token can be created at https://app.circleci.com/settings/user/tokens\n✔ Circleci api key for CI/CD: <MY_CIRCLE_CI_ACCESS_TOKEN>\n✔ us-west-2\n✔ Production Root Host Name (e.g. mydomain.com) - this must be the root of the chosen domain, not a subdomain.: commitzero.com\n✔ Production Frontend Host Name (e.g. app.): app.\n✔ Production Backend Host Name (e.g. api.): api.\n✔ Staging Root Host Name (e.g. mydomain-staging.com) - this must be the root of the chosen domain, not a subdomain.: commitzero-stage.com\n✔ Staging Frontend Host Name (e.g. app.): app.\n✔ Staging Backend Host Name (e.g. api.): api.\n✔ What do you want to call the zero-aws-eks-stack project?: infrastructure\n✔ What do you want to call the zero-backend-go project?: backend-service\n✔ What do you want to call the zero-frontend-react project?: frontend\n\n```\n\nAfter this step you will be able to examine the `zero-project.yml` file to ensure your settings are correct before proceeding to `zero create`.\n"
  },
  {
    "path": "doc-site/docs/reference/learning-resources.md",
    "content": "---\ntitle: Learning Resources\nsidebar_label: Learning Resources\nsidebar_position: 3\n---\n\n\n### AWS\n- [Getting started with AWS](https://aws.amazon.com/getting-started/)\n\n### Kubernetes\n- [Kubernetes Basics](https://kubernetes.io/docs/tutorials/kubernetes-basics/)\n- [Kubernetes Training and Certification](https://kubernetes.io/training/)\n\n### Terraform\n- [Getting started with Terraform in AWS](https://learn.hashicorp.com/collections/terraform/aws-get-started)\n\n### Golang\n- [A Tour of Go](https://tour.golang.org)\n\n### Node.js\n- [Getting started with Node.js](https://nodejs.org/en/docs/guides/getting-started-guide/)\n- [Getting started with Express](https://expressjs.com/en/starter/installing.html)\n- [Getting started with Apollo GraphQL](https://www.apollographql.com/docs/apollo-server/getting-started/)\n\n### React\n- [Getting started with React](https://reactjs.org/docs/getting-started.html)\n"
  },
  {
    "path": "doc-site/docs/reference/module-definition.md",
    "content": "---\ntitle: Module Definition\nsidebar_label: Module Definition\nsidebar_position: 1\n---\n\n### `zero-module.yml`\nThis file is the definition of a Zero module. It contains a list of all the required parameters to be able to prompt a user for choices during `zero init`, information about how to template the contents of the module during `zero create`, and the information needed for the module to run (`zero apply`).\nIt also declares the module's dependencies to determine the order of execution in relation to other modules.\n\n| Parameters    | type                 | Description                                      |\n|---------------|----------------------|--------------------------------------------------|\n| `name`        | `string`             | Name of module                                   |\n| `description` | `string`             | Description of the module                        |\n| `template`    | `template`           | default settings for templating out the module   |\n| `author`      | `string`             | Author of the module                             |\n| `icon`        | `string`             | Path to logo image                               |\n| `parameters`  | `list(Parameter)`    | Parameters to prompt users                       |\n| `commands`    | `Commands`           | Commands to use instead of makefile defaults     |\n| `zeroVersion` | string([go-semver])  | Zero binary versions it's compatible with        |\n\n\n### Commands\nCommands are the lifecycle of `zero apply`, it will run all modules' `check` phase, then once satisfied, run in sequence the `apply` phase, then if successful run the `summary` phase.\n\n| Parameters | Type     | Default        | Description                                                              |\n|------------|----------|----------------|--------------------------------------------------------------------------|\n| `check`    | `string` | `make check`   | Command to check module requirements. check is satisfied if exit code is 0 eg: `sh check-token.sh`, `zero apply` will check all modules before executing |\n| `apply`    | `string` | `make`         | Command to execute the project provisioning.                             |\n| `summary`  | `string` | `make summary` | Command to summarize to users the module's output and next steps.        |\n\n#### Template\nControl how module templates will be parsed during the `zero create` command.\n\n| Parameters   | Type      | Description                                                           |\n|--------------|-----------|-----------------------------------------------------------------------|\n| `strictMode` | `boolean` | whether strict mode is enabled                                        |\n| `delimiters` | `tuple`   | A tuple of open delimiter and ending delimiter eg: `<%` and `%>`      |\n| `inputDir`   | `string`  | Folder to template from the module, becomes the module root for users |\n| `outputDir`  | `string`  | local directory name for the module, gets commited to version control |\n\n### Condition (module)\nModule conditions are considered during the templating phase (`zero create`), based on parameters supplied from the project definition.\nModules can decide to have specific files or directories excluded from the user's project.\nFor example if the user picks `userAuth: no`, we can exclude all the auth resources via templating.\n\n| Parameters   | Type           | Description                                                                                                                   |\n|--------------|----------------|-------------------------------------------------------------------------------------------------------------------------------|\n| `action`     | `enum(string)` | type of condition, currently supports [`ignoreFile`]                                                                          |\n| `matchField` | `string`       | Allows us to check the contents of another parameter's value                                                                  |\n| `WhenValue`  | `string`       | Matches for this value to satisfy the condition                                                                               |\n| `data`       | `list(string)` | Supply extra data for the condition action. `ignoreFile`: list of paths (file or directory) to omit from the rendered project |\n\n### Parameter\nParameter defines how the user will be prompted during the interview phase of `zero init`.\nThere are multiple ways of obtaining the value for each parameter.\nParameters may have `Conditions` that must be fulfilled, otherwise it skips the field entirely.\n\nThe precedence for different types of parameter prompts are as follow.\n1. `execute`: If this parameter is supplied, the command will be executed and the value will be recorded\n2. `type`: zero-defined special ways of obtaining values (for example `AWSProfilePicker` which will set 2 values to the map)\n3. `value`: directly assigns a static value to a parameter\n4. `prompt`: requires users to select an option OR input a string\nNote: `default` specifies the value that will appear initially for that prompt, but the user could still choose a new string or entirely erase it\n\n| Parameters            | Type              | Description                                                                                                               |\n|-----------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------|\n| `field`               | `string`          | Key to store result for project definition                                                                                |\n| `label`               | `string`          | Displayed name for the prompt                                                                                             |\n| `options`             | `map`             | A map of `value: display name` pairs for users to pick from                                                               |\n| `default`             | `string`          | Defaults to this value during prompt                                                                                      |\n| `value`               | `string`          | Skips prompt entirely when set                                                                                            |\n| `info`                | `string`          | Displays during prompt as extra information at the top of the screen guiding user's input                                 |\n| `fieldValidation`     | `Validation`      | Validations for the prompt value                                                                                          |\n| `type`                | `enum(string)`    | Built-in custom prompts: currently supports [`AWSProfilePicker`]                                                          |\n| `execute`             | `string`          | Executes commands and takes stdout as prompt result                                                                       |\n| `omitFromProjectFile` | `bool`            | Field is skipped from adding to project definition                                                                        |\n| `conditions`          | `list(Condition)` | Conditions for prompt to run. If supplied, all conditions must pass. See below.                                           |\n| `envVarName`          | `string`          | During `zero apply` parameters are available as env vars. Defaults to field name but can be overwritten with `envVarName` |\n\n### Condition (parameter)\nParameter conditions are considered while running user prompts. Prompts are\nexecuted in order of the yml, and will be skipped if conditions are not satisfied.\nFor example if a user decides to not use circleCI, a condition can be used to skip the circleCI_api_key prompt.\n\n| Parameters   | Type           | Description                                                       |\n|--------------|----------------|-------------------------------------------------------------------|\n| `action`     | `enum(string)` | type of condition, currently supports [`KeyMatchCondition`]       |\n| `matchField` | `string`       | The name of the parameter to check the value of                   |\n| `whenValue`  | `string`       | The exact value to match                                          |\n| `elseValue`  | `string`       | The value that will be set for this parameter if the condition match fails. Otherwise the value will be set to an empty string.  |\n| `data`       | `list(string)` | Supply extra data for condition to run                            |\n\n### Validation\nAllows forcing the user to adhere to a certain format of value for a parameter, defined as a regular expression.\n\n| Parameters     | type           | Description                         |\n|----------------|----------------|-------------------------------------|\n| `type`         | `enum(string)` | Currently supports [[regex](https://github.com/google/re2/wiki/Syntax)] |\n| `value`        | `string`       | Regular expression string           |\n| `errorMessage` | `string`       | Error message when validation fails |\n\n[go-semver]: https://github.com/hashicorp/go-version/blob/master/README.md\n"
  },
  {
    "path": "doc-site/docs/reference/project-definition.md",
    "content": "---\ntitle: Project Definition\nsidebar_label: Project Definition\nsidebar_position: 1\n---\n\n### `zero-project.yml`\nEach project is defined by this file. This manifest reflects all the options a user chose during the `zero init` step. It defines which modules are part of the project, each of their parameters, and is the source of truth for the templating (`zero create`) and provision (`zero apply`) steps. \n\n_Note: This file contains credentials, so make sure it is not shared with others._\n\n| Parameters               | Type         | Description                                    |\n|--------------------------|--------------|------------------------------------------------|\n| `name`                   | string       | name of the project                            |\n| `shouldPushRepositories` | boolean      | whether to push the modules to version control |\n| `modules`                | map(modules) | a map containing modules of your project       |\n\n\n### Modules\n| Parameters   | Type            | Description                                                             |\n|--------------|-----------------|-------------------------------------------------------------------------|\n| `parameters` | map(string)     | key-value map of all the parameters to run the module                   |\n| `files`      | File            | Stores information such as source-module location and destination       |\n| `dependsOn`  | list(string)    | a list of dependencies that should be fulfilled before this module      |\n| `conditions` | list(condition) | conditions to apply while templating out the module based on parameters |\n\n### Condition\n| Parameters   | Type         | Description                                                                                                                                           |\n|--------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `action`     | enum(string) | type of condition, currently supports [`ignoreFile`]                                                                                                  |\n| `matchField` | string       | Allows you to condition prompt based on another parameter's value                                                                                     |\n| `WhenValue`  | string       | Matches for this value to satisfy the condition                                                                                                       |\n| `data`       | list(string) | Supply extra data for condition to run   `ignoreFile`: provide list of paths (file or directory path) to omit from module when condition is satisfied |\n"
  },
  {
    "path": "doc-site/docs/reference/working-on-zero.md",
    "content": "---\ntitle: Working on Zero\nsidebar_label: Working on Zero\nsidebar_position: 3\n---\n\n### Building the tool\n\n```shell\n$ git clone git@github.com:commitdev/zero.git\n$ cd zero && make build\n```\n\n### Releasing a new version on GitHub and Brew\n\nWe are using a tool called `goreleaser` which you can get from brew if you're on MacOS:\n`brew install goreleaser`\n\nAfter you have the tool, you can follow these steps:\n```\nexport GITHUB_TOKEN=<your token with access to write to the zero repo>\ngit tag -s -a <version number like v0.0.1> -m \"Some message about this release\"\ngit push origin <version number>\ngoreleaser release\n```"
  },
  {
    "path": "doc-site/docusaurus.config.js",
    "content": "/** @type {import('@docusaurus/types').DocusaurusConfig} */\nconst { stylesheets, misc } = require('@commitdev/zero-doc-site-common-elements');\n\nconst siteUrl = process.env.BUILD_DOMAIN ? `https://${process.env.BUILD_DOMAIN}` : 'https://staging.getzero.dev';\nconst baseUrl = '/';\nconst repositoryName = 'zero';\n\nmodule.exports = {\n  title: 'Zero',\n  tagline: 'Opinionated infrastructure to take you from idea to production on day one',\n  url: siteUrl,\n  baseUrl,\n  ...misc(),\n  projectName: repositoryName,\n  themeConfig: {\n    colorMode: {\n      defaultMode: 'dark',\n    },\n    navbar: {\n      logo: {\n        alt: 'Zero Logo',\n        src: 'img/zero.svg',\n      },\n      items: [\n        {\n          to: '/docs/zero/about/overview',\n          label: 'Docs',\n          className: 'header-docs-link header-logo-24',\n          position: 'right'\n        },\n        {\n          href: 'https://slack.getzero.dev',\n          label: 'Slack',\n          className: 'header-slack-link header-logo-24',\n          position: 'right',\n        },\n        {\n          href: 'https://github.com/commitdev/zero',\n          label: 'Github',\n          className: 'header-github-link header-logo-24',\n          position: 'right',\n        },\n      ],\n    },\n    footer: {\n      links: [\n        {\n          items: [\n            {\n              to: '/docs/zero/about/overview',\n              label: 'Docs',\n              className: 'header-docs-link header-logo-24',\n              position: 'right'\n            },\n            {\n              href: 'https://slack.getzero.dev',\n              label: 'Slack',\n              className: 'header-slack-link header-logo-24',\n              position: 'right',\n            },\n            {\n              href: 'https://github.com/commitdev/zero',\n              label: 'Github',\n              className: 'header-github-link header-logo-24',\n              position: 'right',\n            },\n          ],\n        },\n      ],\n    },\n  },\n  presets: [\n    [\n      '@docusaurus/preset-classic',\n      {\n        docs: {\n          sidebarPath: require.resolve('./sidebars.js'),\n          path: 'docs',\n          routeBasePath: 'docs/zero/',\n          include: ['**/*.md', '**/*.mdx'],\n          editUrl: 'https://github.com/commitdev/zero/blob/main/doc-site/',\n        },\n        theme: {\n          customCss: require.resolve('./src/css/custom.css'),\n        },\n        gtag: {\n          trackingID: 'G-6FN66NMDES',\n        },\n        debug: true,\n      },\n    ],\n  ],\n  plugins: [\n    'docusaurus-plugin-sass'\n  ],\n  stylesheets: stylesheets(),\n};\n"
  },
  {
    "path": "doc-site/package.json",
    "content": "{\n  \"name\": \"doc-site\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"docusaurus\": \"docusaurus\",\n    \"start\": \"docusaurus start\",\n    \"build\": \"docusaurus build\",\n    \"swizzle\": \"docusaurus swizzle\",\n    \"deploy\": \"docusaurus deploy\",\n    \"clear\": \"docusaurus clear\",\n    \"serve\": \"docusaurus serve\",\n    \"write-translations\": \"docusaurus write-translations\",\n    \"write-heading-ids\": \"docusaurus write-heading-ids\"\n  },\n  \"dependencies\": {\n    \"@commitdev/zero-doc-site-common-elements\": \"0.0.7\",\n    \"@docusaurus/core\": \"2.2.0\",\n    \"@docusaurus/preset-classic\": \"2.0.0-beta.3\",\n    \"@mdx-js/react\": \"^1.6.21\",\n    \"@svgr/webpack\": \"^5.5.0\",\n    \"clsx\": \"^1.1.1\",\n    \"docusaurus-plugin-sass\": \"^0.2.1\",\n    \"file-loader\": \"^6.2.0\",\n    \"react\": \"^17.0.1\",\n    \"react-dom\": \"^17.0.1\",\n    \"sass\": \"^1.35.1\",\n    \"url-loader\": \"^4.1.1\"\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.5%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  }\n}\n"
  },
  {
    "path": "doc-site/sidebars.js",
    "content": "const config = require('./docusaurus.config');\nconst { sidebarsNavModules } = require('@commitdev/zero-doc-site-common-elements');\n\nmodule.exports = {\n  zero: [\n    {\n      \"About\": [{\n        type: 'autogenerated',\n        dirName: 'about',\n      }],\n      \"Getting started\": [{\n        type: 'autogenerated',\n        dirName: 'getting-started',\n      }],\n      \"Concepts\": [{\n        type: 'autogenerated',\n        dirName: 'concepts',\n      }],\n      \"Reference\": [{\n        type: 'autogenerated',\n        dirName: 'reference',\n      }],\n    },\n    sidebarsNavModules(config),\n  ]\n}\n\n"
  },
  {
    "path": "doc-site/src/components/HomepageFeatures.js",
    "content": "import React from 'react';\nimport clsx from 'clsx';\nimport styles from './HomepageFeatures.module.scss';\n\nconst FeatureList = [\n  {\n    title: 'Reliable',\n    Svg: require('../../static/img/icons/attr-reliable.svg').default,\n    description: (\n      <>\n        Fault-tolerant infrastructure. Production workloads will be \n        highly available, with traffic load balanced to multiple \n        instances of your application. All the infrastructure is \n        represented with code to be reproducible and easy to configure.\n      </>\n    ),\n  },\n  {\n    title: 'Scalable',\n    Svg: require('../../static/img/icons/attr-scalable.svg').default,\n    description: (\n      <>\n        Your system will scale automatically based on your application’s \n        needs. For frontend assets, using a CDN will ensure availability \n        at global scale.\n      </>\n    ),\n  },\n  {\n    title: 'Secure',\n    Svg: require('../../static/img/icons/attr-secure.svg').default,\n    description: (\n      <>\n        All your systems will follow security best practices backed up \n        by multiple security audits and penetration tests, and will be \n        properly configured to limit access to private networks, \n        secrets, and data. Bullet-proof your application by default \n        using existing, tested tools.\n      </>\n    ),\n  },\n];\n\nconst Feature = ({Svg, title, description}) => (\n  <div className={clsx('col col--5') + \" feature\"}>\n    <div className=\"text--center\">\n      <Svg className={styles.featureSvg} alt={title} />\n    </div>\n    <div className=\"text--center padding-horiz--md\">\n      <h3>{title}</h3>\n      <p className=\"description\">{description}</p>\n    </div>\n  </div>\n)\n\nexport default function HomepageFeatures() {\n  return (<>\n    <section className={`${styles.features} featured-sections`}>\n\n      <h2 className={styles.title}>Building something <strong>fast</strong> doesn't mean you can't also build it <strong>right</strong></h2>\n      <div className={`${styles.row} row`}>\n        {FeatureList.map((props, idx) => (\n          <Feature key={idx} {...props} />\n        ))}\n      </div>\n    </section>\n  </>)\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageFeatures.module.scss",
    "content": ".features {\n  align-items: center;\n  @media only screen and (max-width: 641px) {\n    padding-left: 1rem;\n    padding-right: 1rem;\n  }\n\n  .title {\n    color: white;\n    font-weight: 400;\n    text-transform: inherit;\n    max-width: 34rem;\n  }\n  @media screen and (max-width: 768px) {\n    .title {\n      padding: 0 2rem;\n    }\n  }\n\n  .row {\n    justify-content: center;\n  }\n\n  &>div{\n    row-gap: 3rem;\n  }\n\n  h3 {\n    line-height: 250%;\n    color: var(--ifm-color-secondary);\n    margin-bottom: 1rem;\n  }\n\n  p {\n    color: white;\n    font-size: 0.9rem;\n    padding: 0 calc(var(--ifm-spacing-horizontal)*2);\n  }\n\n  .featureSvg {\n    height: 2.5rem;\n  }\n  .featureSvg path {\n    fill: var(--ifm-color-secondary);\n  }\n}\n\n"
  },
  {
    "path": "doc-site/src/components/HomepageOfferings.js",
    "content": "import React, { useState } from 'react';\nimport styles from './HomepageOfferings.module.scss';\n\nconst offerings = [\n  {\n    logo: require('../../static/img/icons/offering-infra.svg').default,\n    label: 'Infrastructure',\n    tools: [\n      {\n        name: 'Terraform',\n        logo: 'img/tools/terraform.png',\n        url: 'https://terraform.io',\n      },\n      {\n        name: 'Kubernetes',\n        logo: 'img/tools/kubernetes.png',\n        url: 'https://kubernetes.io/',\n        noCrop: true,\n      },\n      {\n        name: 'Amazon Web Services',\n        logo: 'img/tools/aws.png',\n        url: 'https://aws.amazon.com/',\n      },\n      {\n        name: 'Cert Manager',\n        logo: 'img/tools/cert-manager.png',\n        url: 'https://cert-manager.io/docs/',\n      },\n      {\n        name: 'External DNS',\n        logo: 'img/tools/external-dns.png',\n        url: 'https://github.com/kubernetes-sigs/external-dns',\n      },\n      {\n        name: 'Wireguard',\n        logo: 'img/tools/wireguard.png',\n        url: 'https://www.wireguard.com/',\n      },\n      {\n        name: 'Prometheus',\n        logo: 'img/tools/prometheus.png',\n        url: 'https://prometheus.io/',\n      },\n      {\n        name: 'Grafana',\n        logo: 'img/tools/grafana.png',\n        url: 'https://grafana.com/',\n      },\n    ]\n  },\n  {\n    logo: require('../../static/img/icons/offering-cicd.svg').default,\n    label: 'CI/CD',\n    tools: [\n      {\n        name: 'Github Actions',\n        logo: 'img/tools/github-actions.svg',\n        noCrop: true,\n        url: 'https://github.com/features/actions',\n      },\n      {\n        name: 'CircleCI',\n        logo: 'img/tools/circleci.png',\n        url: 'https://circleci.com',\n      },\n      {\n        name: 'Docker',\n        logo: 'img/tools/docker.png',\n        url: 'https://docker.com/',\n      },\n      {\n        name: 'AWS ECR',\n        logo: 'img/tools/ecr.png',\n        url: 'https://aws.amazon.com/ecr/',\n      },\n    ]\n  },\n  {\n    logo: require('../../static/img/icons/offering-frontend.svg').default,\n    label: 'FRONTEND',\n    tools: [\n      {\n        name: 'React js',\n        logo: 'img/tools/react.png',\n        url: 'https://reactjs.org',\n      },\n      {\n        name: 'AWS S3',\n        logo: 'img/tools/s3.png',\n        url: 'https://aws.amazon.com/s3/',\n      },\n      {\n        name: 'AWS Cloudfront',\n        logo: 'img/tools/cloudfront.png',\n        url: 'https://aws.amazon.com/cloudfront/',\n      },\n      {\n        name: 'ECMAScript 2018',\n        logo: 'img/tools/js.png',\n        url: 'https://www.w3schools.com/js/js_2018.asp',\n      },\n    ]\n  },\n  {\n    logo: require('../../static/img/icons/offering-backend.svg').default,\n    label: 'BACKEND',\n    tools: [\n      {\n        name: 'Golang',\n        logo: 'img/tools/golang.png',\n        url: 'https://golang.org',\n      },\n      {\n        name: 'Node.js',\n        logo: 'img/tools/nodejs.png',\n        url: 'https://nodejs.org',\n        noCrop: true,\n      },\n      {\n        name: 'Open ID Connect',\n        logo: 'img/tools/openid.png',\n        url: 'https://openid.net/connect/',\n      },\n      {\n        name: 'Ory Kratos & Oathkeeper',\n        logo: 'img/tools/ory.png',\n        url: 'https://www.ory.sh/kratos/docs/',\n      },\n      {\n        name: 'Telepresence',\n        logo: 'img/tools/telepresence.png',\n        url: 'https://www.telepresence.io/',\n        noCrop: true,\n      },\n      {\n        name: 'Stripe',\n        logo: 'img/tools/stripe.png',\n        url: 'https://stripe.com',\n        noCrop: true,\n      },\n    ]\n  },\n]\n\nconst Offerings = ({data, active, clickHandler}) => (\n  <div className={styles.offering_box}>\n    <div className={styles.left_box}>\n      {\n        data.map((i, idx) =>\n          <Discipline\n            key={idx}\n            logo={i.logo}\n            label={i.label}\n            clickHandler={clickHandler}\n            active={i.label == active}\n          />\n        )\n      }\n    </div>\n\n    <div className={styles.right_box}>\n      <ToolBox\n        data={ data.find((i) => i.label == active).tools }\n      />\n    </div>\n  </div>\n)\n\nconst Discipline = ({logo: LogoSvg, label, clickHandler, active}) => (\n  <div\n    className={`${styles.discipline} ${active && styles.discipline_active}`}\n    onClick={() => clickHandler({ active: label})}\n  >\n    <LogoSvg className={styles.logo} alt=\"logo\" />\n    <h3 className={styles.discipline_name}>{label}</h3>\n  </div>\n)\n\nconst ToolBox = ({data}) => <ul>\n  {\n    data.map((tool, idx) =>\n      <Tool key={idx} tool={tool} idx={idx} />\n    )\n  }\n</ul>\n\nconst Tool = ({tool, idx}) => (<li key={`tool-${idx}`}>\n  <a href={tool.url} target=\"_blank\">\n    <img src={tool.logo} className={tool.noCrop && styles[\"no-crop\"]} />\n    <p>{tool.name}</p>\n  </a>\n</li>)\n\nexport default function FeaturedOfferings() {\n  const title = \"What do you get out of the box ?\"\n  const [state, setState] = useState({\n    active: \"Infrastructure\",\n  })\n\n  return <div className={`${styles.offerings_container} featured-sections`}>\n    <h2>{title}</h2>\n    <Offerings data={offerings} active={state.active} clickHandler={setState} />\n  </div>\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageOfferings.module.scss",
    "content": ".offerings_container{\n  text-align: center;\n  padding: 9rem 8rem 5rem;\n  margin: 0 auto;\n  min-height: 53rem;\n  background-color: #E8EDF4;\n  color: var(--ifm-landing-page-inverse-font-color);\n\n\n  .offering_box {\n    display: flex;\n    column-gap: 5rem;\n    flex-wrap: wrap;\n    justify-content: center;\n  }\n}\n\n.left_box{\n  flex: 0 1 270px;\n  margin: 2rem 0px;\n  display: flex;\n  flex-direction: column;\n  column-gap: 1rem;\n  row-gap: 1rem;\n\n  .discipline {\n    display: flex;\n    height: 5rem;\n    column-gap: 1rem;\n    position: relative;\n    font-weight: bold;\n    text-transform: uppercase;\n    flex: 0 0 auto;\n    cursor: pointer;\n    border-radius: 12px;\n    background: #D7DEE8;\n    padding-right: 1rem;\n\n    .logo {\n      flex: 1;\n      height: 3rem;\n      align-self: center;\n      margin: 1rem;\n    }\n\n    .discipline_name {\n      flex: 4;\n      align-self: center;\n      line-height: 0;\n      margin: 0;\n      text-align: left;\n      font-size: 1rem;\n    }\n    &:hover:not(.discipline_active) .discipline_name{\n      color: var(--ifm-color-secondary);\n    }\n  }\n\n  .discipline_active{\n    background: var(--ifm-color-secondary);\n    color: var(--ifm-color-dark-active);\n\n    .logo path{\n      fill: var(--ifm-color-dark-active);\n    }\n\n    &:after {\n      content: \"\";\n      width: 5rem;\n      right: -5rem;\n      height: 0px;\n      align-self: center;\n      position: absolute;\n      border-top: 4px dashed var(--ifm-color-secondary);\n    }\n  }\n}\n\n.right_box{\n  border: 4px dashed var(--ifm-color-secondary);\n  border-radius: 12px;\n  display: flex;\n  padding: 2rem;\n\n  ul{\n    height: 100%;\n    width: 100%;\n    align-self: center;\n    margin: 0;\n    padding: 0;\n    display: flex;\n    flex-direction: column;\n    row-gap: 1.2rem;\n\n    li {\n      list-style: none;\n      justify-self: stretch;\n      flex: 1 1 40px;\n\n      a{\n        display: flex;\n        column-gap: 1rem;\n        text-decoration: none;\n        color: var(--ifm-landing-page-inverse-font-color);\n        font-weight: bold;\n\n        &:hover{\n          color: var(--ifm-color-secondary);\n        }\n      }\n\n      img{\n        width: 32px;\n        border-radius: 50%;\n        padding: 0;\n\n        &.no-crop{\n          border-radius: 0;\n        }\n      }\n\n      p{\n        flex: 5;\n        text-align: left;\n        font-size: 1.1rem;\n        margin: 0;\n        align-self: center;\n        font-family: 'Montserrat';\n        font-weight: 600;\n        white-space: nowrap;\n      }\n      @media only screen and (max-width: 400px) {\n        p {\n          font-size: 0.8rem;\n        }\n      }\n    }\n  }\n}\n\n/**\n  box becomes stacked when screen is small\n  and the dotted line is vertical instead of horizontal\n**/\n@media screen and (max-width: 967px) {\n  .offerings_container {\n    padding: 5rem 2rem;\n    min-height: 62rem;\n  }\n  .left_box {\n    justify-content: center;\n    flex-direction: row;\n    flex-wrap: wrap;\n    flex: 1 1 auto;\n\n    .discipline {\n\n      .logo {\n        flex: 1 1 33px;\n        margin: 0.5rem;\n      }\n    }\n    .discipline_active{\n      order: 1;\n      flex: 4 4 100%;\n      margin: 0 16%;\n\n      &:after {\n        content: \"\";\n        width: 0px;\n        height: 2rem;\n        right: 50%;\n        position: absolute;\n        border-right: var(--ifm-color-secondary) dashed 4px;\n        bottom: -2rem;\n        box-sizing: border-box;\n        overflow: visible;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageTrustedBy.js",
    "content": "import React from 'react';\nimport styles from './HomepageTrustedBy.module.scss';\n\nconst trustedByData = [\n  {\n    img: \"img/partners/planworth.png\",\n    src: \"https://www.planworth.co/\",\n  },\n  {\n    img: \"img/partners/patch.png\",\n    src: \"https://www.patch.io/\",\n  },\n  {\n    img: \"img/partners/atlasone.png\",\n    src: \"https://www.atlasone.ca/\",\n  },\n  {\n    img: \"img/partners/placeholder.png\",\n    src: \"https://placeholder.co/\",\n  },\n]\n\nconst Carousel = ({data}) => (\n  <ul className={styles.trusted}>\n    {\n      data.map((item, idx) => (\n        <li key={idx}>\n          <a href={item.src} target=\"_blank\">\n            <img src={item.img} />\n          </a>\n        </li>\n      ))\n    }\n  </ul>\n)\n\nexport default function TrustedByCarousel() {\n  return <div className=\"featured-sections\">\n    <h3 className={styles.title}>Trusted By</h3>\n    <Carousel data={trustedByData} />\n  </div>\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageTrustedBy.module.scss",
    "content": "h3.title {\n  letter-spacing: 0.1rem;\n  text-align: center;\n  font-weight: 400;\n  font-family: \"Montserrat\";\n}\n\n.trusted {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: center;\n  padding: 0;\n  row-gap: 2em;\n\n  li{\n    list-style: none;\n    margin: 0 1.25rem;\n    height: 100px;\n    width: 200px;\n    background-color: rgba(196, 196, 196, 0.5);\n    display: flex;\n    border-radius: 5px;\n    padding: 2rem;\n\n    &:hover{\n      background-color: rgba(220,235,245, 0.8);\n    }\n\n    a{\n      align-self: center;\n      line-height: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageVideo.js",
    "content": "import React from 'react';\nimport styles from './HomepageVideo.module.scss';\n\nexport default function FeatureVideo () {\nreturn <div className={styles.video}>\n    <iframe width=\"850\" height=\"450\" src=\"https://www.youtube.com/embed/6rBM8L8dz4A\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n  </div>\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageVideo.module.scss",
    "content": ".video {\n  width: 100%;\n  text-align: center;\n\n  iframe {\n    max-width: 65%;\n  }\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageWhyZero.js",
    "content": "import React, { useState } from 'react';\nimport clsx from 'clsx';\nimport styles from './HomepageWhyZero.module.scss';\n\nconst reasons = [\n  {\n    logo: require('../../static/img/icons/reason-diamond.svg').default,\n    title: 'Quality',\n    text: `Like the best DevOps engineer you’ve ever met - except open source and free.`,\n    details: [\n      `The devops skill gap is real. Why spend precious time picking up unfamiliar tech, \n      making wrong choices that result in costly refactoring or rebuilding in the future, \n      and missing tools and best practices that would speed up your product iteration?`,\n      `Get real-time support for all your questions from Zero’s community.`\n    ]\n  },\n  {\n    logo: require('../../static/img/icons/reason-clockwise.svg').default,\n    title: 'Speed',\n    text: `Just as fast as other tools like Heroku to get up and running.`,\n    details: [\n      `Building foundational infrastructure the right way doesn’t have to take a long time. Our team has years of experience building and scaling startups and have poured that knowledge into Zero. What used to take us weeks of DevOps work can now take you 30 minutes.`,\n      `We provide constant updates and new modules that you can pull in on an ongoing basis.`,\n    ]\n  },\n  {\n    logo: require('../../static/img/icons/reason-key.svg').default,\n    title: 'Ownership',\n    text: `You own 100% of the code. No lock-in!`,\n    details: [\n      `Everything built by Zero is yours. It’s your code to change or migrate off at any point.`,\n      `Cloud application hosting tools are built to lock you in and don’t scale. `,\n      `Infrastructure is created in your cloud provider account. You can customize as much \n      as you like with no strings attached. You control how much you spend.`\n    ]\n  }\n];\n\nconst Reasons = ({ data, expanded, setExpanded }) => (\n  <div className={`${styles.reasons} row`}>\n    {\n      data.map((i, idx) => (\n        <div key={idx} className={`${styles.reason} ${clsx('col col--3') }`}>\n          <i.logo className={styles.reason_logo} alt=\"logo\" />\n          <h4 className={styles.title}>{i.title}</h4>\n\n          <p className={`${styles.description} description`}>{i.text}</p>\n          {expanded && <ul className={`${styles.description} description`}>{i.details.map(content=> <li>{content}</li>)}</ul>}\n        </div>\n      ))\n    }\n  </div>\n)\n\nexport default function FeatureWhyZero () {\n  const [expanded, setExpanded] = useState(false)\nconst title = \"Why is Zero good for startups ?\"\nreturn <div className={`${styles.reasons_container} featured-sections`}>\n    <h2 className={styles.title}>\n      {title}\n      <h4 className={styles.subtitle}>\n        As engineer #1, your sole priority is to build the logic for your application and get it into customers’ hands as quickly and reliably as possible.\n      </h4>\n    </h2>\n    <Reasons data={reasons} expanded={expanded} setExpanded={setExpanded} />\n    <div className={`${styles.expand} ${expanded && styles.expanded}`}>\n      <a href=\"javascript:void(0);\" onClick={()=>{setExpanded(!expanded)}}>{expanded ? \"Less\" : \"More\" } Details</a>\n    </div>\n  </div>\n}\n"
  },
  {
    "path": "doc-site/src/components/HomepageWhyZero.module.scss",
    "content": ".reasons_container{\n  text-align: center;\n  background: white;\n  color: var(--ifm-landing-page-inverse-font-color);\n  @media only screen and (max-width: 641px) {\n    padding-left: 3rem;\n    padding-right: 3rem;\n  }\n\n  .title {\n    margin-bottom: 5rem;\n    letter-spacing: 0.05rem;\n  }\n  .subtitle {\n    text-transform: none;\n    margin: 1rem auto;\n    font-family: 'lato';\n    font-weight: 400;\n    max-width: 34rem;\n    letter-spacing: normal;\n  }\n  .expand {\n    margin-top: 2rem;\n\n    a{\n      display: flex;\n      justify-content: center;\n\n      &:active, &:link, &:visited{\n        text-decoration: none;\n        color: var(--ifm-landing-page-inverse-font-color);\n      }\n      &:after {\n        align-self: center;\n        margin: 0 0.5rem;\n        content: \"\";\n        width: 18px;\n        height: 18px;\n        background:  url(../../static/img/icons/icon-plus.svg);\n        background-size: cover;\n      }\n    }\n    &.expanded a{\n      &:after {\n        background:  url(../../static/img/icons/icon-minus.svg);\n        background-size: cover;\n      }\n    }\n  }\n}\n\n.reasons {\n  display: flex;\n  flex-direction: row;\n  row-gap: 3rem;\n  justify-content: center;\n  column-gap: 2rem;\n}\n\n.reason {\n  max-width: 10rem;\n\n  .title{\n    color: var(--ifm-color-secondary);\n    text-transform: uppercase;\n    margin: 1rem;\n  }\n\n  .description{\n    min-height: 2.5rem;\n    font-size: 0.9rem;\n    align-self: center;\n  }\n  ul.description {\n    margin-top: 2rem;\n    padding-left: 1rem;\n  }\n  .description li{\n    text-align: left;\n  }\n\n  .reason_logo {\n    height: 3rem;\n    flex: 0 0 3rem;\n    align-self: center;\n\n    path {\n      stroke: var(--ifm-color-secondary);\n    }\n  }\n}\n"
  },
  {
    "path": "doc-site/src/css/custom.css",
    "content": "/**\n * Any CSS included here will be global. The classic template\n * bundles Infima by default. Infima is a CSS framework designed to\n * work well for content-centric websites.\n */\n\n /* You can override the default Infima variables here. */\n:root {\n  --ifm-color-primary-dark: rgb(33, 175, 144);\n  --ifm-color-primary-darker: rgb(31, 165, 136);\n  --ifm-color-primary-darkest: rgb(26, 136, 112);\n  --ifm-color-primary-light: rgb(70, 203, 174);\n  --ifm-color-primary-lighter: rgb(102, 212, 189);\n  --ifm-color-primary-lightest: rgb(146, 224, 208);\n  --ifm-navbar-link-hover-color: rgb(33, 175, 144);\n  --ifm-code-font-size: 95%;\n  --ifm-footer-padding-vertical: 20px;\n  --ifm-footer-padding-horizontal: 10px;\n  --ifm-footer-background-color: transparent;\n  --ifm-color-secondary: rgb(255, 106, 185);\n  --ifm-button-cta-background: linear-gradient(294.55deg, #F2BD6D 17.88%, #F17F84 65.95%, #FF3EA7 93.96%);\n  --ifm--hero--text-background-gradient: linear-gradient(234.45deg, #12C6FF 0%, #6184C9 68.23%);\n  --ifm-landing-page-inverse-font-color: #333;\n  --ifm-color-dark-active: rgba(5, 6, 55, 1);\n  --ifm-spacing-vertical: 0px;\n  --ifm-navbar-sidebar-width: 15rem;\n  --ifm-alert-padding-vertical: 1rem;\n  --ifm-alert-padding-horizontal: 1rem;\n}\n\nhtml[data-theme='dark'] {\n  --ifm-color-primary: #edc281;\n  --ifm-font-color-base-inverse: white;\n  --ifm-navbar-background-color: transparent;\n  --ifm-background-color: linear-gradient(90deg, rgba(15, 16, 17, 1) 0%, rgba(1, 2, 66, 1) 100%);\n  --ifm-color-info: rgba(255, 166, 0, 0.4);\n  --ifm-menu-color-active: #edc281;\n  --ifm-navbar-link-hover-color: #edc281;\n  --ifm-link-color: #edc281;\n  --ifm-code-background: rgba(140,140,140,0.5);\n}\n\nhtml[data-theme='light'] {\n  --ifm-heading-color: navy;\n  --ifm-font-color-base-inverse: navy;\n  --ifm-color-secondary: #6184C9;\n  --ifm-background-color: #fefefe;\n}\nhtml[data-theme='light'] .navbar--fixed-top, html[data-theme='light'] .navbar-sidebar__brand{\n  background: linear-gradient(90deg, rgba(15, 16, 17, 0.3) 0%, rgba(1, 2, 66, 0.4) 100%);\n}\n\n/** PAGE BACKGROUND **/\nhtml{\n  background: var(--ifm-background-color);\n}\n\n.hero--primary{\n  background: none;\n}\n\n/** FONTS **/\n.navbar {\n  font-family: 'lato';\n  font-weight: 900;\n}\n.menu__list .menu__link--sublist {\n  font-weight: 900;\n}\n.menu__list .menu__list-item {\n  font-weight: 400;\n}\n.featured-sections h3 , .featured-sections h2, .featured-sections h4{\n  font-family: 'Montserrat';\n  font-weight: 800;\n}\n.button-cta{\n  font-family: 'Montserrat';\n  font-weight: 700;\n}\n.description {\n  font-family: 'lato';\n  font-weight: 400;\n}\n\n/** Small screen hamburger menu slideout background **/\n.navbar-sidebar--show .navbar-sidebar {\n  background-color: var(--ifm-code-background);\n}\n.navbar-sidebar__brand .navbar__brand {\n  height: 1rem;\n}\n.navbar-sidebar__brand .navbar__brand img{\n  margin: 1rem;\n}\n.navbar-sidebar__items .menu__link{\n  column-gap: 1rem;\n  justify-content: flex-start;\n}\n.navbar__brand img{\n  height: 80%;\n  margin-left: 0.6rem;\n}\n\n/** Docs sidebar - folder name capitalize */\n.menu__link--sublist{\n  text-transform: capitalize;\n}\n\n/** FLATTEN FOOTER ITEMS **/\n.footer__items{\n  display: flex;\n  justify-content: flex-end;\n}\n\n.footer__item{\n  padding:0 var(--ifm-spacing-horizontal);\n}\n\n@media only screen and (max-width: 400px) {\n  .footer {\n    display: none;\n  }\n}\n\n/** LANDING PAGE SECTION **/\n.featured-sections {\n  text-align: center;\n  padding: 6rem 0;\n}\n\n.featured-sections h3, .featured-sections h2 {\n  text-transform: uppercase;\n  letter-spacing: 0.05rem;\n  padding: 0;\n  margin: 0 auto 3rem auto;\n}\n\n.featured-sections h2 {\n  margin: 0 auto 5rem auto;\n}\n\n/**\nNAV BAR Icons\nflattens footer icons\n */\n.header-logo-24.navbar__link, .footer__item .header-logo-24 {\n  display:flex;\n  line-height: 24px;\n  text-transform: uppercase;\n  color: var(--ifm-font-color-base-inverse);\n  font-weight: 700;\n}\n\n.header-logo-24:hover{\n  text-decoration: none;\n}\n\n.header-logo-24:before{\n  content: \"\";\n  display: flex;\n  height: 24px;\n  width: 24px;\n  margin: 0 3px;\n  background-size: cover;\n}\n\nhtml[data-theme='dark'] .header-github-link:before {\n  background: url(../../static/img/icons/octocat.svg) no-repeat;\n}\n\nhtml[data-theme='light'] .header-github-link:before  {\n  background: url(../../static/img/icons/octocat-navy.svg) no-repeat;\n}\n\nhtml[data-theme='dark'] .header-slack-link:before {\n  background: url(../../static/img/icons/slack.svg) no-repeat;\n}\n\nhtml[data-theme='light'] .header-slack-link:before  {\n  background: url(../../static/img/icons/slack-navy.svg) no-repeat;\n}\n\nhtml[data-theme='dark'] .header-docs-link:before {\n  background: url(../../static/img/icons/notes.svg) no-repeat;\n}\n\nhtml[data-theme='light'] .header-docs-link:before  {\n  background: url(../../static/img/icons/notes-navy.svg) no-repeat;\n}\n\n/* alert - note */\n.alert--secondary{\n  --ifm-alert-background-color: rgb(200, 212, 226);\n  --ifm-alert-border-color: rgb(200, 212, 226);\n  --ifm-alert-color: var(--ifm-color-gray-900);\n  --ra-admonition-icon-color: var(--ifm-color-gray-900);\n}\n/* alert - info */\n.alert--info{\n  --ifm-alert-background-color: rgb(117, 157, 209);\n  --ifm-alert-border-color: rgb(117, 157, 209);\n  --ifm-alert-color: var(--ifm-color-gray-900);\n  --ra-admonition-icon-color: var(--ifm-color-gray-900);\n}\n\n.docs-main-toc .toc-item {\n  margin: 2rem 1rem;\n}\n\n.header-logo-24 span svg {\n  display: none;\n}\n"
  },
  {
    "path": "doc-site/src/pages/docs/index.js",
    "content": "import React from 'react';\nimport { Redirect } from 'react-router-dom';\n\nexport default () => <Redirect to=\"/docs/zero/about/overview\" />\n"
  },
  {
    "path": "doc-site/src/pages/docs/zero/index.js",
    "content": "import React from 'react';\nimport { Redirect } from 'react-router-dom';\n\nexport default () => <Redirect to=\"/docs/zero/about/overview\" />\n"
  },
  {
    "path": "doc-site/src/pages/index.js",
    "content": "import React from 'react';\nimport clsx from 'clsx';\nimport Layout from '@theme/Layout';\nimport Link from '@docusaurus/Link';\nimport useDocusaurusContext from '@docusaurus/useDocusaurusContext';\nimport styles from './index.module.scss';\nimport HomepageFeatures from '../components/HomepageFeatures';\nimport HomepageTrustedBy from '../components/HomepageTrustedBy';\nimport HomepageVideo from '../components/HomepageVideo';\nimport HomepageWhyZero from '../components/HomepageWhyZero';\nimport HomepageOfferings from '../components/HomepageOfferings';\n\nfunction HomepageHeader() {\n  const {siteConfig} = useDocusaurusContext();\n\n  return (\n    <header className={clsx('hero hero--primary', styles.heroBanner)}>\n      <p className={styles.hero__subtitle}>{siteConfig.tagline}</p>\n    </header>\n  );\n}\n\nfunction HomePageCallToAction () {\n  return <div className={styles.buttons}>\n    <Link\n      className=\"button button-cta button--secondary button--lg\"\n      to=\"/docs/zero/getting-started/installation\">\n      Get Started\n    </Link>\n  </div>\n}\n\nexport default function Home() {\n  const landingPageOnlyGlobalItemStyle = `\n  .navbar  {\n    padding: 2.5rem 0 3.5rem;\n    box-shadow: none;\n    background: linear-gradient(90deg, rgba(15, 16, 17, 0.6) 0%, rgba(1, 2, 66, 0.6) 100%);\n  }\n  @media only screen and (max-width: 641px) {\n    .navbar__items--right {\n      display: none;\n    }\n  }\n  .navbar__inner {\n    padding: 0 3rem;\n  }\n  .navbar__brand img{\n    height: 130%;\n  }\n  .react-toggle{\n    display: none;\n  }\n  `;\n  const {siteConfig} = useDocusaurusContext();\n  return (\n    <Layout\n      title={`Build it Fast, Build it Right!`}\n      description=\"Opinionated infrastructure to take you from idea to production on day one!\"\n      >\n      <style>{landingPageOnlyGlobalItemStyle}</style>\n      <HomepageHeader />\n      <main>\n        <HomepageVideo />\n        <HomePageCallToAction />\n        <HomepageTrustedBy />\n        <HomepageWhyZero />\n        <HomepageFeatures />\n        <HomepageOfferings />\n        <HomePageCallToAction />\n      </main>\n    </Layout>\n  );\n}\n"
  },
  {
    "path": "doc-site/src/pages/index.module.scss",
    "content": "/**\n * CSS files with the .module.css suffix will be treated as CSS modules\n * and scoped locally.\n */\n\n.heroBanner {\n  padding: 4rem 0 2rem;\n  text-align: center;\n\n  .hero__subtitle {\n    background: var(--ifm--hero--text-background-gradient);\n    background-clip: text;\n    -webkit-background-clip: text;\n    -webkit-text-fill-color: transparent;\n    font-weight: 900;\n    font-size: 2rem;\n    max-width: 50rem;\n    margin: 3rem auto;\n  }\n}\n\n.buttons {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin: 4rem auto 4rem;\n\n  a {\n    text-transform: uppercase;\n    background: var(--ifm-button-cta-background);\n    border-radius: 25px;\n    border-color: transparent;\n    border-style: none;\n    background-size: 150% 100%;\n  }\n}\n\n.hiring {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin: 4rem auto 4rem;\n  text-align: center;\n  a {\n    font-size: 2rem;\n    font-weight: bold;\n    color: #ffffff;\n  }\n  @media only screen and (max-width: 600px) {\n    a {\n      padding: 0 2rem;\n    }\n  }\n}\n\n@media screen and (max-width: 966px) {\n  .heroBanner {\n    padding: 2rem;\n  }\n}\n"
  },
  {
    "path": "doc-site/src/theme/DocSidebar/index.js",
    "content": "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport React, {useState, useCallback, useEffect, useRef, memo} from 'react';\nimport clsx from 'clsx';\nimport {useThemeConfig, isSamePath} from '@docusaurus/theme-common';\nimport useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';\nimport useLockBodyScroll from '@theme/hooks/useLockBodyScroll';\nimport useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';\nimport useScrollPosition from '@theme/hooks/useScrollPosition';\nimport Link from '@docusaurus/Link';\nimport isInternalUrl from '@docusaurus/isInternalUrl';\nimport Logo from '@theme/Logo';\nimport IconArrow from '@theme/IconArrow';\nimport IconMenu from '@theme/IconMenu';\nimport {translate} from '@docusaurus/Translate';\nimport styles from './styles.module.css';\nconst MOBILE_TOGGLE_SIZE = 24;\n\nfunction usePrevious(value) {\n  const ref = useRef(value);\n  useEffect(() => {\n    ref.current = value;\n  }, [value]);\n  return ref.current;\n}\n\nconst isActiveSidebarItem = (item, activePath) => {\n  if (item.type === 'link') {\n    return isSamePath(item.href, activePath);\n  }\n\n  if (item.type === 'category') {\n    return item.items.some((subItem) =>\n      isActiveSidebarItem(subItem, activePath),\n    );\n  }\n\n  return false;\n}; // Optimize sidebar at each \"level\"\n// TODO this item should probably not receive the \"activePath\" props\n// TODO this triggers whole sidebar re-renders on navigation\n\nconst DocSidebarItems = memo(function DocSidebarItems({items, ...props}) {\n  return items.map((item, index) => (\n    <DocSidebarItem\n      key={index} // sidebar is static, the index does not change\n      item={item}\n      {...props}\n    />\n  ));\n});\n\nfunction DocSidebarItem(props) {\n  switch (props.item.type) {\n    case 'category':\n      return <DocSidebarItemCategory {...props} />;\n\n    case 'link':\n    default:\n      return <DocSidebarItemLink {...props} />;\n  }\n}\n\nfunction DocSidebarItemCategory({\n  item,\n  onItemClick,\n  collapsible,\n  activePath,\n  ...props\n}) {\n  const {items, label} = item;\n  const isActive = isActiveSidebarItem(item, activePath);\n  const wasActive = usePrevious(isActive); // active categories are always initialized as expanded\n  // the default (item.collapsed) is only used for non-active categories\n\n  const [collapsed, setCollapsed] = useState(() => {\n    if (!collapsible) {\n      return false;\n    }\n\n    return isActive ? false : item.collapsed;\n  });\n  const menuListRef = useRef(null);\n  const [menuListHeight, setMenuListHeight] = useState(undefined);\n\n  const handleMenuListHeight = (calc = true) => {\n    setMenuListHeight(\n      calc ? `${menuListRef.current?.scrollHeight}px` : undefined,\n    );\n  }; // If we navigate to a category, it should automatically expand itself\n\n  useEffect(() => {\n    const justBecameActive = isActive && !wasActive;\n\n    if (justBecameActive && collapsed) {\n      setCollapsed(false);\n    }\n  }, [isActive, wasActive, collapsed]);\n  const handleItemClick = useCallback(\n    (e) => {\n      e.preventDefault();\n\n      if (!menuListHeight) {\n        handleMenuListHeight();\n      }\n\n      setTimeout(() => setCollapsed((state) => !state), 100);\n    },\n    [menuListHeight],\n  );\n\n  if (items.length === 0) {\n    return null;\n  }\n\n  return (\n    <li\n      className={clsx('menu__list-item', {\n        'menu__list-item--collapsed': collapsed,\n      })}>\n      <a\n        className={clsx('menu__link', {\n          'menu__link--sublist': collapsible,\n          'menu__link--active': collapsible && isActive,\n          [styles.menuLinkText]: !collapsible,\n        })}\n        onClick={collapsible ? handleItemClick : undefined}\n        href={collapsible ? '#!' : undefined}\n        {...props}>\n        {label}\n      </a>\n      <ul\n        className=\"menu__list\"\n        ref={menuListRef}\n        style={{\n          height: menuListHeight,\n        }}\n        onTransitionEnd={() => {\n          if (!collapsed) {\n            handleMenuListHeight(false);\n          }\n        }}>\n        <DocSidebarItems\n          items={items}\n          tabIndex={collapsed ? '-1' : '0'}\n          onItemClick={onItemClick}\n          collapsible={collapsible}\n          activePath={activePath}\n        />\n      </ul>\n    </li>\n  );\n}\n\nfunction DocSidebarItemLink({\n  item,\n  onItemClick,\n  activePath,\n  collapsible: _collapsible,\n  ...props\n}) {\n  const {href, label, customProps} = item;\n  const isActive = isActiveSidebarItem(item, activePath);\n  return (\n    <li className=\"menu__list-item\" key={label}>\n      <Link\n        className={clsx('menu__link', {\n          'menu__link--active': isActive,\n          [styles.menuLinkExternal]: !isInternalUrl(href),\n        })}\n        to={href}\n        {...(isInternalUrl(href) && {\n          isNavLink: true,\n          exact: true,\n          onClick: onItemClick,\n        })}\n        {...props}\n        {...customProps}>\n        {label}\n      </Link>\n    </li>\n  );\n}\n\nfunction useShowAnnouncementBar() {\n  const {isAnnouncementBarClosed} = useUserPreferencesContext();\n  const [showAnnouncementBar, setShowAnnouncementBar] = useState(\n    !isAnnouncementBarClosed,\n  );\n  useScrollPosition(({scrollY}) => {\n    if (!isAnnouncementBarClosed) {\n      setShowAnnouncementBar(scrollY === 0);\n    }\n  });\n  return showAnnouncementBar;\n}\n\nfunction useResponsiveSidebar() {\n  const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);\n  useLockBodyScroll(showResponsiveSidebar);\n  const windowSize = useWindowSize();\n  useEffect(() => {\n    if (windowSize === windowSizes.desktop) {\n      setShowResponsiveSidebar(false);\n    }\n  }, [windowSize]);\n  const closeResponsiveSidebar = useCallback(\n    (e) => {\n      e.target.blur();\n      setShowResponsiveSidebar(false);\n    },\n    [setShowResponsiveSidebar],\n  );\n  const toggleResponsiveSidebar = useCallback(() => {\n    setShowResponsiveSidebar((value) => !value);\n  }, [setShowResponsiveSidebar]);\n  return {\n    showResponsiveSidebar,\n    closeResponsiveSidebar,\n    toggleResponsiveSidebar,\n  };\n}\n\nfunction HideableSidebarButton({onClick}) {\n  return (\n    <button\n      type=\"button\"\n      title={translate({\n        id: 'theme.docs.sidebar.collapseButtonTitle',\n        message: 'Collapse sidebar',\n        description: 'The title attribute for collapse button of doc sidebar',\n      })}\n      aria-label={translate({\n        id: 'theme.docs.sidebar.collapseButtonAriaLabel',\n        message: 'Collapse sidebar',\n        description: 'The title attribute for collapse button of doc sidebar',\n      })}\n      className={clsx(\n        'button button--secondary button--outline',\n        styles.collapseSidebarButton,\n      )}\n      onClick={onClick}>\n      <IconArrow className={styles.collapseSidebarButtonIcon} />\n    </button>\n  );\n}\n\nfunction ResponsiveSidebarButton({responsiveSidebarOpened, onClick}) {\n  return (\n    <button\n      aria-label={\n        responsiveSidebarOpened\n          ? translate({\n              id: 'theme.docs.sidebar.responsiveCloseButtonLabel',\n              message: 'Close menu',\n              description:\n                'The ARIA label for close button of mobile doc sidebar',\n            })\n          : translate({\n              id: 'theme.docs.sidebar.responsiveOpenButtonLabel',\n              message: 'Open menu',\n              description:\n                'The ARIA label for open button of mobile doc sidebar',\n            })\n      }\n      aria-haspopup=\"true\"\n      className=\"button button--secondary button--sm menu__button\"\n      type=\"button\"\n      onClick={onClick}>\n      {responsiveSidebarOpened ? (\n        <span\n          className={clsx(styles.sidebarMenuIcon, styles.sidebarMenuCloseIcon)}>\n          &times;\n        </span>\n      ) : (\n        <IconMenu\n          className={styles.sidebarMenuIcon}\n          height={MOBILE_TOGGLE_SIZE}\n          width={MOBILE_TOGGLE_SIZE}\n        />\n      )}\n    </button>\n  );\n}\n\nfunction DocSidebar({\n  path,\n  sidebar,\n  sidebarCollapsible = true,\n  onCollapse,\n  isHidden,\n}) {\n  const showAnnouncementBar = useShowAnnouncementBar();\n  const {\n    navbar: {hideOnScroll},\n    hideableSidebar,\n  } = useThemeConfig();\n  const {isAnnouncementBarClosed} = useUserPreferencesContext();\n  const {\n    showResponsiveSidebar,\n    closeResponsiveSidebar,\n    toggleResponsiveSidebar,\n  } = useResponsiveSidebar();\n  return (\n    <div\n      className={clsx(styles.sidebar, {\n        [styles.sidebarWithHideableNavbar]: hideOnScroll,\n        [styles.sidebarHidden]: isHidden,\n      })}>\n      {hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}\n      <div\n        className={clsx(\n          'menu',\n          'menu--responsive',\n          'thin-scrollbar',\n          styles.menu,\n          {\n            'menu--show': showResponsiveSidebar,\n            [styles.menuWithAnnouncementBar]:\n              !isAnnouncementBarClosed && showAnnouncementBar,\n          },\n        )}>\n        <ResponsiveSidebarButton\n          responsiveSidebarOpened={showResponsiveSidebar}\n          onClick={toggleResponsiveSidebar}\n        />\n        <ul className=\"menu__list\">\n          <DocSidebarItems\n            items={sidebar}\n            onItemClick={closeResponsiveSidebar}\n            collapsible={sidebarCollapsible}\n            activePath={path}\n          />\n        </ul>\n      </div>\n      {hideableSidebar && <HideableSidebarButton onClick={onCollapse} />}\n    </div>\n  );\n}\n\nexport default DocSidebar;\n"
  },
  {
    "path": "doc-site/src/theme/DocSidebar/styles.module.css",
    "content": "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n:root {\n  --collapse-button-bg-color-dark: #2e333a;\n  overflow-x: hidden;\n}\n\n@media (min-width: 997px) {\n  .sidebar {\n    display: flex;\n    flex-direction: column;\n    max-height: 100vh;\n    height: 100%;\n    position: sticky;\n    top: 0;\n    padding-top: var(--ifm-navbar-height);\n    width: var(--doc-sidebar-width);\n    transition: opacity 50ms ease;\n  }\n\n  .sidebarWithHideableNavbar {\n    padding-top: 0;\n  }\n\n  .sidebarHidden {\n    opacity: 0;\n    height: 0;\n    overflow: hidden;\n    visibility: hidden;\n  }\n\n  .sidebarLogo {\n    display: flex !important;\n    align-items: center;\n    margin: 0 var(--ifm-navbar-padding-horizontal);\n    min-height: var(--ifm-navbar-height);\n    max-height: var(--ifm-navbar-height);\n    color: inherit !important;\n    text-decoration: none !important;\n  }\n\n  .sidebarLogo img {\n    margin-right: 0.5rem;\n    height: 2rem;\n  }\n\n  .menu {\n    flex-grow: 1;\n    padding: 0.5rem;\n  }\n\n  .menuLinkText {\n    cursor: initial;\n  }\n\n  .menuLinkText:hover {\n    background: none;\n  }\n\n  .menuWithAnnouncementBar {\n    margin-bottom: var(--docusaurus-announcement-bar-height);\n  }\n\n  .collapseSidebarButton {\n    display: block !important;\n    background-color: var(--ifm-button-background-color);\n    height: 40px;\n    position: sticky;\n    bottom: 0;\n    border-radius: 0;\n    border: 1px solid var(--ifm-toc-border-color);\n  }\n\n  .collapseSidebarButtonIcon {\n    transform: rotate(180deg);\n    margin-top: 4px;\n  }\n  html[dir='rtl'] .collapseSidebarButtonIcon {\n    transform: rotate(0);\n  }\n\n  html[data-theme='dark'] .collapseSidebarButton {\n    background-color: var(--collapse-button-bg-color-dark);\n  }\n\n  html[data-theme='dark'] .collapseSidebarButton:hover,\n  html[data-theme='dark'] .collapseSidebarButton:focus {\n    background-color: var(--ifm-color-emphasis-200);\n  }\n}\n\n.sidebarLogo,\n.collapseSidebarButton {\n  display: none;\n}\n\n.sidebarMenuIcon {\n  vertical-align: middle;\n}\n\n.sidebarMenuCloseIcon {\n  display: inline-flex;\n  justify-content: center;\n  align-items: center;\n  height: 24px;\n  font-size: 1.5rem;\n  font-weight: var(--ifm-font-weight-bold);\n  line-height: 0.9;\n  width: 24px;\n}\n\n:global(.menu__list) :global(.menu__list) {\n  overflow-y: hidden;\n  will-change: height;\n  transition: height var(--ifm-transition-fast) linear;\n}\n\n:global(.menu__list-item--collapsed) :global(.menu__list) {\n  height: 0 !important;\n}\n\n.menuLinkExternal {\n  align-items: center;\n}\n.menuLinkExternal:after {\n  content: '';\n  height: 1.15rem;\n  width: 1.15rem;\n  min-width: 1.15rem;\n  margin: 0 0 0 3%;\n  background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z'/%3E%3C/svg%3E\")\n    no-repeat;\n  filter: var(--ifm-menu-link-sublist-icon-filter);\n}\n"
  },
  {
    "path": "doc-site/static/.nojekyll",
    "content": ""
  },
  {
    "path": "go.mod",
    "content": "module github.com/commitdev/zero\n\ngo 1.16\n\nrequire (\n\tgithub.com/aws/aws-sdk-go v1.30.12\n\tgithub.com/buger/goterm v1.0.0\n\tgithub.com/coreos/go-semver v0.3.0\n\tgithub.com/gabriel-vasile/mimetype v1.1.1\n\tgithub.com/google/go-cmp v0.3.1\n\tgithub.com/google/uuid v1.1.1\n\tgithub.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02\n\tgithub.com/hashicorp/go-version v1.2.1\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/hashicorp/terraform v0.12.26\n\tgithub.com/iancoleman/strcase v0.1.2\n\tgithub.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5 // indirect\n\tgithub.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect\n\tgithub.com/k0kubun/pp v3.0.1+incompatible\n\tgithub.com/kyokomi/emoji v2.1.0+incompatible\n\tgithub.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434\n\tgithub.com/lunixbochs/vtclean v1.0.0 // indirect\n\tgithub.com/machinebox/graphql v0.2.2\n\tgithub.com/manifoldco/promptui v0.8.0\n\tgithub.com/matryer/is v1.3.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.8 // indirect\n\tgithub.com/mattn/go-isatty v0.0.13 // indirect\n\tgithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect\n\tgithub.com/sirupsen/logrus v1.6.0\n\tgithub.com/spf13/cobra v1.1.3\n\tgithub.com/stretchr/testify v1.7.0\n\tgithub.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae\n\tgolang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect\n\tgolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect\n\tgolang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f // indirect\n\tgopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect\n\tgopkg.in/yaml.v2 v2.4.0\n\n)\n\n// Tencent cloud unpublished their version v3.0.82 and became v1.0.191\n// https://github.com/hashicorp/terraform/issues/29021\nreplace github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible => github.com/tencentcloud/tencentcloud-sdk-go v1.0.191\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=\ngithub.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=\ngithub.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=\ngithub.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.2.0/go.mod h1:WWTbGPvkAg3I4ms2j2s+Zr5xCGwGqTQh+6M2ZqOczkE=\ngithub.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=\ngithub.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=\ngithub.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=\ngithub.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=\ngithub.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=\ngithub.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=\ngithub.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=\ngithub.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=\ngithub.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=\ngithub.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=\ngithub.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=\ngithub.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=\ngithub.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZylxvcg8H7wBIDfvO5g/cy4/sz1iucBlc2l3Jw=\ngithub.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=\ngithub.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=\ngithub.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=\ngithub.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=\ngithub.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=\ngithub.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171/go.mod h1:JXY95WvQrPJQtudvNARshgWajS7jNNlM90altXIPNyI=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=\ngithub.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aws/aws-sdk-go v1.30.12 h1:KrjyosZvkpJjcwMk0RNxMZewQ47v7+ZkbQDXjWsJMs8=\ngithub.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=\ngithub.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=\ngithub.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=\ngithub.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=\ngithub.com/buger/goterm v1.0.0 h1:ZB6uUlY8+sjJyFGzz2WpRqX2XYPeXVgtZAOJMwOsTWM=\ngithub.com/buger/goterm v1.0.0/go.mod h1:16STi3LquiscTIHA8SXUNKEa/Cnu4ZHBH8NsCaWgso0=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=\ngithub.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=\ngithub.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=\ngithub.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=\ngithub.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA=\ngithub.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=\ngithub.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=\ngithub.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/aws-sdk-go-base v0.4.0/go.mod h1:eRhlz3c4nhqxFZJAahJEFL7gh6Jyj5rQmQc7F9eHFyQ=\ngithub.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE=\ngithub.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02 h1:l1KB3bHVdvegcIf5upQ5mjcHjs2qsWnKh4Yr9xgIuu8=\ngithub.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=\ngithub.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=\ngithub.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\ngithub.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=\ngithub.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=\ngithub.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=\ngithub.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=\ngithub.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-tfe v0.8.1/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=\ngithub.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=\ngithub.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE=\ngithub.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8=\ngithub.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hashicorp/terraform v0.12.26 h1:FVsTCH1DMvTlzVSO2sKCzkwLczf/eZBO4GuY5IbHFk4=\ngithub.com/hashicorp/terraform v0.12.26/go.mod h1:CBxNAiTW0pLap44/3GU4j7cYE2bMhkKZNlHPcr4P55U=\ngithub.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A=\ngithub.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=\ngithub.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=\ngithub.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=\ngithub.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=\ngithub.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=\ngithub.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=\ngithub.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5 h1:Q5klzs6BL5FkassBX65t+KkG0XjYcjxEm+GNcQAsuaw=\ngithub.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=\ngithub.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=\ngithub.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=\ngithub.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=\ngithub.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=\ngithub.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o=\ngithub.com/kyokomi/emoji v2.1.0+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/likexian/gokit v0.0.0-20190309162924-0a377eecf7aa/go.mod h1:QdfYv6y6qPA9pbBA2qXtoT8BMKha6UyNbxWGWl/9Jfk=\ngithub.com/likexian/gokit v0.0.0-20190418170008-ace88ad0983b/go.mod h1:KKqSnk/VVSW8kEyO2vVCXoanzEutKdlBAPohmGXkxCk=\ngithub.com/likexian/gokit v0.0.0-20190501133040-e77ea8b19cdc/go.mod h1:3kvONayqCaj+UgrRZGpgfXzHdMYCAO0KAt4/8n0L57Y=\ngithub.com/likexian/gokit v0.20.15/go.mod h1:kn+nTv3tqh6yhor9BC4Lfiu58SmH8NmQ2PmEl+uM6nU=\ngithub.com/likexian/simplejson-go v0.0.0-20190409170913-40473a74d76d/go.mod h1:Typ1BfnATYtZ/+/shXfFYLrovhFyuKvzwrdOnIDHlmg=\ngithub.com/likexian/simplejson-go v0.0.0-20190419151922-c1f9f0b4f084/go.mod h1:U4O1vIJvIKwbMZKUJ62lppfdvkCdVd2nfMimHK81eec=\ngithub.com/likexian/simplejson-go v0.0.0-20190502021454-d8787b4bfa0b/go.mod h1:3BWwtmKP9cXWwYCr5bkoVDEfLywacOv0s06OBEDpyt8=\ngithub.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 h1:im9kkmH0WWwxzegiv18gSUJbuXR9y028rXrWuPp6Jug=\ngithub.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=\ngithub.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=\ngithub.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=\ngithub.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=\ngithub.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84=\ngithub.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=\ngithub.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo=\ngithub.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=\ngithub.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=\ngithub.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b/go.mod h1:wr1VqkwW0AB5JS0QLy5GpVMS9E3VtRoSYXUYyVk46KY=\ngithub.com/matryer/is v1.3.0 h1:9qiso3jaJrOe6qBRJRBt2Ldht05qDiFP9le0JOIhRSI=\ngithub.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=\ngithub.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=\ngithub.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=\ngithub.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=\ngithub.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=\ngithub.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=\ngithub.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=\ngithub.com/tencentcloud/tencentcloud-sdk-go v1.0.191/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI=\ngithub.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=\ngithub.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae h1:vgGSvdW5Lqg+I1aZOlG32uyE6xHpLdKhZzcTEktz5wM=\ngithub.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae/go.mod h1:quDq6Se6jlGwiIKia/itDZxqC5rj6/8OdFyMMAwTxCs=\ngithub.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=\ngithub.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=\ngithub.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=\ngithub.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=\ngithub.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=\ngithub.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=\ngithub.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8=\ngithub.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=\ngithub.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f h1:3MlESg/jvTr87F4ttA/q4B+uhe/q6qleC9/DP+IwQmY=\ngolang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "internal/apply/apply.go",
    "content": "package apply\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\n\t\"log\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/commitdev/zero/internal/module\"\n\t\"github.com/commitdev/zero/internal/util\"\n\t\"github.com/hashicorp/terraform/dag\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/manifoldco/promptui\"\n)\n\nfunc Apply(rootDir string, configPath string, environments []string) error {\n\tvar errs []error\n\tif strings.Trim(configPath, \" \") == \"\" {\n\t\texit.Fatal(\"config path cannot be empty!\")\n\t}\n\tconfigFilePath := path.Join(rootDir, configPath)\n\tprojectConfig := projectconfig.LoadConfig(configFilePath)\n\n\tif len(environments) == 0 {\n\t\tfmt.Println(`Choose the environments to apply. This will create infrastructure, CI pipelines, etc.\nAt this point, real things will be generated that may cost money!\nOnly a single environment may be suitable for an initial test, but for a real system we suggest setting up both staging and production environments.`)\n\t\tenvironments = promptEnvironments()\n\t}\n\n\tflog.Infof(\":mag: checking project %s's module requirements.\", projectConfig.Name)\n\n\terrs = modulesWalkCmd(\"check\", rootDir, projectConfig, \"check\", environments, false, false)\n\t// Check operation walks through all modules and can return multiple errors\n\tif len(errs) > 0 {\n\t\tmsg := \"\"\n\t\tfor i := 0; i < len(errs); i++ {\n\t\t\tmsg += \"- \" + errs[i].Error()\n\t\t}\n\t\treturn errors.New(fmt.Sprintf(\"The following Module check(s) failed: \\n%s\", msg))\n\t}\n\n\tflog.Infof(\":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.\", projectConfig.Name)\n\n\tflog.Infof(\"Cloud provider: %s\", \"AWS\") // will this come from the config?\n\n\tflog.Infof(\"Runtime platform: %s\", \"Kubernetes\")\n\n\tflog.Infof(\"Infrastructure executor: %s\", \"Terraform\")\n\n\terrs = modulesWalkCmd(\"apply\", rootDir, projectConfig, \"apply\", environments, true, true)\n\tif len(errs) > 0 {\n\t\treturn errors.New(fmt.Sprintf(\"Module Apply failed: %s\", errs[0]))\n\t}\n\n\tflog.Infof(\":check_mark_button: Done.\")\n\n\tflog.Infof(\"Your projects and infrastructure have been successfully created.  Here are some useful links and commands to get you started:\")\n\terrs = modulesWalkCmd(\"summary\", rootDir, projectConfig, \"summary\", environments, true, true)\n\tif len(errs) > 0 {\n\t\treturn errors.New(fmt.Sprintf(\"Module summary failed: %s\", errs[0]))\n\t}\n\treturn nil\n}\n\nfunc modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, operation string, environments []string, bailOnError bool, shouldPipeStderr bool) []error {\n\tvar moduleErrors []error\n\tgraph := projectConfig.GetDAG()\n\troot := []dag.Vertex{projectconfig.GraphRootName}\n\tenvironmentArg := fmt.Sprintf(\"ENVIRONMENT=%s\", strings.Join(environments, \",\"))\n\terr := graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error {\n\t\t// Don't process the root\n\t\tif depth == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tname := v.(string)\n\t\tmod := projectConfig.Modules[name]\n\t\t// Add env vars for the makefile\n\t\tenvList := []string{\n\t\t\tenvironmentArg,\n\t\t\tfmt.Sprintf(\"PROJECT_NAME=%s\", projectConfig.Name),\n\t\t\tfmt.Sprintf(\"PROJECT_DIR=%s\", path.Join(dir, mod.Files.Directory)),\n\t\t\tfmt.Sprintf(\"REPOSITORY=%s\", mod.Files.Repository),\n\t\t}\n\n\t\tmodulePath := module.GetSourceDir(mod.Files.Source)\n\t\t// Passed in `dir` will only be used to find the project path, not the module path,\n\t\t// unless the module path is relative\n\t\tif module.IsLocal(mod.Files.Source) && !filepath.IsAbs(modulePath) {\n\t\t\tmodulePath = filepath.Join(dir, modulePath)\n\t\t}\n\t\tflog.Debugf(\"Loaded module: %s from %s\", name, modulePath)\n\n\t\t// TODO: in the case user lost the `/tmp` (module source dir), this will fail\n\t\t// and we should redownload the module for the user\n\t\tmodConfig, err := module.ParseModuleConfig(modulePath)\n\t\tif err != nil {\n\t\t\texit.Fatal(\"Failed to load Module: %s\", err)\n\t\t}\n\n\t\tenvVarTranslationMap := modConfig.GetParamEnvVarTranslationMap()\n\t\tenvList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList, envVarTranslationMap)\n\t\tflog.Debugf(\"Env injected: %#v\", envList)\n\n\t\t// only print msg for apply, or else it gets a little spammy\n\t\tif lifecycleName == \"apply\" {\n\t\t\tflog.Infof(\"Executing %s command for %s...\", lifecycleName, modConfig.Name)\n\t\t}\n\t\toperationCommand := getModuleOperationCommand(modConfig, operation)\n\t\texecErr := util.ExecuteCommand(exec.Command(operationCommand[0], operationCommand[1:]...), modulePath, envList, shouldPipeStderr)\n\t\tif execErr != nil {\n\t\t\tformatedErr := errors.New(fmt.Sprintf(\"Module (%s) %s\", modConfig.Name, execErr.Error()))\n\t\t\tif bailOnError {\n\t\t\t\treturn formatedErr\n\t\t\t} else {\n\t\t\t\tmoduleErrors = append(moduleErrors, formatedErr)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tmoduleErrors = append(moduleErrors, err)\n\t}\n\n\treturn moduleErrors\n}\n\nfunc getModuleOperationCommand(mod moduleconfig.ModuleConfig, operation string) (operationCommand []string) {\n\tdefaultCheck := []string{\"make\", \"check\"}\n\tdefaultApply := []string{\"make\"}\n\tdefaultSummary := []string{\"make\", \"summary\"}\n\n\tswitch operation {\n\tcase \"check\":\n\t\tif mod.Commands.Check != \"\" {\n\t\t\toperationCommand = []string{\"sh\", \"-c\", mod.Commands.Check}\n\t\t} else {\n\t\t\toperationCommand = defaultCheck\n\t\t}\n\tcase \"apply\":\n\t\tif mod.Commands.Apply != \"\" {\n\t\t\toperationCommand = []string{\"sh\", \"-c\", mod.Commands.Apply}\n\t\t} else {\n\t\t\toperationCommand = defaultApply\n\t\t}\n\tcase \"summary\":\n\t\tif mod.Commands.Summary != \"\" {\n\t\t\toperationCommand = []string{\"sh\", \"-c\", mod.Commands.Summary}\n\t\t} else {\n\t\t\toperationCommand = defaultSummary\n\t\t}\n\tdefault:\n\t\tpanic(\"Unexpected operation\")\n\t}\n\treturn operationCommand\n}\n\n// promptEnvironments Prompts the user for the environments to apply against and returns a slice of strings representing the environments\nfunc promptEnvironments() []string {\n\titems := map[string][]string{\n\t\t\"Staging\":    {\"stage\"},\n\t\t\"Production\": {\"prod\"},\n\t}\n\n\tlabels := []string{\"Staging\", \"Production\"}\n\n\tproviderPrompt := promptui.Select{\n\t\tLabel: \"Environments\",\n\t\tItems: labels,\n\t}\n\t_, providerResult, err := providerPrompt.Run()\n\tif err != nil {\n\t\tlog.Fatalf(\"Prompt failed %v\\n\", err)\n\t\tpanic(err)\n\t}\n\treturn items[providerResult]\n}\n\nfunc validateEnvironments(applyEnvironments []string) {\n\t// Strict for now, we can brainstorm how much we want to support custom environments later\n\tfor _, env := range applyEnvironments {\n\t\tif env != \"staging\" && env != \"production\" {\n\t\t\texit.Fatal(\"The currently supported environments are \\\"staging\\\" and \\\"production\\\"\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/apply/apply_test.go",
    "content": "package apply_test\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/apply\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/termie/go-shutil\"\n)\n\nfunc TestApply(t *testing.T) {\n\tapplyConfigPath := constants.ZeroProjectYml\n\tapplyEnvironments := []string{\"staging\", \"production\"}\n\tvar tmpDir string\n\n\tt.Run(\"Should run apply and execute make on each folder module\", func(t *testing.T) {\n\t\ttmpDir = setupTmpDir(t, \"../../tests/test_data/apply/\")\n\t\terr := apply.Apply(tmpDir, applyConfigPath, applyEnvironments)\n\t\tassert.FileExists(t, filepath.Join(tmpDir, \"project1/project.out\"))\n\t\tassert.FileExists(t, filepath.Join(tmpDir, \"project2/project.out\"))\n\n\t\tcontent, err := ioutil.ReadFile(filepath.Join(tmpDir, \"project1/project.out\"))\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"foo: bar\\nrepo: github.com/commitdev/project1\\n\", string(content))\n\n\t\tcontent, err = ioutil.ReadFile(filepath.Join(tmpDir, \"project2/project.out\"))\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"baz: qux\\n\", string(content))\n\n\t})\n\n\tt.Run(\"Modules runs command overides\", func(t *testing.T) {\n\t\tcontent, err := ioutil.ReadFile(filepath.Join(tmpDir, \"project2/check.out\"))\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"custom check\\n\", string(content))\n\t})\n\n\tt.Run(\"Zero apply honors the envVarName overwrite from module definition\", func(t *testing.T) {\n\t\tcontent, err := ioutil.ReadFile(filepath.Join(tmpDir, \"project1/feature.out\"))\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"envVarName of viaEnvVarName: baz\\n\", string(content))\n\t})\n\n\tt.Run(\"Modules with failing checks should return error\", func(t *testing.T) {\n\t\ttmpDir = setupTmpDir(t, \"../../tests/test_data/apply-failing/\")\n\n\t\terr := apply.Apply(tmpDir, applyConfigPath, applyEnvironments)\n\t\tassert.Regexp(t, \"^The following Module check\\\\(s\\\\) failed:\", err.Error())\n\t\tassert.Regexp(t, \"Module \\\\(project1\\\\)\", err.Error())\n\t\tassert.Regexp(t, \"Module \\\\(project2\\\\)\", err.Error())\n\t\tassert.Regexp(t, \"Module \\\\(project3\\\\)\", err.Error())\n\t})\n\n}\n\nfunc setupTmpDir(t *testing.T, exampleDirPath string) string {\n\tvar err error\n\ttmpDir := filepath.Join(os.TempDir(), \"apply\")\n\n\terr = os.RemoveAll(tmpDir)\n\tassert.NoError(t, err)\n\n\terr = shutil.CopyTree(exampleDirPath, tmpDir, nil)\n\tassert.NoError(t, err)\n\treturn tmpDir\n}\n"
  },
  {
    "path": "internal/condition/condition.go",
    "content": "// This module is invoked when we do template rendering during \"zero create.\"\n//\n// Each module can have a \"conditions\" section in their zero-module.yml that\n// specifies a condition in the form:\n//\n//   conditions:\n//     - action: ignoreFile\n//       matchField: <the name of a parameter in zero-module.yml>\n//       whenValue: <value for the matchField that triggers this condition>\n//       data:\n//       - <arbitrary string>\n//\n// The structure for this is defined in:\n// internal/config/projectconfig/project_config.go.\n// The definition is in that file simply to avoid cyclic dependencies; but\n// The logic for each type of condition exists here.\n//\n// See: internal/generate/generate_modules.go\n// See: internal/config/projectconfig/project_config.go\n//\npackage condition\n\nimport (\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n)\n\n// Function dispatch for any kind of condition.\nfunc Perform(cond projectconfig.Condition, mod projectconfig.Module) {\n\tvalue, found := mod.Parameters[cond.MatchField]\n\n\t// Exit if the condition isn't met.\n\tif !found || value != cond.WhenValue {\n\t\treturn\n\t}\n\n\t// Okay, the condition was met, let's execute it.\n\tswitch cond.Action {\n\tcase \"ignoreFile\":\n\t\tignoreFile(cond.Data, mod)\n\t}\n}\n\n// Excludes paths from template rendering.\n// This occurs after-the-fact. That is, we render all templates to disk, then\n// use 'paths' to determine which files and directories to remove from disk.\n//\nfunc ignoreFile(paths []string, mod projectconfig.Module) {\n\tfor _, file := range paths {\n\t\tfilepath := path.Join(mod.Files.Directory, file)\n\t\tos.RemoveAll(filepath)\n\t}\n}\n"
  },
  {
    "path": "internal/condition/condition_test.go",
    "content": "package condition_test\n\nimport (\n\t\"encoding/base64\"\n\t\"math/rand\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/condition\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n)\n\nfunc testSetup(paramKey, paramValue string) (string, projectconfig.Module) {\n\tbytes := make([]byte, 15)\n\t_, _ = rand.Read(bytes)\n\tname := string(base64.StdEncoding.EncodeToString(bytes[:]))\n\n\t_, _ = os.Create(name)\n\n\tparams := make(projectconfig.Parameters)\n\tparams[paramKey] = paramValue\n\n\tmod := projectconfig.Module{\n\t\tParameters: params,\n\t\tFiles: projectconfig.Files{\n\t\t\tDirectory: \".\",\n\t\t\tSource:    \".\",\n\t\t},\n\t}\n\n\treturn name, mod\n}\n\nfunc TestPerformIgnoreFileConditionNotMet(t *testing.T) {\n\tfield := \"testField\"\n\tvalue := \"trigger\"\n\n\tfilename, mod := testSetup(field, \"other value\")\n\tdefer os.Remove(filename)\n\n\tcond := projectconfig.Condition{\n\t\tAction:     \"ignoreFile\",\n\t\tMatchField: field,\n\t\tWhenValue:  value,\n\t\tData:       []string{filename},\n\t}\n\tcondition.Perform(cond, mod)\n\n\t_, err := os.Stat(filename)\n\tif err != nil && !os.IsExist(err) {\n\t\tt.Errorf(\"Expected %v not to be removed\\n\", filename)\n\t}\n}\n\nfunc TestPerformIgnoreFileConditionMet(t *testing.T) {\n\tfield := \"testField\"\n\tvalue := \"trigger\"\n\n\tfilename, mod := testSetup(field, value)\n\tdefer os.Remove(filename)\n\n\tcond := projectconfig.Condition{\n\t\tAction:     \"ignoreFile\",\n\t\tMatchField: field,\n\t\tWhenValue:  value,\n\t\tData:       []string{filename},\n\t}\n\tcondition.Perform(cond, mod)\n\n\t_, err := os.Stat(filename)\n\tif !os.IsNotExist(err) {\n\t\tt.Errorf(\"Expected %v to be removed\\n\", filename)\n\t}\n}\n"
  },
  {
    "path": "internal/config/moduleconfig/module_config.go",
    "content": "package moduleconfig\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"reflect\"\n\t\"strings\"\n\n\tgoVersion \"github.com/hashicorp/go-version\"\n\tyaml \"gopkg.in/yaml.v2\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/commitdev/zero/version\"\n\t\"github.com/iancoleman/strcase\"\n)\n\ntype ModuleConfig struct {\n\tName                string\n\tDescription         string\n\tAuthor              string\n\tCommands            ModuleCommands `yaml:\"commands,omitempty\"`\n\tDependsOn           []string       `yaml:\"dependsOn,omitempty\"`\n\tTemplateConfig      `yaml:\"template\"`\n\tRequiredCredentials []string           `yaml:\"requiredCredentials\"`\n\tZeroVersion         VersionConstraints `yaml:\"zeroVersion,omitempty\"`\n\tParameters          []Parameter\n\tConditions          []Condition `yaml:\"conditions,omitempty\"`\n}\n\ntype ModuleCommands struct {\n\tApply   string `yaml:\"apply,omitempty\"`\n\tCheck   string `yaml:\"check,omitempty\"`\n\tSummary string `yaml:\"summary,omitempty\"`\n}\n\nfunc checkVersionAgainstConstrains(vc VersionConstraints, versionString string) bool {\n\tv, err := goVersion.NewVersion(versionString)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn vc.Check(v)\n}\n\n// ValidateZeroVersion receives a module config, and returns whether the running zero's binary\n// is compatible with the module\nfunc ValidateZeroVersion(mc ModuleConfig) bool {\n\tif mc.ZeroVersion.String() == \"\" {\n\t\treturn true\n\t}\n\n\tzeroVersion := version.AppVersion\n\tflog.Debugf(\"Checking Zero version (%s) against %s\", zeroVersion, mc.ZeroVersion)\n\n\t// Unreleased versions or test runs, defaults to SNAPSHOT when not declared\n\tif zeroVersion == \"SNAPSHOT\" {\n\t\treturn true\n\t}\n\n\treturn checkVersionAgainstConstrains(mc.ZeroVersion, zeroVersion)\n}\n\ntype Parameter struct {\n\tField               string\n\tLabel               string        `yaml:\"label,omitempty\"`\n\tOptions             yaml.MapSlice `yaml:\"options,omitempty\"`\n\tExecute             string        `yaml:\"execute,omitempty\"`\n\tValue               string        `yaml:\"value,omitempty\"`\n\tDefault             string        `yaml:\"default,omitempty\"`\n\tInfo                string        `yaml:\"info,omitempty\"`\n\tFieldValidation     Validate      `yaml:\"fieldValidation,omitempty\"`\n\tType                string        `yaml:\"type,omitempty\"`\n\tOmitFromProjectFile bool          `yaml:\"omitFromProjectFile,omitempty\"`\n\tConditions          []Condition   `yaml:\"conditions,omitempty\"`\n\tEnvVarName          string        `yaml:\"envVarName,omitempty\"`\n}\n\ntype Condition struct {\n\tAction     string   `yaml:\"action\"`\n\tMatchField string   `yaml:\"matchField\"`\n\tWhenValue  string   `yaml:\"whenValue\"`\n\tData       []string `yaml:\"data,omitempty\"`\n\tElseValue  string   `yaml:\"elseValue,omitempty\"`\n}\n\ntype Validate struct {\n\tType         string `yaml:\"type,omitempty\"`\n\tValue        string `yaml:\"value,omitempty\"`\n\tErrorMessage string `yaml:\"errorMessage,omitempty\"`\n}\n\ntype TemplateConfig struct {\n\tStrictMode bool\n\tDelimiters []string\n\tInputDir   string `yaml:\"inputDir\"`\n\tOutputDir  string `yaml:\"outputDir\"`\n}\n\ntype VersionConstraints struct {\n\tgoVersion.Constraints\n}\n\n// A \"nice\" wrapper around findMissing()\nfunc (cfg ModuleConfig) collectMissing() []string {\n\tvar missing []string\n\tfindMissing(reflect.ValueOf(cfg), \"\", \"\", &missing)\n\n\treturn missing\n}\n\n// GetParamEnvVarTranslationMap returns a map for translating parameter's `Field` into env-var keys\n// It loops through each parameter then adds to translation map if applicable\n// for zero apply / zero init's prompt execute,\n// this is useful for translating params like AWS credentials for running the AWS cli\nfunc (cfg ModuleConfig) GetParamEnvVarTranslationMap() map[string]string {\n\ttranslationMap := make(map[string]string)\n\tfor i := 0; i < len(cfg.Parameters); i++ {\n\t\tparam := cfg.Parameters[i]\n\t\tif param.EnvVarName != \"\" {\n\t\t\ttranslationMap[param.Field] = param.EnvVarName\n\t\t}\n\t}\n\treturn translationMap\n}\n\nfunc LoadModuleConfig(filePath string) (ModuleConfig, error) {\n\tconfig := ModuleConfig{}\n\n\tdata, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\treturn config, err\n\t}\n\n\terr = yaml.Unmarshal(data, &config)\n\tif err != nil {\n\t\treturn config, err\n\t}\n\n\tmissing := config.collectMissing()\n\tif len(missing) > 0 {\n\t\tflog.Errorf(\"%v is missing information\", filePath)\n\n\t\tfor _, m := range missing {\n\t\t\tflog.Errorf(\"\\t %v\", m)\n\t\t}\n\n\t\tlog.Fatal(\"\")\n\t}\n\n\tif !ValidateZeroVersion(config) {\n\t\tconstraint := config.ZeroVersion.Constraints.String()\n\t\terrTpl := `Module(%s) requires Zero to be version %s. Your current Zero version is: %s\nPlease update your Zero version to %s.\nPlease check %s for available releases.`\n\t\treturn config, errors.New(fmt.Sprintf(errTpl, config.Name, constraint, version.AppVersion, constraint, constants.ZeroReleaseURL))\n\t}\n\treturn config, nil\n}\n\n// Recurses through a datastructure to find any missing data.\n// This assumes several things:\n// 1. The structure matches that defined by ModuleConfig and its child datastructures.\n// 2. YAML struct field metadata is sufficient to define whether an attribute is missing or not.\n//    That is, \"yaml:foo,omitempty\" tells us this is not a required field because we can omit it.\n// 3. Slices and arrays are assumed to be optional.\n//\n// As this function recurses through the datastructure, it builds up a string\n// path representing each node's path within the datastructure.\n// If the value of the current node is equal to the zero value for its datatype\n// and its struct field does *not* have a \"omitempty\" value, then we assume it\n// is missing and add it to the resultset.\nfunc findMissing(obj reflect.Value, path, metadata string, missing *[]string) {\n\tt := obj.Type()\n\tswitch t.Kind() {\n\tcase reflect.String:\n\t\tif obj.String() == \"\" && !strings.Contains(metadata, \"omitempty\") {\n\t\t\t*missing = append(*missing, path)\n\t\t}\n\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < obj.Len(); i++ {\n\t\t\tprefix := fmt.Sprintf(\"%v[%v]\", path, i)\n\t\t\tfindMissing(obj.Index(i), prefix, metadata, missing)\n\t\t}\n\n\tcase reflect.Struct:\n\t\tfor i := 0; i < t.NumField(); i++ {\n\t\t\tfieldType := t.Field(i)\n\t\t\tfieldTags, _ := fieldType.Tag.Lookup(\"yaml\")\n\t\t\tfieldVal := obj.Field(i)\n\n\t\t\ttags := strings.Split(fieldTags, \",\")\n\n\t\t\thasOmitEmpty := false\n\t\t\t// We have all metadata yaml tags, now let's remove the \"omitempty\" tag if\n\t\t\t// it is present.\n\t\t\t// Then if we have only one tag remaining, this must be the expected yaml\n\t\t\t// identifer.\n\t\t\t// Otherwise the name of the yaml identifier should match the struct\n\t\t\t// attribute name.\n\t\t\tfor i := len(tags) - 1; i >= 0; i-- {\n\t\t\t\ttag := tags[i]\n\t\t\t\tif tag == \"omitempty\" {\n\t\t\t\t\thasOmitEmpty = true\n\t\t\t\t\ttags = append(tags[:i], tags[i+1:]...)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyamlName := strcase.ToLowerCamel(fieldType.Name)\n\t\t\tif len(tags) == 1 && tags[0] != \"\" { // For some reason, empty tag lists are giving a count of 1.\n\t\t\t\tyamlName = tags[0]\n\t\t\t}\n\n\t\t\tprefix := yamlName\n\t\t\tif path != \"\" {\n\t\t\t\tprefix = fmt.Sprintf(\"%v.%v\", path, yamlName)\n\t\t\t}\n\n\t\t\tzeroVal := reflect.Zero(fieldType.Type)\n\t\t\tif fieldVal == zeroVal && !hasOmitEmpty {\n\t\t\t\t*missing = append(*missing, prefix)\n\t\t\t}\n\n\t\t\tfindMissing(fieldVal, prefix, fieldTags, missing)\n\t\t}\n\t}\n}\n\n// SummarizeParameters receives all parameters gathered from prompts during `Zero init`\n// and based on module definition to construct the parameters for each module for zero-project.yml\n// filters out parameters defined as OmitFromProjectFile: true\nfunc SummarizeParameters(module ModuleConfig, allParams map[string]string) map[string]string {\n\tmoduleParams := make(projectconfig.Parameters)\n\t// Loop through all the prompted values and find the ones relevant to this module\n\tfor parameterKey, parameterValue := range allParams {\n\t\tfor _, moduleParameter := range module.Parameters {\n\t\t\tif moduleParameter.Field == parameterKey {\n\t\t\t\tif moduleParameter.OmitFromProjectFile {\n\t\t\t\t\tflog.Debugf(\"Omitted %s from %s\", parameterKey, module.Name)\n\t\t\t\t} else {\n\t\t\t\t\tmoduleParams[parameterKey] = parameterValue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn moduleParams\n}\n\n// SummarizeConditions based on conditions from zero-module.yml\n// creates and returns slice of conditions for project config\nfunc SummarizeConditions(module ModuleConfig) []projectconfig.Condition {\n\tmoduleConditions := make([]projectconfig.Condition, len(module.Conditions))\n\n\tfor i, condition := range module.Conditions {\n\t\tmoduleConditions[i] = projectconfig.Condition{\n\t\t\tAction:     condition.Action,\n\t\t\tMatchField: condition.MatchField,\n\t\t\tWhenValue:  condition.WhenValue,\n\t\t\tData:       condition.Data,\n\t\t}\n\t}\n\treturn moduleConditions\n}\n\n// GetFirstConditionElseValue returns the default value of the first condition that has a default\nfunc GetFirstConditionElseValue(parameter Parameter) string {\n\tfor _, condition := range parameter.Conditions {\n\t\tif condition.ElseValue != \"\" {\n\t\t\treturn condition.ElseValue\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// UnmarshalYAML Parses a version constraint string into go-version constraint during yaml parsing\nfunc (semVer *VersionConstraints) UnmarshalYAML(unmarshal func(interface{}) error) error {\n\tvar versionString string\n\terr := unmarshal(&versionString)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif versionString != \"\" {\n\t\tconstraints, constErr := goVersion.NewConstraint(versionString)\n\t\t// If an invalid constraint is declared in a module\n\t\t// instead of erroring out we just print a warning message\n\t\tif constErr != nil {\n\t\t\tflog.Warnf(\"Zero version constraint invalid format: %s\", constErr)\n\t\t}\n\n\t\t*semVer = VersionConstraints{constraints}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/config/projectconfig/init.go",
    "content": "package projectconfig\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path\"\n\t\"text/template\"\n\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/internal/util\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nconst zeroProjectConfigTemplate = `\n# zero-project.yml file containing all the required modules and their configuration.\n# This file is generated by the zero init command but can be modified by hand before running zero create.\n# Do not check this into source control, it may contain sensitive credentials.\n\nname: {{.Name}}\n\nshouldPushRepositories: {{.ShouldPushRepositories | printf \"%v\"}}\n\nmodules:\n{{.Modules}}\n`\n\nvar RootDir = \"./\"\n\nfunc SetRootDir(dir string) {\n\tRootDir = dir\n}\n\n// CreateProjectConfigFile extracts the required content for zero project config file then write to disk.\nfunc CreateProjectConfigFile(dir string, projectName string, projectContext *ZeroProjectConfig) error {\n\tcontent, err := getProjectFileContent(*projectContext)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfilePath := path.Join(dir, projectName, constants.ZeroProjectYml)\n\tflog.Debugf(\"Project file path: %s\", filePath)\n\twriteErr := ioutil.WriteFile(filePath, []byte(content), 0644)\n\tif writeErr != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc getProjectFileContent(projectConfig ZeroProjectConfig) (string, error) {\n\tvar tplBuffer bytes.Buffer\n\ttmpl, err := template.New(\"projectConfig\").Parse(zeroProjectConfigTemplate)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(projectConfig.Modules) == 0 {\n\t\treturn \"\", fmt.Errorf(\"Invalid project config, expected config modules to be non-empty\")\n\t}\n\n\tpConfigModules, err := yaml.Marshal(projectConfig.Modules)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tt := struct {\n\t\tName                   string\n\t\tShouldPushRepositories bool\n\t\tModules                string\n\t}{\n\t\tName:                   projectConfig.Name,\n\t\tShouldPushRepositories: projectConfig.ShouldPushRepositories,\n\t\tModules:                util.IndentString(string(pConfigModules), 2),\n\t}\n\n\tif err := tmpl.Execute(&tplBuffer, t); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn tplBuffer.String(), nil\n}\n"
  },
  {
    "path": "internal/config/projectconfig/init_test.go",
    "content": "package projectconfig_test\n\nimport (\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCreateProjectConfigFile(t *testing.T) {\n\tconst testDir = \"../../test-sandbox\"\n\tprojectName := \"test-project\"\n\n\tprojectconfig.SetRootDir(testDir)\n\tdefer os.RemoveAll(testDir)\n\n\ttestDirPath := path.Join(projectconfig.RootDir, projectName)\n\n\t// create sandbox dir\n\terr := os.MkdirAll(testDirPath, os.ModePerm)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpectedConfig := &projectconfig.ZeroProjectConfig{\n\t\tName:                   projectName,\n\t\tShouldPushRepositories: false,\n\t\tModules:                eksGoReactSampleModules(),\n\t}\n\tassert.NoError(t, projectconfig.CreateProjectConfigFile(projectconfig.RootDir, projectName, expectedConfig))\n\n\t// make sure the file exists\n\tif _, err := os.Stat(path.Join(testDirPath, constants.ZeroProjectYml)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Run(\"Should return a valid project config\", func(t *testing.T) {\n\t\tresultConfig := projectconfig.LoadConfig(path.Join(testDirPath, constants.ZeroProjectYml))\n\n\t\tif !cmp.Equal(expectedConfig, resultConfig, cmpopts.EquateEmpty()) {\n\t\t\tt.Errorf(\"projectconfig.ZeroProjectConfig.Unmarshal mismatch (-expected +result):\\n%s\", cmp.Diff(expectedConfig, resultConfig))\n\t\t}\n\t})\n\n\tt.Run(\"Should fail if modules are missing from project config\", func(t *testing.T) {\n\t\texpectedConfig.Modules = nil\n\t\tassert.Error(t, projectconfig.CreateProjectConfigFile(projectconfig.RootDir, projectName, expectedConfig))\n\t})\n\n}\n"
  },
  {
    "path": "internal/config/projectconfig/project_config.go",
    "content": "package projectconfig\n\nimport (\n\t\"errors\"\n\t\"io/ioutil\"\n\n\t\"github.com/hashicorp/terraform/dag\"\n\t\"github.com/k0kubun/pp\"\n\t\"gopkg.in/yaml.v2\"\n\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n)\n\n// GraphRootName represents the root of the graph of modules in a project\nconst GraphRootName = \"graphRoot\"\n\ntype ZeroProjectConfig struct {\n\tName                   string `yaml:\"name\"`\n\tShouldPushRepositories bool   `yaml:\"shouldPushRepositories\"`\n\tParameters             map[string]string\n\tModules                Modules `yaml:\"modules\"`\n}\n\ntype Modules map[string]Module\n\ntype Module struct {\n\tDependsOn  []string   `yaml:\"dependsOn,omitempty\"`\n\tParameters Parameters `yaml:\"parameters,omitempty\"`\n\tFiles      Files\n\tConditions []Condition `yaml:\"conditions,omitempty\"`\n}\n\n// ReadVendorCredentialsFromModule uses parsed project-config's module\n// based on vendor parameter, retrieve the vendor's credential\n// for pre-defined functionalities (eg: Github api key for pushing repos to github)\nfunc ReadVendorCredentialsFromModule(m Module, vendor string) (error, string) {\n\t// this mapping could be useful for module config as well\n\tvendorToParamMap := map[string]string{\n\t\t\"github\":   \"githubAccessToken\",\n\t\t\"circleci\": \"circleciApiKey\",\n\t}\n\tif parameterKey, ok := vendorToParamMap[vendor]; ok {\n\t\tif val, ok := m.Parameters[parameterKey]; ok {\n\t\t\treturn nil, val\n\t\t}\n\t\treturn errors.New(\"Parameter not found in module.\"), \"\"\n\t}\n\treturn errors.New(\"Unsupported vendor provided.\"), \"\"\n}\n\ntype Parameters map[string]string\n\ntype Condition struct {\n\tAction     string   `yaml:\"action\"`\n\tMatchField string   `yaml:\"matchField\"`\n\tWhenValue  string   `yaml:\"whenValue\"`\n\tData       []string `yaml:\"data,omitempty\"`\n}\n\ntype Files struct {\n\tDirectory  string `yaml:\"dir,omitempty\"`\n\tRepository string `yaml:\"repo,omitempty\"`\n\tSource     string\n}\n\nfunc LoadConfig(filePath string) *ZeroProjectConfig {\n\tconfig := &ZeroProjectConfig{}\n\tdata, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\texit.Fatal(\"failed to read config: %v\", err)\n\t}\n\terr = yaml.Unmarshal(data, &config)\n\tif err != nil {\n\t\texit.Fatal(\"failed to parse config: %v\", err)\n\t}\n\tflog.Debugf(\"Loaded project config: %s from %s\", config.Name, filePath)\n\treturn config\n}\n\nfunc (c *ZeroProjectConfig) Print() {\n\tpp.Println(c)\n}\n\n// GetDAG returns a graph of the module names used in this project config\nfunc (c *ZeroProjectConfig) GetDAG() dag.AcyclicGraph {\n\tvar g dag.AcyclicGraph\n\n\t// Add vertices to graph\n\tg.Add(GraphRootName)\n\tfor name := range c.Modules {\n\t\tg.Add(name)\n\t}\n\n\t// Connect modules in graph\n\tfor name, m := range c.Modules {\n\t\tif len(m.DependsOn) == 0 {\n\t\t\tg.Connect(dag.BasicEdge(GraphRootName, name))\n\t\t} else {\n\t\t\tfor _, dependencyName := range m.DependsOn {\n\t\t\t\tg.Connect(dag.BasicEdge(dependencyName, name))\n\t\t\t}\n\t\t}\n\t}\n\treturn g\n}\n\nfunc NewModule(parameters Parameters, directory string, repository string, source string, dependsOn []string, conditions []Condition) Module {\n\treturn Module{\n\t\tParameters: parameters,\n\t\tDependsOn:  dependsOn,\n\t\tFiles: Files{\n\t\t\tDirectory:  directory,\n\t\t\tRepository: repository,\n\t\t\tSource:     source,\n\t\t},\n\t\tConditions: conditions,\n\t}\n}\n"
  },
  {
    "path": "internal/config/projectconfig/project_config_test.go",
    "content": "package projectconfig_test\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoadConfig(t *testing.T) {\n\tfile, err := ioutil.TempFile(os.TempDir(), \"config.yml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer os.Remove(file.Name())\n\tfile.Write([]byte(validConfigContent()))\n\tfilePath := file.Name()\n\n\twant := &projectconfig.ZeroProjectConfig{\n\t\tName:                   \"abc\",\n\t\tShouldPushRepositories: true,\n\t\tModules:                eksGoReactSampleModules(),\n\t}\n\n\tt.Run(\"Should load and unmarshal config correctly\", func(t *testing.T) {\n\t\tgot := projectconfig.LoadConfig(filePath)\n\t\tif !cmp.Equal(want, got, cmpopts.EquateEmpty()) {\n\t\t\tt.Errorf(\"projectconfig.ZeroProjectConfig.Unmarshal mismatch (-want +got):\\n%s\", cmp.Diff(want, got))\n\t\t}\n\t})\n}\n\nfunc eksGoReactSampleModules() projectconfig.Modules {\n\tparameters := projectconfig.Parameters{\"a\": \"b\"}\n\treturn projectconfig.Modules{\n\t\t\"aws-eks-stack\":  projectconfig.NewModule(parameters, \"zero-aws-eks-stack\", \"github.com/something/repo1\", \"github.com/commitdev/zero-aws-eks-stack\", []string{}, []projectconfig.Condition{}),\n\t\t\"backend-go\":     projectconfig.NewModule(parameters, \"zero-backend-go\", \"github.com/something/repo2\", \"github.com/commitdev/zero-backend-go\", []string{}, []projectconfig.Condition{}),\n\t\t\"frontend-react\": projectconfig.NewModule(parameters, \"zero-frontend-react\", \"github.com/something/repo3\", \"github.com/commitdev/zero-frontend-react\", []string{}, []projectconfig.Condition{}),\n\t}\n}\n\nfunc validConfigContent() string {\n\treturn `\n# Templated zero-project.yml file\nname: abc\n\nshouldPushRepositories: true\n\nmodules:\n  aws-eks-stack:\n    parameters:\n      a: b\n    files:\n      dir: zero-aws-eks-stack\n      repo: github.com/something/repo1\n      source: github.com/commitdev/zero-aws-eks-stack\n  backend-go:\n    parameters:\n      a: b\n    files:\n      dir: zero-backend-go\n      repo: github.com/something/repo2\n      source: github.com/commitdev/zero-backend-go\n  frontend-react:\n    parameters:\n      a: b\n    files:\n      dir: zero-frontend-react\n      repo: github.com/something/repo3\n      source: github.com/commitdev/zero-frontend-react\n`\n}\n\nfunc TestProjectConfigModuleGraph(t *testing.T) {\n\tconfigPath := filepath.Join(\"../../../tests/test_data/projectconfig/\", constants.ZeroProjectYml)\n\n\tt.Run(\"Should generate a valid, correct graph based on the project config\", func(t *testing.T) {\n\t\tpc := projectconfig.LoadConfig(configPath)\n\t\tgraph := pc.GetDAG()\n\n\t\t// Validate the graph\n\t\tassert.NoError(t, graph.Validate())\n\n\t\t// Check the structure of the graph\n\t\troot, err := graph.Root()\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"graphRoot\", root)\n\n\t\twant := `graphRoot\n  project1\nproject1\n  project2\n  project3\nproject2\n  project4\nproject3\n  project4\n  project5\nproject4\nproject5\n`\n\t\tassert.Equal(t, want, graph.String())\n\n\t})\n\n}\n"
  },
  {
    "path": "internal/constants/constants.go",
    "content": "package constants\n\nconst (\n\tTmpRegistryYml    = \"tmp/registry.yaml\"\n\tTemplatesDir      = \"tmp/templates\"\n\tZeroProjectYml    = \"zero-project.yml\"\n\tZeroModuleYml     = \"zero-module.yml\"\n\tZeroHomeDirectory = \".zero\"\n\tIgnoredPaths      = \"(?i)zero.module.yml|.git/\"\n\tTemplateExtn      = \".tmpl\"\n\n\t// prompt constants\n\n\tMaxPnameLength     = 16\n\tMaxOnameLength     = 39\n\tRegexValidation    = \"regex\"\n\tFunctionValidation = \"function\"\n\tZeroReleaseURL     = \"https://github.com/commitdev/zero/releases\"\n)\n"
  },
  {
    "path": "internal/generate/generate_modules.go",
    "content": "package generate\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sync\"\n\t\"text/template\"\n\n\t\"github.com/commitdev/zero/internal/condition\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/internal/module\"\n\t\"github.com/commitdev/zero/internal/util\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/commitdev/zero/pkg/util/fs\"\n\n\t\"github.com/gabriel-vasile/mimetype\"\n)\n\n// Generate accepts a projectconfig struct and renders the templates for all referenced modules\nfunc Generate(projectConfig projectconfig.ZeroProjectConfig, overwriteFiles bool) error {\n\tflog.Infof(\":clock: Fetching Modules\")\n\n\t// Make sure module sources are on disk\n\twg := sync.WaitGroup{}\n\twg.Add(len(projectConfig.Modules))\n\tfor _, mod := range projectConfig.Modules {\n\t\tgo module.FetchModule(mod.Files.Source, &wg)\n\t}\n\twg.Wait()\n\n\tflog.Infof(\":memo: Rendering Modules\")\n\tfor _, mod := range projectConfig.Modules {\n\t\t// Load module configuration\n\t\tmoduleConfig, err := module.ParseModuleConfig(mod.Files.Source)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to load module (%s):  %v\", mod.Files.Source, err)\n\t\t}\n\n\t\tmoduleDir := path.Join(module.GetSourceDir(mod.Files.Source), moduleConfig.InputDir)\n\t\tdelimiters := moduleConfig.Delimiters\n\t\toutputDir := mod.Files.Directory\n\n\t\t// Data that will be passed in to each template\n\t\ttemplateData := struct {\n\t\t\tName       string\n\t\t\tParams     projectconfig.Parameters\n\t\t\tFiles      projectconfig.Files\n\t\t\tConditions []projectconfig.Condition\n\t\t}{\n\t\t\tprojectConfig.Name,\n\t\t\tmod.Parameters,\n\t\t\tmod.Files,\n\t\t\tmod.Conditions,\n\t\t}\n\n\t\ttxtTypeFiles, binTypeFiles := sortFileType(moduleDir, outputDir, overwriteFiles)\n\n\t\texecuteTemplates(txtTypeFiles, templateData, delimiters)\n\t\tcopyBinFiles(binTypeFiles)\n\n\t\tfor _, cond := range mod.Conditions {\n\t\t\tcondition.Perform(cond, mod)\n\t\t}\n\t}\n\treturn nil\n}\n\ntype fileConfig struct {\n\tsource      string\n\tdestination string\n\tmodeBits    os.FileMode\n}\n\n// sortFileType walks the module directory to find and classify all files into bin / text/plain (non-bin) types.\nfunc sortFileType(moduleDir string, outputDir string, overwrite bool) ([]*fileConfig, []*fileConfig) {\n\tbinTypeFiles := []*fileConfig{}\n\ttxtTypeFiles := []*fileConfig{}\n\n\tpaths, err := getAllFilePathsInDirectory(moduleDir)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, path := range paths {\n\t\tignoredPaths, _ := regexp.Compile(constants.IgnoredPaths)\n\t\tif ignoredPaths.MatchString(path) {\n\t\t\tcontinue\n\t\t}\n\n\t\toutputPath := fs.ReplacePath(path, moduleDir, outputDir)\n\n\t\tif !overwrite {\n\t\t\tif exists, _ := fs.FileExists(outputPath); exists {\n\t\t\t\tflog.Warnf(\"%v already exists. skipping.\", outputPath)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tfileInfo, err := os.Stat(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// detect the file type\n\t\tdetectedMIME, err := mimetype.DetectFile(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// detect root file type\n\t\tisBinary := true\n\t\tfor mime := detectedMIME; mime != nil; mime = mime.Parent() {\n\t\t\tif mime.Is(\"text/plain\") {\n\t\t\t\tisBinary = false\n\t\t\t}\n\t\t}\n\n\t\tif isBinary {\n\t\t\tbinTypeFiles = append(binTypeFiles, &fileConfig{\n\t\t\t\tsource:      path,\n\t\t\t\tdestination: outputPath,\n\t\t\t\tmodeBits:    fileInfo.Mode().Perm(),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\ttxtTypeFiles = append(txtTypeFiles, &fileConfig{\n\t\t\tsource:      path,\n\t\t\tdestination: outputPath,\n\t\t\tmodeBits:    fileInfo.Mode().Perm(),\n\t\t})\n\t}\n\treturn txtTypeFiles, binTypeFiles\n}\n\n// getAllFilePathsInDirectory Recursively get all file paths in directory, including sub-directories.\nfunc getAllFilePathsInDirectory(moduleDir string) ([]string, error) {\n\tvar paths []string\n\terr := filepath.Walk(moduleDir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\tpaths = append(paths, path)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn paths, nil\n}\n\nfunc executeTemplates(templates []*fileConfig, data interface{}, delimiters []string) {\n\tvar wg sync.WaitGroup\n\tleftDelim := delimiters[0]\n\trightDelim := delimiters[1]\n\tif leftDelim == \"\" {\n\t\tleftDelim = \"{{\"\n\t}\n\tif rightDelim == \"\" {\n\t\trightDelim = \"}}\"\n\t}\n\t// flog.Infof(\"Templating params:\")\n\t// pp.Println(data)\n\n\tfor _, tmpltConfig := range templates {\n\t\tsource := tmpltConfig.source\n\t\tdest := tmpltConfig.destination\n\n\t\toutputDirPath, _ := path.Split(dest)\n\t\terr := fs.CreateDirs(outputDirPath)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error creating directory '%s': %v\", source, err)\n\t\t}\n\t\tf, err := os.Create(dest)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error initializing file '%s'\", err)\n\t\t}\n\n\t\terr = f.Chmod(tmpltConfig.modeBits)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error changing mode bits '%s'\", err)\n\t\t}\n\n\t\t// @TODO if strict mode then only copy file\n\t\tname := path.Base(source)\n\t\ttemplate, err := template.New(name).Delims(leftDelim, rightDelim).Funcs(util.FuncMap).ParseFiles(source)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error in template '%s': %v\", source, err)\n\t\t}\n\t\terr = template.Execute(f, data)\n\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error templating '%s': %v\", source, err)\n\t\t} else {\n\t\t\tflog.Successf(\"Finished templating : %s\", dest)\n\t\t}\n\t}\n\n\twg.Wait()\n}\n\nfunc copyBinFiles(binTypeFiles []*fileConfig) {\n\tfor _, binFile := range binTypeFiles {\n\t\tsource := binFile.source\n\t\tdest := binFile.destination\n\n\t\t// create dir\n\t\toutputDirPath, _ := path.Split(dest)\n\t\terr := fs.CreateDirs(outputDirPath)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error creating directory '%s': %v\", source, err)\n\t\t}\n\n\t\t// create refs to src and dest\n\t\tfrom, err := os.Open(source)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error opening file to read '%s' : %v\", source, err)\n\t\t}\n\t\tdefer from.Close()\n\n\t\tto, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, binFile.modeBits)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t\tflog.Errorf(\"Error creating file '%s': %v\", dest, err)\n\t\t}\n\t\tdefer to.Close()\n\n\t\t// copy file\n\t\t_, err = io.Copy(to, from)\n\t\tif err != nil {\n\t\t\tflog.Errorf(\"Error copying file '%s' : %v\", source, err)\n\t\t} else {\n\t\t\tflog.Successf(\"Finished copying file : %s\", dest)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/generate/generate_test.go",
    "content": "package generate_test\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/generate\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst baseTestFixturesDir = \"../../tests/test_data/generate/\"\n\nfunc setupTeardown(t *testing.T) (func(t *testing.T), string) {\n\ttmpDir := filepath.Join(os.TempDir(), \"generate\")\n\tos.MkdirAll(tmpDir, 0755)\n\tos.RemoveAll(tmpDir)\n\treturn func(t *testing.T) {\n\t\tos.RemoveAll(tmpDir)\n\t}, tmpDir\n}\n\nfunc TestGenerateModules(t *testing.T) {\n\tteardown, tmpDir := setupTeardown(t)\n\tdefer teardown(t)\n\n\tprojectConfig := projectconfig.ZeroProjectConfig{\n\t\tName: \"foo\",\n\t\tModules: projectconfig.Modules{\n\t\t\t\"mod1\": projectconfig.NewModule(map[string]string{\"test\": \"bar\"}, tmpDir, \"github.com/fake-org/repo-foo\", baseTestFixturesDir, []string{}, []projectconfig.Condition{}),\n\t\t},\n\t}\n\tgenerate.Generate(projectConfig, true)\n\n\tcontent, err := ioutil.ReadFile(filepath.Join(tmpDir, \"file_to_template.txt\"))\n\tassert.NoError(t, err)\n\n\texpectedContent := `Name is foo\nParams.test is bar\nFiles.Repository is github.com/fake-org/repo-foo\n`\n\tassert.Equal(t, string(content), expectedContent)\n}\n"
  },
  {
    "path": "internal/init/custom-prompts.go",
    "content": "package init\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\tproject \"github.com/commitdev/zero/pkg/credentials\"\n)\n\n// CustomPromptHandler handles non-input and enum options prompts\n// zero-module's parameters allow prompts to specify types of custom actions\n// this allows non-standard enum / string input to be added, such as AWS profile picker\nfunc CustomPromptHandler(promptType string, params map[string]string) error {\n\tswitch promptType {\n\n\tcase \"AWSProfilePicker\":\n\t\terr := promptAWSProfilePicker(params)\n\t\tif err != nil {\n\t\t\tparams[\"useExistingAwsProfile\"] = \"no\"\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn errors.New(fmt.Sprintf(\"Unsupported custom prompt type %s.\", promptType))\n\t}\n\treturn nil\n}\n\nfunc promptAWSProfilePicker(params map[string]string) error {\n\tprofiles, err := project.GetAWSProfiles()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tawsPrompt := PromptHandler{\n\t\tParameter: moduleconfig.Parameter{\n\t\t\tField:   \"aws_profile\",\n\t\t\tLabel:   \"Select AWS Profile\",\n\t\t\tOptions: listToPromptOptions(profiles),\n\t\t},\n\t\tCondition: NoCondition,\n\t\tValidate:  NoValidation,\n\t}\n\t_, value := promptParameter(awsPrompt)\n\tcredErr := project.FillAWSProfile(\"\", value, params)\n\tif credErr != nil {\n\t\treturn errors.New(\"Failed to retrieve profile, falling back to User input\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/init/init.go",
    "content": "package init\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/module\"\n\t\"github.com/commitdev/zero/internal/registry\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/manifoldco/promptui\"\n\t\"gopkg.in/yaml.v2\"\n)\n\n// Create cloud provider context\nfunc Init(outDir, localModulePath, registryFilePath string) *projectconfig.ZeroProjectConfig {\n\tprojectConfig := defaultProjConfig()\n\n\tprojectRootParams := map[string]string{}\n\temptyEnvVarTranslationMap := map[string]string{}\n\tpromptName := getProjectNamePrompt()\n\tpromptName.RunPrompt(projectRootParams, emptyEnvVarTranslationMap)\n\tprojectConfig.Name = projectRootParams[promptName.Field]\n\n\trootDir := path.Join(outDir, projectConfig.Name)\n\tflog.Infof(\":tada: Initializing project\")\n\n\terr := os.MkdirAll(rootDir, os.ModePerm)\n\tif os.IsExist(err) {\n\t\texit.Fatal(\"Directory %v already exists! Error: %v\", projectConfig.Name, err)\n\t} else if err != nil {\n\t\texit.Fatal(\"Error creating root: %v \", err)\n\t}\n\n\tregistry, err := registry.GetRegistry(localModulePath, registryFilePath)\n\n\tif err != nil {\n\t\texit.Fatal(\"Error getting registry: %v \", err)\n\t}\n\n\tmoduleSources := chooseStack(registry)\n\tmoduleConfigs, mappedSources := loadAllModules(moduleSources)\n\n\tprompts := getProjectPrompts(projectConfig.Name, moduleConfigs)\n\n\tinitParams := make(map[string]string)\n\tprojectConfig.ShouldPushRepositories = true\n\tprompts[\"ShouldPushRepositories\"].RunPrompt(initParams, emptyEnvVarTranslationMap)\n\tif initParams[\"ShouldPushRepositories\"] == \"n\" {\n\t\tprojectConfig.ShouldPushRepositories = false\n\t}\n\n\t// Prompting for push-up stream, then conditionally prompting for github\n\tprompts[\"GithubRootOrg\"].RunPrompt(initParams, emptyEnvVarTranslationMap)\n\n\tprojectData := promptAllModules(moduleConfigs, &projectConfig)\n\n\t// Map parameter values back to specific modules\n\tfor moduleName, module := range moduleConfigs {\n\t\tprompts[moduleName].RunPrompt(initParams, emptyEnvVarTranslationMap)\n\t\trepoName := initParams[prompts[moduleName].Field]\n\t\trepoURL := fmt.Sprintf(\"%s/%s\", initParams[\"GithubRootOrg\"], repoName)\n\t\tprojectModuleParams := moduleconfig.SummarizeParameters(module, projectData)\n\t\tprojectModuleConditions := moduleconfig.SummarizeConditions(module)\n\n\t\tprojectConfig.Modules[moduleName] = projectconfig.NewModule(\n\t\t\tprojectModuleParams,\n\t\t\trepoName,\n\t\t\trepoURL,\n\t\t\tmappedSources[moduleName],\n\t\t\tmodule.DependsOn,\n\t\t\tprojectModuleConditions,\n\t\t)\n\t}\n\n\treturn &projectConfig\n}\n\n// loadAllModules takes a list of module sources, downloads those modules, and parses their config\nfunc loadAllModules(moduleSources []string) (map[string]moduleconfig.ModuleConfig, map[string]string) {\n\tmodules := make(map[string]moduleconfig.ModuleConfig)\n\tmappedSources := make(map[string]string)\n\n\twg := sync.WaitGroup{}\n\twg.Add(len(moduleSources))\n\tfor _, moduleSource := range moduleSources {\n\t\tgo module.FetchModule(moduleSource, &wg)\n\t}\n\twg.Wait()\n\n\tfor _, moduleSource := range moduleSources {\n\t\tmod, err := module.ParseModuleConfig(moduleSource)\n\t\tif err != nil {\n\t\t\texit.Fatal(\"Unable to load module (%s):  %v\\n\", moduleSource, err)\n\t\t}\n\t\tmodules[mod.Name] = mod\n\t\tmappedSources[mod.Name] = moduleSource\n\t}\n\treturn modules, mappedSources\n}\n\n// Project name is prompt individually because the rest of the prompts\n// requires the projectName to populate defaults\nfunc getProjectNamePrompt() PromptHandler {\n\treturn PromptHandler{\n\t\tParameter: moduleconfig.Parameter{\n\t\t\tField:   \"projectName\",\n\t\t\tLabel:   \"Project Name\",\n\t\t\tInfo:    \"This name will be used in most of the resources that are created and should be unique within an AWS account.\",\n\t\t\tDefault: \"\",\n\t\t},\n\t\tCondition: NoCondition,\n\t\tValidate:  ValidateProjectName,\n\t}\n}\n\nfunc getProjectPrompts(projectName string, modules map[string]moduleconfig.ModuleConfig) map[string]PromptHandler {\n\thandlers := map[string]PromptHandler{\n\t\t\"ShouldPushRepositories\": {\n\t\t\tParameter: moduleconfig.Parameter{\n\t\t\t\tField: \"ShouldPushRepositories\",\n\t\t\t\tLabel: \"Should the created projects be checked into github automatically?\",\n\t\t\t\tInfo:  \"If yes, we will automatically create repositories for you in github and check in the generated code.\\nIf no, you will need to do these steps manually after running the zero create command.\",\n\t\t\t\tOptions: yaml.MapSlice{\n\t\t\t\t\tyaml.MapItem{Key: \"y\", Value: \"yes\"},\n\t\t\t\t\tyaml.MapItem{Key: \"n\", Value: \"no\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tCondition: NoCondition,\n\t\t\tValidate:  SpecificValueValidation(\"y\", \"n\"),\n\t\t},\n\t\t\"GithubRootOrg\": {\n\t\t\tParameter: moduleconfig.Parameter{\n\t\t\t\tField:   \"GithubRootOrg\",\n\t\t\t\tLabel:   \"What's the root of the github organization that will own these repositories?\",\n\t\t\t\tInfo:    \"This should be github.com/<your-organization-name>\",\n\t\t\t\tDefault: \"github.com/\",\n\t\t\t},\n\t\t\tCondition: NoCondition,\n\t\t\tValidate:  ValidateOrganizationName,\n\t\t},\n\t}\n\n\tfor moduleName, module := range modules {\n\t\tlabel := fmt.Sprintf(\"What do you want to call the %s project?\", moduleName)\n\n\t\thandlers[moduleName] = PromptHandler{\n\t\t\tParameter: moduleconfig.Parameter{\n\t\t\t\tField:   moduleName,\n\t\t\t\tLabel:   label,\n\t\t\t\tInfo:    \"This will be used as the name of the repository.\",\n\t\t\t\tDefault: module.OutputDir,\n\t\t\t},\n\t\t\tCondition: NoCondition,\n\t\t\tValidate:  NoValidation,\n\t\t}\n\t}\n\n\treturn handlers\n}\n\nfunc chooseCloudProvider(projectConfig *projectconfig.ZeroProjectConfig) {\n\t// @TODO move options into configs\n\tproviderPrompt := promptui.Select{\n\t\tLabel: \"Select Cloud Provider\",\n\t\tItems: []string{\"Amazon AWS\", \"Google GCP\", \"Microsoft Azure\"},\n\t}\n\n\t_, providerResult, err := providerPrompt.Run()\n\tif err != nil {\n\t\texit.Fatal(\"Prompt failed %v\\n\", err)\n\t}\n\n\tif providerResult != \"Amazon AWS\" {\n\t\texit.Fatal(\"Only the AWS provider is available at this time\")\n\t}\n}\n\nfunc chooseStack(reg registry.Registry) []string {\n\tshowInfoBox(\"A stack is a group of Zero modules. They will be pulled in from the registry and templated to create the different parts of your project such as infrastructure, backend, frontend, etc.\")\n\n\tproviderPrompt := promptui.Select{\n\t\tLabel: \"Pick the stack you'd like to use\",\n\t\tItems: registry.AvailableLabels(reg),\n\t}\n\t_, providerResult, err := providerPrompt.Run()\n\tif err != nil {\n\t\texit.Fatal(\"Prompt failed %v\\n\", err)\n\t}\n\n\treturn registry.GetModulesByName(reg, providerResult)\n}\n\nfunc defaultProjConfig() projectconfig.ZeroProjectConfig {\n\treturn projectconfig.ZeroProjectConfig{\n\t\tName:       \"\",\n\t\tParameters: map[string]string{},\n\t\tModules:    projectconfig.Modules{},\n\t}\n}\n"
  },
  {
    "path": "internal/init/prompts.go",
    "content": "package init\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"strings\"\n\n\ttm \"github.com/buger/goterm\"\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t\"github.com/commitdev/zero/internal/config/projectconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/internal/util\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/manifoldco/promptui\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nconst cyanArrow = \"\\033[36m\\U000025B6\\033[0m\"\nconst greenCheckMark = \"\\033[32m\\U00002714\\033[0m\"\n\nconst awsPickProfile = \"Existing AWS Profiles\"\nconst awsManualInputCredentials = \"Enter my own AWS credentials\"\n\n// PromptHandler defines how a user is prompted for a parameter, containing information about the parameter, conditions, and value validation\ntype PromptHandler struct {\n\tmoduleconfig.Parameter\n\tCondition CustomConditionSignature\n\tValidate  func(string) error\n}\n\n// CredentialPrompts is a list of prompts for sensitive credentials\ntype CredentialPrompts struct {\n\tVendor  string\n\tPrompts []PromptHandler\n}\n\n// CustomConditionSignature is the function signature of a custom condition. It takes a map of parameters and returns a boolean\ntype CustomConditionSignature func(map[string]string) bool\n\n// NoCondition is a no-op condition check function that always returns true\nfunc NoCondition(map[string]string) bool {\n\treturn true\n}\n\n// KeyMatchCondition is a condition that checks if the key matches the value\nfunc KeyMatchCondition(key string, value string) CustomConditionSignature {\n\treturn func(param map[string]string) bool {\n\t\treturn param[key] == value\n\t}\n}\n\n// NoValidation is a no-op validation function\nfunc NoValidation(string) error {\n\treturn nil\n}\n\n// SpecificValueValidation is a validation function that checks if the value is in the list of options\nfunc SpecificValueValidation(values ...string) func(string) error {\n\treturn func(checkValue string) error {\n\t\tfor _, allowedValue := range values {\n\t\t\tif checkValue == allowedValue {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"Please choose one of %s\", strings.Join(values, \"/\"))\n\t}\n}\n\n// ValidateAKID checks if the input is a valid AWS Access Key ID\nfunc ValidateAKID(input string) error {\n\t// 20 uppercase alphanumeric characters\n\tvar awsAccessKeyIDPat = regexp.MustCompile(`^[A-Z0-9]{20}$`)\n\tif !awsAccessKeyIDPat.MatchString(input) {\n\t\treturn errors.New(\"Invalid aws_access_key_id\")\n\t}\n\treturn nil\n}\n\n// ValidateSAK checks if the input is a valid AWS Secret Access Key\nfunc ValidateSAK(input string) error {\n\t// 40 base64 characters\n\tvar awsSecretAccessKeyPat = regexp.MustCompile(`^[A-Za-z0-9/+=]{40}$`)\n\tif !awsSecretAccessKeyPat.MatchString(input) {\n\t\treturn errors.New(\"Invalid aws_secret_access_key\")\n\t}\n\treturn nil\n}\n\n// ValidateProjectName validates Project Name field user input.\nfunc ValidateProjectName(input string) error {\n\t// the first 62 char out of base64 and -\n\tvar pName = regexp.MustCompile(`^[a-zA-Z][A-Za-z0-9-]{1,16}$`)\n\tif !pName.MatchString(input) {\n\t\t// error if char len is greater than 16\n\t\tif len(input) > constants.MaxPnameLength {\n\t\t\treturn errors.New(\"Invalid, Project Name: (cannot exceed a max length of 16)\")\n\t\t}\n\t\treturn errors.New(\"invalid, Project Name: (can only contain alphanumeric chars & '-') and must start with a letter\")\n\t}\n\treturn nil\n}\n\n// ValidateOrganizationName validates Organization Name field user input.\nfunc ValidateOrganizationName(input string) error {\n\t// the first 62 char out of base64 and -\n\tvar organizationName = strings.TrimLeft(input, \"github.com/\")\n\tvar oName = regexp.MustCompile(`^[A-Za-z0-9-]{1,39}$`)\n\t// error if char len is greater than 39\n\tif len(organizationName) > constants.MaxOnameLength {\n\t\treturn errors.New(\"Invalid, Organization Name: (cannot exceed a max length of 39)\")\n\t}\n\tif !oName.MatchString(organizationName) {\n\t\treturn errors.New(\"Invalid, Organization Name: (can only contain alphanumeric chars & '-')\")\n\t}\n\treturn nil\n}\n\nconst infoBoxHeight = 4\n\nvar currentLine int = infoBoxHeight\n\n// showInfoBox prints a box with some text in it, and the title \"Info\"\nfunc showInfoBox(infoText string) {\n\tbox := tm.NewBox(100|tm.PCT, 4, 0)\n\tfmt.Fprint(box, infoText)\n\ttm.Print(tm.MoveTo(box.String(), 1, 1))\n\ttm.MoveCursor(4, 1)\n\ttm.Printf(\"Info\")\n}\n\n// RunPrompt obtains the value of PromptHandler depending on the parameter's definition\n// for the project config,  there are multiple ways of obtaining the value\n// values go into params depending on `Condition` as the highest precedence (Whether it gets this value)\n// then follows this order to determine HOW it obtains that value\n// 1. Execute (this could potentially be refactored into type + data)\n// 2. type: specific ways of obtaining values (in AWS credential case it will set 2 values to the map)\n// 3. value: directly assigns a value to a parameter\n// 4. prompt: requires users to select an option OR input a string\nfunc (p PromptHandler) RunPrompt(projectParams map[string]string, envVarTranslationMap map[string]string) error {\n\tvar err error\n\tvar result string\n\n\tif p.Condition(projectParams) {\n\n\t\t// If we start printing below the bottom of the terminal screen, go back to the top\n\t\tif currentLine+infoBoxHeight+1 > tm.Height() {\n\t\t\ttm.Clear()\n\t\t\tcurrentLine = infoBoxHeight\n\t\t}\n\n\t\t// TODO: figure out scope of projectParams per project\n\t\t// potentially dangerous to have cross module env leaking\n\t\t// so if community module has an `execute: twitter tweet $ENV`\n\t\t// it wouldnt leak things the module shouldnt have access to\n\t\tif p.Parameter.Execute != \"\" {\n\t\t\tresult = executeCmd(p.Parameter.Execute, projectParams, envVarTranslationMap)\n\t\t} else if p.Parameter.Type != \"\" {\n\t\t\terr = CustomPromptHandler(p.Parameter.Type, projectParams)\n\t\t} else if p.Parameter.Value != \"\" {\n\t\t\tresult = p.Parameter.Value\n\t\t} else {\n\t\t\tshowInfoBox(p.Parameter.Info)\n\t\t\t// Move down to the next line to show the prompt\n\t\t\tcurrentLine++\n\t\t\ttm.MoveCursor(1, currentLine)\n\t\t\ttm.Flush() // Call it every time at the end of rendering\n\n\t\t\terr, result = promptParameter(p)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Append the result to parameter map\n\t\tprojectParams[p.Field] = sanitizeParameterValue(result)\n\t} else {\n\t\telseValue := moduleconfig.GetFirstConditionElseValue(p.Parameter)\n\t\tif elseValue != \"\" {\n\t\t\tflog.Debugf(\"Skipping prompt(%s) due to condition failed but assigning default value \\\"%s\\\"\", p.Field, elseValue)\n\t\t\tprojectParams[p.Field] = elseValue\n\t\t} else {\n\t\t\tflog.Debugf(\"Skipping prompt(%s) due to condition failed\", p.Field)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc promptParameter(prompt PromptHandler) (error, string) {\n\tparam := prompt.Parameter\n\tlabel := param.Label\n\tif param.Label == \"\" {\n\t\tlabel = param.Field\n\t}\n\tdefaultValue := param.Default\n\n\tvar err error\n\tvar result string\n\tif len(param.Options) > 0 {\n\t\tvar selectedIndex int\n\t\t// Scope of selected does not have the label data, so we need a dynamic\n\t\t// template with string format to put in the label in `selected`\n\t\toptionTemplate := &promptui.SelectTemplates{\n\t\t\tLabel:    `{{ . }}`,\n\t\t\tActive:   fmt.Sprintf(\"%s {{ .Value | cyan }}\", cyanArrow),\n\t\t\tInactive: \"  {{ .Value }}\",\n\t\t\tSelected: fmt.Sprintf(\"%s %s: {{ .Value }}\", greenCheckMark, label),\n\t\t}\n\n\t\tprompt := promptui.Select{\n\t\t\tLabel:     label,\n\t\t\tItems:     param.Options,\n\t\t\tTemplates: optionTemplate,\n\t\t}\n\n\t\tselectedIndex, _, err = prompt.Run()\n\t\tresult = param.Options[selectedIndex].Key.(string)\n\t} else {\n\t\tprompt := promptui.Prompt{\n\t\t\tLabel:     label,\n\t\t\tDefault:   defaultValue,\n\t\t\tAllowEdit: true,\n\t\t\tValidate:  prompt.Validate,\n\t\t}\n\t\tresult, err = prompt.Run()\n\t}\n\tif err != nil {\n\t\treturn err, \"\"\n\t}\n\n\treturn nil, result\n}\n\nfunc executeCmd(command string, envVars map[string]string, envVarTranslationMap map[string]string) string {\n\tcmd := exec.Command(\"bash\", \"-c\", command)\n\t// Might need to pass down module's translation map as well,\n\t// currently only works in `zero apply`\n\tcmd.Env = util.AppendProjectEnvToCmdEnv(envVars, os.Environ(), envVarTranslationMap)\n\tout, err := cmd.Output()\n\tflog.Debugf(\"Running command: %s\", command)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to execute  %v\\n\", err)\n\t}\n\tflog.Debugf(\"Command result: %s\", string(out))\n\treturn string(out)\n}\n\n// aws cli prints output with linebreak in them\nfunc sanitizeParameterValue(str string) string {\n\tre := regexp.MustCompile(\"\\\\n\")\n\treturn re.ReplaceAllString(str, \"\")\n}\n\n// PromptModuleParams renders series of prompt UI based on the config\nfunc PromptModuleParams(moduleConfig moduleconfig.ModuleConfig, parameters map[string]string) (map[string]string, error) {\n\tenvVarTranslationMap := moduleConfig.GetParamEnvVarTranslationMap()\n\tfor _, parameter := range moduleConfig.Parameters {\n\t\t// deduplicate fields already prompted and received\n\t\tif _, isAlreadySet := parameters[parameter.Field]; isAlreadySet {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar validateFunc func(input string) error = nil\n\n\t\t// type:regex field validation for zero-module.yaml\n\t\tif parameter.FieldValidation.Type == constants.RegexValidation {\n\t\t\tvalidateFunc = func(input string) error {\n\t\t\t\tvar regexRule = regexp.MustCompile(parameter.FieldValidation.Value)\n\t\t\t\tif !regexRule.MatchString(input) {\n\t\t\t\t\treturn errors.New(parameter.FieldValidation.ErrorMessage)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\t// TODO: type:fuction field validation for zero-module.yaml\n\n\t\tpromptHandler := PromptHandler{\n\t\t\tParameter: parameter,\n\t\t\tCondition: paramConditionsMapper(parameter.Conditions),\n\t\t\tValidate:  validateFunc,\n\t\t}\n\t\t// merging the context of param and credentals\n\t\t// this treats credentialEnvs as throwaway, parameters is shared between modules\n\t\t// so credentials should not be in parameters as it gets returned to parent\n\t\t// for k, v := range parameters {\n\t\t// \tcredentialEnvs[k] = v\n\t\t// }\n\t\terr := promptHandler.RunPrompt(parameters, envVarTranslationMap)\n\t\tif err != nil {\n\t\t\treturn parameters, err\n\t\t}\n\t}\n\tflog.Debugf(\"Module %s prompt: \\n %#v\", moduleConfig.Name, parameters)\n\treturn parameters, nil\n}\n\n// promptAllModules takes a map of all the modules and prompts the user for values for all the parameters\n// Important: This is done here because in this step we share the parameter across modules,\n// meaning if module A and B both asks for region, it will reuse the response for both (and is deduped during runtime)\nfunc promptAllModules(modules map[string]moduleconfig.ModuleConfig, projectConfig *projectconfig.ZeroProjectConfig) map[string]string {\n\tparameterValues := availableProjectContext(projectConfig)\n\tfor _, config := range modules {\n\t\tvar err error\n\n\t\tparameterValues, err = PromptModuleParams(config, parameterValues)\n\t\tif err != nil {\n\t\t\texit.Fatal(\"Exiting prompt(%s):  %v\\n\", config.Name, err)\n\t\t}\n\t}\n\treturn parameterValues\n}\n\n// availableProjectContext declares a list of variables usable in modules parameter prompt's execute step\nfunc availableProjectContext(projectConfig *projectconfig.ZeroProjectConfig) map[string]string {\n\treturn map[string]string{\n\t\t\"projectName\": projectConfig.Name,\n\t}\n}\n\n// paramConditionsMapper returns a condition checking function that checks if all the conditions are met\nfunc paramConditionsMapper(conditions []moduleconfig.Condition) CustomConditionSignature {\n\tif len(conditions) == 0 {\n\t\treturn NoCondition\n\t} else {\n\t\treturn func(params map[string]string) bool {\n\t\t\t// Prompts must pass every condition to proceed\n\t\t\tfor i := 0; i < len(conditions); i++ {\n\t\t\t\tcond := conditions[i]\n\t\t\t\tif !conditionHandler(cond)(params) {\n\t\t\t\t\tflog.Debugf(\"Did not meet condition %v, expected %v to be %v\", cond.Action, cond.MatchField, cond.WhenValue)\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t}\n}\n\n// conditionHandler is a helper that accepts condition config and returns a function that checks if the condition is met\nfunc conditionHandler(cond moduleconfig.Condition) CustomConditionSignature {\n\tif cond.Action == \"KeyMatchCondition\" {\n\t\treturn KeyMatchCondition(cond.MatchField, cond.WhenValue)\n\t} else {\n\t\tflog.Errorf(\"Unsupported condition\")\n\t\treturn nil\n\t}\n}\n\nfunc appendToSet(set []string, toAppend []string) []string {\n\tfor _, appendee := range toAppend {\n\t\tif !util.ItemInSlice(set, appendee) {\n\t\t\tset = append(set, appendee)\n\t\t}\n\t}\n\treturn set\n}\n\nfunc listToPromptOptions(list []string) yaml.MapSlice {\n\tmapSlice := make(yaml.MapSlice, len(list))\n\tfor i := 0; i < len(list); i++ {\n\t\tmapSlice[i] = yaml.MapItem{\n\t\t\tKey:   list[i],\n\t\t\tValue: list[i],\n\t\t}\n\t}\n\treturn mapSlice\n}\n"
  },
  {
    "path": "internal/init/prompts_test.go",
    "content": "package init_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t// init is a reserved word\n\tinitPrompts \"github.com/commitdev/zero/internal/init\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGetParam(t *testing.T) {\n\n\tenvVarTranslationMap := map[string]string{}\n\tprojectParams := map[string]string{}\n\tt.Run(\"Should execute params without prompt\", func(t *testing.T) {\n\t\tparam := moduleconfig.Parameter{\n\t\t\tField:   \"account-id\",\n\t\t\tExecute: \"echo \\\"my-account-id\\\"\",\n\t\t}\n\n\t\tprompt := initPrompts.PromptHandler{\n\t\t\tparam,\n\t\t\tinitPrompts.NoCondition,\n\t\t\tinitPrompts.NoValidation,\n\t\t}\n\n\t\tprompt.RunPrompt(projectParams, envVarTranslationMap)\n\t\tassert.Equal(t, \"my-account-id\", projectParams[param.Field])\n\t})\n\n\tt.Run(\"executes with project context\", func(t *testing.T) {\n\t\tparam := moduleconfig.Parameter{\n\t\t\tField:   \"myEnv\",\n\t\t\tExecute: \"echo $INJECTEDENV\",\n\t\t}\n\n\t\tprompt := initPrompts.PromptHandler{\n\t\t\tparam,\n\t\t\tinitPrompts.NoCondition,\n\t\t\tinitPrompts.NoValidation,\n\t\t}\n\n\t\tprojectParams := map[string]string{\"INJECTEDENV\": \"SOME_ENV_VAR_VALUE\"}\n\t\tprompt.RunPrompt(projectParams, envVarTranslationMap)\n\t\tassert.Equal(t, \"SOME_ENV_VAR_VALUE\", projectParams[param.Field])\n\t})\n\n\tt.Run(\"Should return static value\", func(t *testing.T) {\n\t\tparam := moduleconfig.Parameter{\n\t\t\tField: \"placeholder\",\n\t\t\tValue: \"lorem-ipsum\",\n\t\t}\n\n\t\tprompt := initPrompts.PromptHandler{\n\t\t\tparam,\n\t\t\tinitPrompts.NoCondition,\n\t\t\tinitPrompts.NoValidation,\n\t\t}\n\n\t\tprompt.RunPrompt(projectParams, envVarTranslationMap)\n\t\tassert.Equal(t, \"lorem-ipsum\", projectParams[param.Field])\n\t})\n\n\tt.Run(\"Prompt value to retain existing params\", func(t *testing.T) {\n\t\tprojectParams = map[string]string{\n\t\t\t\"existing_value\": \"foo\",\n\t\t}\n\t\tparam := moduleconfig.Parameter{\n\t\t\tField: \"new_value\",\n\t\t\tValue: \"bar\",\n\t\t}\n\n\t\tprompt := initPrompts.PromptHandler{\n\t\t\tparam,\n\t\t\tinitPrompts.NoCondition,\n\t\t\tinitPrompts.NoValidation,\n\t\t}\n\n\t\tprompt.RunPrompt(projectParams, envVarTranslationMap)\n\t\tassert.Equal(t, \"foo\", projectParams[\"existing_value\"])\n\t\tassert.Equal(t, \"bar\", projectParams[param.Field])\n\t})\n\n\tt.Run(\"Prompt to apply in order and allow EnvVarMapping\", func(t *testing.T) {\n\n\t\tprojectParams = map[string]string{}\n\t\tparams := []moduleconfig.Parameter{\n\t\t\t{\n\t\t\t\tField:      \"param1\",\n\t\t\t\tValue:      \"foo\",\n\t\t\t\tEnvVarName: \"envvar1\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tField:      \"param2\",\n\t\t\t\tExecute:    \"echo $envvar1 bar\",\n\t\t\t\tEnvVarName: \"envvar2\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tField:   \"param3\",\n\t\t\t\tExecute: \"echo $envvar2 baz\",\n\t\t\t},\n\t\t}\n\t\tmodule := moduleconfig.ModuleConfig{Parameters: params}\n\n\t\tprojectParams, _ = initPrompts.PromptModuleParams(module, projectParams)\n\n\t\tassert.Equal(t, \"foo\", projectParams[\"param1\"])\n\t\tassert.Equal(t, \"foo bar\", projectParams[\"param2\"], \"should reference param1 via env-var\")\n\t\tassert.Equal(t, \"foo bar baz\", projectParams[\"param3\"], \"should reference param2 via env-var\")\n\t})\n\n\tt.Run(\"Prompt conditions\", func(t *testing.T) {\n\n\t\tprojectParams = map[string]string{}\n\t\tparams := []moduleconfig.Parameter{\n\t\t\t{\n\t\t\t\tField: \"param1\",\n\t\t\t\tValue: \"pass\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tField: \"passing_condition\",\n\t\t\t\tValue: \"pass\",\n\t\t\t\tConditions: []moduleconfig.Condition{\n\t\t\t\t\t{\n\t\t\t\t\t\tAction:     \"KeyMatchCondition\",\n\t\t\t\t\t\tMatchField: \"param1\",\n\t\t\t\t\t\tWhenValue:  \"pass\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tField: \"failing_condition\",\n\t\t\t\tValue: \"pass\",\n\t\t\t\tConditions: []moduleconfig.Condition{\n\t\t\t\t\t{\n\t\t\t\t\t\tAction:     \"KeyMatchCondition\",\n\t\t\t\t\t\tMatchField: \"param1\",\n\t\t\t\t\t\tWhenValue:  \"not pass\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tField: \"multiple_condition\",\n\t\t\t\tValue: \"pass\",\n\t\t\t\tConditions: []moduleconfig.Condition{\n\t\t\t\t\t{\n\t\t\t\t\t\tAction:     \"KeyMatchCondition\",\n\t\t\t\t\t\tMatchField: \"param1\",\n\t\t\t\t\t\tWhenValue:  \"pass\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tAction:     \"KeyMatchCondition\",\n\t\t\t\t\t\tMatchField: \"passing_condition\",\n\t\t\t\t\t\tWhenValue:  \"pass\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tField: \"condition_with_default\",\n\t\t\t\tValue: \"pass\",\n\t\t\t\tConditions: []moduleconfig.Condition{\n\t\t\t\t\t{\n\t\t\t\t\t\tAction:     \"KeyMatchCondition\",\n\t\t\t\t\t\tMatchField: \"param1\",\n\t\t\t\t\t\tWhenValue:  \"not pass\",\n\t\t\t\t\t\tElseValue:  \"itsadefault\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tmodule := moduleconfig.ModuleConfig{Parameters: params}\n\t\tprojectParams, _ = initPrompts.PromptModuleParams(module, projectParams)\n\n\t\tassert.Equal(t, \"pass\", projectParams[\"param1\"], \"Value just hardcoded\")\n\t\tassert.Equal(t, \"pass\", projectParams[\"passing_condition\"], \"Expected to pass condition and set value\")\n\t\tassert.NotContains(t, projectParams, \"failing_condition\", \"Expected to fail condition and not set value\")\n\t\tassert.Equal(t, \"pass\", projectParams[\"multiple_condition\"], \"Expected to pass multiple condition and set value\")\n\t\tassert.Equal(t, \"itsadefault\", projectParams[\"condition_with_default\"], \"Expected to set a default value for a condition that failed\")\n\t})\n\n\tt.Run(\"Should return error upon unsupported custom prompt type\", func(t *testing.T) {\n\n\t\tprojectParams = map[string]string{}\n\t\tparams := []moduleconfig.Parameter{\n\t\t\t{\n\t\t\t\tField: \"param1\",\n\t\t\t\tType:  \"random-type\",\n\t\t\t},\n\t\t}\n\t\tmodule := moduleconfig.ModuleConfig{Parameters: params}\n\t\t_, err := initPrompts.PromptModuleParams(module, projectParams)\n\t\tassert.Equal(t, \"Unsupported custom prompt type random-type.\", err.Error())\n\t})\n}\n\nfunc TestValidateProjectNam(t *testing.T) {\n\tt.Run(\"Should return error upon invalid project name\", func(t *testing.T) {\n\t\terr := initPrompts.ValidateProjectName(\"0invalid\")\n\t\tassert.Error(t, err, \"Project name should not start with a number\")\n\t})\n\n\tt.Run(\"Should return error upon invalid project name length\", func(t *testing.T) {\n\t\terr := initPrompts.ValidateProjectName(\"invalid name with more than 30 characters\")\n\t\tassert.Error(t, err, \"Project name should not be longer than 30 characters\")\n\t})\n\n\tt.Run(\"Should return nil upon valid project name\", func(t *testing.T) {\n\t\terr := initPrompts.ValidateProjectName(\"valid-name\")\n\t\tassert.Nil(t, err)\n\t})\n}\n"
  },
  {
    "path": "internal/module/module.go",
    "content": "package module\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"log\"\n\t\"path\"\n\t\"regexp\"\n\t\"sync\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/commitdev/zero/internal/util\"\n\t\"github.com/commitdev/zero/pkg/util/exit\"\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/hashicorp/go-getter\"\n)\n\n// FetchModule downloads the remote module source if necessary. Meant to be run in a goroutine.\nfunc FetchModule(source string, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tlocalPath := GetSourceDir(source)\n\tif !IsLocal(source) {\n\t\tflog.Debugf(\"Downloading module: %s to %s\", source, localPath)\n\t\terr := getter.Get(localPath, source)\n\t\tif err != nil {\n\t\t\texit.Fatal(\"Failed to fetch remote module from %s: %v\\n\", source, err)\n\t\t}\n\t}\n\treturn\n}\n\n// ParseModuleConfig loads the local config file for a module and parses the yaml\nfunc ParseModuleConfig(source string) (moduleconfig.ModuleConfig, error) {\n\tlocalPath := GetSourceDir(source)\n\tconfig := moduleconfig.ModuleConfig{}\n\tconfigPath := path.Join(localPath, constants.ZeroModuleYml)\n\tconfig, err := moduleconfig.LoadModuleConfig(configPath)\n\treturn config, err\n}\n\n// GetSourcePath gets a unique local source directory name. For local modules, it use the local directory\nfunc GetSourceDir(source string) string {\n\tif !IsLocal(source) {\n\t\th := md5.New()\n\t\tio.WriteString(h, source)\n\t\tsource = base64.StdEncoding.EncodeToString(h.Sum(nil))\n\t\treturn path.Join(constants.TemplatesDir, source)\n\t} else {\n\t\treturn source\n\t}\n}\n\n// IsLocal uses the go-getter FileDetector to check if source is a file\nfunc IsLocal(source string) bool {\n\tpwd := util.GetCwd()\n\n\t// ref: https://github.com/hashicorp/go-getter/blob/master/detect_test.go\n\tout, err := getter.Detect(source, pwd, getter.Detectors)\n\n\tmatch, err := regexp.MatchString(\"^file://.*\", out)\n\tif err != nil {\n\t\tlog.Panicf(\"invalid source format %s\", err)\n\t}\n\n\treturn match\n}\n\nfunc withPWD(pwd string) func(*getter.Client) error {\n\treturn func(c *getter.Client) error {\n\t\tc.Pwd = pwd\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "internal/module/module_internal_test.go",
    "content": "package module\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIsLocal(t *testing.T) {\n\tsource := \"./tests/test_data/modules\"\n\tres := IsLocal(source)\n\tif !res {\n\t\tt.Errorf(\"Error, source %s SHOULD BE determined as local\", source)\n\t}\n\n\tsource = \"https://github.com/commitdev/my-repo\"\n\tres = IsLocal(source)\n\tif res {\n\t\tt.Errorf(\"Error, source %s SHOULD NOT BE determined as local\", source)\n\t}\n}\n"
  },
  {
    "path": "internal/module/module_test.go",
    "content": "package module_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/config/moduleconfig\"\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/commitdev/zero/internal/module\"\n\t\"github.com/commitdev/zero/version\"\n)\n\nfunc TestGetSourceDir(t *testing.T) {\n\tsource := \"tests/test_data/modules\"\n\trelativeSource := source\n\tdir := module.GetSourceDir(source)\n\n\tt.Log(\"dir\", dir)\n\tif dir != relativeSource {\n\t\tt.Errorf(\"Error, local sources should not be changed: %s\", source)\n\t}\n\n\tsource = \"github.com/commitdev/my-repo\"\n\tdir = module.GetSourceDir(source)\n\tif dir == relativeSource {\n\t\tt.Errorf(\"Error, remote sources should be converted to a local dir: %s\", source)\n\t}\n}\n\nfunc TestParseModuleConfig(t *testing.T) {\n\ttestModuleSource := \"../../tests/test_data/modules/ci\"\n\tvar mod moduleconfig.ModuleConfig\n\n\tt.Run(\"Loading module from source\", func(t *testing.T) {\n\t\tmod, _ = module.ParseModuleConfig(testModuleSource)\n\t\tmoduleconfig.ValidateZeroVersion(mod)\n\n\t\tassert.Equal(t, \"CI templates\", mod.Name)\n\t})\n\n\tt.Run(\"Parameters are loaded\", func(t *testing.T) {\n\t\tparam, err := findParameter(mod.Parameters, \"platform\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tassert.Equal(t, \"platform\", param.Field)\n\t\tassert.Equal(t, \"CI Platform\", param.Label)\n\n\t})\n\n\tt.Run(\"OmitFromProjectFile default\", func(t *testing.T) {\n\t\tparam, err := findParameter(mod.Parameters, \"platform\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tassert.Equal(t, false, param.OmitFromProjectFile, \"OmitFromProjectFile should default to false\")\n\t\tuseCredsParam, useCredsErr := findParameter(mod.Parameters, \"useExistingAwsProfile\")\n\t\tif useCredsErr != nil {\n\t\t\tpanic(useCredsErr)\n\t\t}\n\t\tassert.Equal(t, true, useCredsParam.OmitFromProjectFile, \"OmitFromProjectFile should be read from file\")\n\t})\n\n\tt.Run(\"Parsing Conditions and Typed prompts from config\", func(t *testing.T) {\n\t\tparam, err := findParameter(mod.Parameters, \"profilePicker\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tassert.Equal(t, \"AWSProfilePicker\", param.Type)\n\t\tassert.Equal(t, \"KeyMatchCondition\", param.Conditions[0].Action)\n\t\tassert.Equal(t, \"useExistingAwsProfile\", param.Conditions[0].MatchField)\n\t\tassert.Equal(t, \"yes\", param.Conditions[0].WhenValue)\n\t})\n\n\tt.Run(\"parsing envVarName from module config\", func(t *testing.T) {\n\t\tparam, err := findParameter(mod.Parameters, \"accessKeyId\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tassert.Equal(t, \"AWS_ACCESS_KEY_ID\", param.EnvVarName)\n\t})\n\n\tt.Run(\"TemplateConfig is unmarshaled\", func(t *testing.T) {\n\t\tmod, _ = module.ParseModuleConfig(testModuleSource)\n\t\tassert.Equal(t, \".circleci\", mod.TemplateConfig.OutputDir)\n\t\tassert.Equal(t, \"templates\", mod.TemplateConfig.InputDir)\n\t\tassert.Equal(t, []string{\"<%\", \"%>\"}, mod.TemplateConfig.Delimiters)\n\t})\n\n\tt.Run(\"Parsing commands\", func(t *testing.T) {\n\t\tcheckCommand := mod.Commands.Check\n\t\tassert.Equal(t, \"ls\", checkCommand)\n\t})\n\n\tt.Run(\"Parsing zero version constraints\", func(t *testing.T) {\n\t\tmoduleConstraints := mod.ZeroVersion.Constraints.String()\n\t\tassert.Equal(t, \">= 3.0.0, < 4.0.0\", moduleConstraints)\n\t})\n\n\tt.Run(\"Should Fail against old zero version\", func(t *testing.T) {\n\t\tmoduleConstraints := mod.ZeroVersion.Constraints.String()\n\n\t\t// Mocking zero's version, testing against \">= 3.0.0, <= 4.0.0\"\n\t\toriginalVersion := version.AppVersion\n\t\tversion.AppVersion = \"2.0.0\"\n\t\tdefer func() { version.AppVersion = originalVersion }()\n\t\t// end of mock\n\n\t\tisValid := moduleconfig.ValidateZeroVersion(mod)\n\t\tassert.Equal(t, false, isValid, fmt.Sprintf(\"Version should satisfy %s\", moduleConstraints))\n\t})\n\n\tt.Run(\"Should Fail against too new zero version\", func(t *testing.T) {\n\t\tmoduleConstraints := mod.ZeroVersion.Constraints.String()\n\n\t\t// Mocking zero's version, testing against \">= 3.0.0, <= 4.0.0\"\n\t\toriginalVersion := version.AppVersion\n\t\tversion.AppVersion = \"4.0.0\"\n\t\tdefer func() { version.AppVersion = originalVersion }()\n\t\t// end of mock\n\n\t\tisValid := moduleconfig.ValidateZeroVersion(mod)\n\t\tassert.Equal(t, false, isValid, fmt.Sprintf(\"Version should satisfy %s\", moduleConstraints))\n\t})\n\n\tt.Run(\"Should validate against valid versions\", func(t *testing.T) {\n\t\tmoduleConstraints := mod.ZeroVersion.Constraints.String()\n\n\t\t// Mocking zero's version, testing against \">= 3.0.0, <= 4.0.0\"\n\t\tconst newZeroVersion = \"3.0.5\"\n\t\toriginalVersion := version.AppVersion\n\t\tversion.AppVersion = newZeroVersion\n\t\tdefer func() { version.AppVersion = originalVersion }()\n\t\t// end of mock\n\n\t\tisValid := moduleconfig.ValidateZeroVersion(mod)\n\t\tassert.Equal(t, true, isValid, fmt.Sprintf(\"Version should satisfy %s\", moduleConstraints))\n\t})\n\n\tt.Run(\"default to SNAPSHOT version passes tests\", func(t *testing.T) {\n\t\tassert.Equal(t, \"SNAPSHOT\", version.AppVersion)\n\t\tisValid := moduleconfig.ValidateZeroVersion(mod)\n\t\tassert.Equal(t, true, isValid, \"default test run should pass version constraint\")\n\t})\n\n}\n\nfunc TestModuleWithNoVersionConstraint(t *testing.T) {\n\ttestModuleSource := \"../../tests/test_data/modules/no-version-constraint\"\n\tvar mod moduleconfig.ModuleConfig\n\tvar err error\n\n\tt.Run(\"Parsing Module with no version constraint\", func(t *testing.T) {\n\t\tmod, err = module.ParseModuleConfig(testModuleSource)\n\t\tassert.Equal(t, \"\", mod.ZeroVersion.String())\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"Should pass Validation if constraint not specified\", func(t *testing.T) {\n\t\tisValid := moduleconfig.ValidateZeroVersion(mod)\n\t\tassert.Equal(t, true, isValid, \"Module with no constraint should pass version validation\")\n\t})\n}\n\nfunc findParameter(params []moduleconfig.Parameter, field string) (moduleconfig.Parameter, error) {\n\tfor _, v := range params {\n\t\tif v.Field == field {\n\t\t\treturn v, nil\n\t\t}\n\t}\n\treturn moduleconfig.Parameter{}, errors.New(\"parameter not found\")\n}\n"
  },
  {
    "path": "internal/registry/registry.go",
    "content": "package registry\n\nimport (\n\t\"io/ioutil\"\n\n\t\"github.com/commitdev/zero/internal/constants\"\n\t\"github.com/hashicorp/go-getter\"\n\n\tyaml \"gopkg.in/yaml.v2\"\n)\n\ntype Registry []Stack\n\ntype Stack struct {\n\tName          string   `yaml:\"name\"`\n\tModuleSources []string `yaml:\"moduleSources\"`\n}\n\nfunc GetRegistry(localModulePath, registryFilePath string) (Registry, error) {\n\tregistry := Registry{}\n\n\terr := getter.GetFile(constants.TmpRegistryYml, registryFilePath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata, err := ioutil.ReadFile(constants.TmpRegistryYml)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = yaml.Unmarshal(data, &registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor i := 0; i < len(registry); i++ {\n\t\tfor j := 0; j < len(registry[i].ModuleSources); j++ {\n\t\t\tregistry[i].ModuleSources[j] = localModulePath + registry[i].ModuleSources[j]\n\t\t}\n\t}\n\n\treturn registry, nil\n}\n\nfunc GetModulesByName(registry Registry, name string) []string {\n\tfor _, v := range registry {\n\t\tif v.Name == name {\n\t\t\treturn v.ModuleSources\n\t\t}\n\t}\n\treturn []string{}\n}\n\nfunc AvailableLabels(registry Registry) []string {\n\tlabels := make([]string, len(registry))\n\ti := 0\n\tfor _, stack := range registry {\n\t\tlabels[i] = stack.Name\n\t\ti++\n\t}\n\treturn labels\n}\n"
  },
  {
    "path": "internal/registry/registry_test.go",
    "content": "package registry_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/internal/registry\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAvailableLabels(t *testing.T) {\n\treg := testRegistry()\n\n\tt.Run(\"should be same order as declared\", func(t *testing.T) {\n\t\tlabels := registry.AvailableLabels(reg)\n\t\tassert.Equal(t, labels, []string{\n\t\t\t\"EKS + Go + React + Gatsby\",\n\t\t\t\"foo\",\n\t\t\t\"bar\",\n\t\t\t\"lorem\",\n\t\t\t\"ipsum\",\n\t\t\t\"Custom\",\n\t\t})\n\t})\n}\n\nfunc TestGetModulesByName(t *testing.T) {\n\treg := testRegistry()\n\tt.Run(\"should return modules of specified stack\", func(t *testing.T) {\n\n\t\tassert.Equal(t, registry.GetModulesByName(reg, \"EKS + Go + React + Gatsby\"),\n\t\t\t[]string{\"module-source 1\", \"module-source 2\"})\n\t\tassert.Equal(t, registry.GetModulesByName(reg, \"lorem\"), []string{\"module-source 5\"})\n\t\tassert.Equal(t, registry.GetModulesByName(reg, \"ipsum\"), []string{\"module-source 6\"})\n\t\tassert.Equal(t, registry.GetModulesByName(reg, \"Custom\"), []string{\"module-source 7\"})\n\t})\n}\n\nfunc testRegistry() registry.Registry {\n\treturn registry.Registry{\n\t\t{\"EKS + Go + React + Gatsby\", []string{\"module-source 1\", \"module-source 2\"}},\n\t\t{\"foo\", []string{\"module-source 3\"}},\n\t\t{\"bar\", []string{\"module-source 4\"}},\n\t\t{\"lorem\", []string{\"module-source 5\"}},\n\t\t{\"ipsum\", []string{\"module-source 6\"}},\n\t\t{\"Custom\", []string{\"module-source 7\"}},\n\t}\n}\n"
  },
  {
    "path": "internal/util/util.go",
    "content": "package util\n\n// @TODO split up and move into /pkg directory\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"text/template\"\n\n\t\"github.com/google/uuid\"\n)\n\nfunc CreateDirIfDoesNotExist(path string) error {\n\tif _, err := os.Stat(path); os.IsNotExist(err) {\n\t\terr = os.MkdirAll(path, os.ModePerm)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc CleanGoIdentifier(identifier string) string {\n\treturn strings.ReplaceAll(identifier, \"-\", \"\")\n}\n\n// @TODO how can we make these type of helpers extensible?\nvar FuncMap = template.FuncMap{\n\t\"Title\":             strings.Title,\n\t\"ToLower\":           strings.ToLower,\n\t\"CleanGoIdentifier\": CleanGoIdentifier,\n\t\"GenerateUUID\":      uuid.New,\n}\n\nfunc GetCwd() string {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tlog.Fatalf(\"Getting working directory failed: %v\\n\", err)\n\t\tpanic(err)\n\t}\n\n\treturn dir\n}\n\nfunc ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string, shouldPipeStdErr bool) error {\n\n\tcmd.Dir = pathPrefix\n\tif !filepath.IsAbs(pathPrefix) {\n\t\tdir := GetCwd()\n\t\tcmd.Dir = path.Join(dir, pathPrefix)\n\t}\n\n\tstdoutPipe, _ := cmd.StdoutPipe()\n\tstderrPipe, _ := cmd.StderrPipe()\n\n\tvar errStdout, errStderr error\n\terrContent := new(bytes.Buffer)\n\n\tcmd.Env = os.Environ()\n\tif envars != nil {\n\t\tcmd.Env = append(os.Environ(), envars...)\n\t}\n\n\terr := cmd.Start()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgo func() {\n\t\t_, errStdout = io.Copy(os.Stdout, stdoutPipe)\n\t}()\n\tgo func() {\n\t\tstderrStreams := []io.Writer{errContent}\n\t\tif shouldPipeStdErr {\n\t\t\tstderrStreams = append(stderrStreams, os.Stderr)\n\t\t}\n\t\tstdErr := io.MultiWriter(stderrStreams...)\n\t\t_, errStderr = io.Copy(stdErr, stderrPipe)\n\t}()\n\n\terr = cmd.Wait()\n\tif err != nil {\n\t\t// Detecting and returning the makefile error to cmd\n\t\t// Passing alone makefile stderr as error message, otherwise it just says \"exit status 2\"\n\t\tif exitError, ok := err.(*exec.ExitError); ok {\n\t\t\tws := exitError.Sys().(syscall.WaitStatus)\n\t\t\texitCode := ws.ExitStatus()\n\t\t\tif exitCode == 2 {\n\t\t\t\tstderrOut := errContent.String()\n\t\t\t\tisMissingTarget, _ := regexp.MatchString(\"No rule to make target\", stderrOut)\n\t\t\t\tif isMissingTarget {\n\t\t\t\t\treturn errors.New(\"Module missing mandatory targets, this is likely an issue with the module itself.\")\n\t\t\t\t}\n\t\t\t\treturn errors.New(stderrOut)\n\t\t\t}\n\t\t}\n\n\t\treturn errors.New(errContent.String())\n\t}\n\n\tif errStdout != nil {\n\t\tlog.Printf(\"Failed to capture stdout: %v\\n\", errStdout)\n\t}\n\n\tif errStderr != nil {\n\t\tlog.Printf(\"Failed to capture stderr: %v\\n\", errStderr)\n\t}\n\treturn nil\n}\n\n// ExecuteCommandOutput runs the command and returns its\n// combined standard output and standard error.\nfunc ExecuteCommandOutput(cmd *exec.Cmd, pathPrefix string, envars []string) string {\n\n\tcmd.Dir = pathPrefix\n\tif !filepath.IsAbs(pathPrefix) {\n\t\tdir := GetCwd()\n\t\tcmd.Dir = path.Join(dir, pathPrefix)\n\t}\n\n\tcmd.Env = os.Environ()\n\tif envars != nil {\n\t\tcmd.Env = append(os.Environ(), envars...)\n\t}\n\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\tlog.Fatalf(\"Executing command with output failed: (%v) %s\\n\", err, out)\n\t}\n\treturn string(out)\n}\n\n// AppendProjectEnvToCmdEnv converts a key-value pair map into a slice of `key=value`s\n// allow module definition to use an alternative env-var-name than field while apply\nfunc AppendProjectEnvToCmdEnv(envMap map[string]string, envList []string, translationMap map[string]string) []string {\n\n\tfor key, val := range envMap {\n\t\tif val != \"\" {\n\t\t\t// overwrite key if exist in translation map\n\t\t\tif val, ok := translationMap[key]; ok {\n\t\t\t\tkey = val\n\t\t\t}\n\t\t\tenvList = append(envList, fmt.Sprintf(\"%s=%s\", key, val))\n\t\t}\n\t}\n\treturn envList\n}\n\n// IndentString will Add x space char padding at the beginging of each line.\nfunc IndentString(content string, spaces int) string {\n\tvar result string\n\tsubStr := strings.Split(content, \"\\n\")\n\tfor _, s := range subStr {\n\t\tresult += fmt.Sprintf(\"%\"+strconv.Itoa(spaces)+\"s%s\\n\", \"\", s)\n\t}\n\treturn result\n}\n\nfunc ItemInSlice(slice []string, target string) bool {\n\tfor _, item := range slice {\n\t\tif item == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// ReflectStructValueIntoMap receives a resource of struct type as\n// type AWSCreds struct{\n// \tAccessKeyID  string `yaml:\"accessKeyId,omitempty\"`\n// \tSecretAccessKey  string `yaml:\"secretAccessKey,omitempty\"`\n// }{\n// \tAccessKeyID: \"FOO\",\n// \tSecretAccessKey: \"BAR\",\n// }\n// It will base on the tag, fill in the value to supplied map[string]string\nfunc ReflectStructValueIntoMap(resource interface{}, tagName string, paramsToFill map[string]string) {\n\tt := reflect.ValueOf(resource)\n\n\tfor i := 0; i < t.NumField(); i++ {\n\n\t\tchildStruct := t.Type().Field(i)\n\t\tchildValue := t.Field(i)\n\t\tif childValue.Kind().String() != \"string\" {\n\t\t\tcontinue\n\t\t}\n\t\ttag, _ := parseTag(childStruct.Tag.Get(tagName))\n\t\tparamsToFill[tag] = childValue.String()\n\t}\n}\n\nfunc parseTag(tag string) (string, string) {\n\tif idx := strings.Index(tag, \",\"); idx != -1 {\n\t\treturn tag[:idx], tag[idx+1:]\n\t}\n\treturn tag, \"\"\n}\n"
  },
  {
    "path": "internal/vcs/create-git-repos.go",
    "content": "package vcs\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n\t\"github.com/machinebox/graphql\"\n)\n\n// InitializeRepository Creates and initializes a github repository for the given url\n// repositoryUrl is expected to be in the format \"github.com/{ownerName}/{repositoryName}\"\nfunc InitializeRepository(repositoryUrl string, githubApiKey string) {\n\n\tvar err error\n\townerName, repositoryName, err := parseRepositoryUrl(repositoryUrl)\n\tif err != nil {\n\t\tfmt.Printf(\"error creating repository: %s\\n\", err.Error())\n\t\treturn\n\t}\n\tflog.Debugf(\"Initialized repo: %s/%s\", ownerName, repositoryName)\n\n\tisOrgOwned, ownerId, err := isOrganizationOwned(ownerName, githubApiKey)\n\tif err != nil {\n\t\tfmt.Printf(\"error creating repository: %s\\n\", err.Error())\n\t\treturn\n\t}\n\n\tif isOrgOwned {\n\t\tr := graphql.NewRequest(createOrganizationRepositoryMutation)\n\t\tr.Var(\"repoName\", repositoryName)\n\t\tr.Var(\"repoDescription\", fmt.Sprintf(\"Repository for %s\", repositoryName))\n\t\tr.Var(\"ownerId\", ownerId)\n\t\tr.Header.Add(\"Authorization\", fmt.Sprintf(\"Bearer %s\", githubApiKey))\n\n\t\tif err := createRepository(r); err != nil {\n\t\t\tfmt.Printf(\"error creating repository: %s\\n\", err.Error())\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tr := graphql.NewRequest(createPersonalRepositoryMutation)\n\t\tr.Var(\"repoName\", repositoryName)\n\t\tr.Var(\"repoDescription\", fmt.Sprintf(\"Repository for %s\", repositoryName))\n\t\tr.Header.Add(\"Authorization\", fmt.Sprintf(\"Bearer %s\", githubApiKey))\n\n\t\tif err := createRepository(r); err != nil {\n\t\t\tfmt.Printf(\"error creating repository: %s\\n\", err.Error())\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := doInitialCommit(ownerName, repositoryName); err != nil {\n\t\tfmt.Printf(\"error initializing repository: %s\\n\", err.Error())\n\t\treturn\n\t}\n\n\tflog.Infof(\":check_mark_button: Repository created: %s\", repositoryUrl)\n}\n\n// parseRepositoryUrl extracts the owner name and repository name from a repository url.\n// repositoryUrl is expected to be in the format \"github.com/{ownerName}/{repositoryName}\"\nfunc parseRepositoryUrl(repositoryUrl string) (string, string, error) {\n\tif len(repositoryUrl) == 0 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid repository url.  expected format \\\"github.com/{ownerName}/{repositoryName}\\\"\")\n\t}\n\n\tsegments := strings.Split(repositoryUrl, \"/\")\n\tif len(segments) != 3 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid repository url.  expected format \\\"github.com/{ownerName}/{repositoryName}\\\"\")\n\t}\n\n\townerName := segments[1]\n\trepositoryName := segments[2]\n\n\treturn ownerName, repositoryName, nil\n}\n\nconst createPersonalRepositoryMutation = `mutation ($repoName: String!, $repoDescription: String!) {\n\t\tcreateRepository(\n\t\t\tinput: {\n\t\t\t\tname:$repoName,\n\t\t\t\tvisibility: PRIVATE,\n\t\t\t\tdescription: $repoDescription\n\t\t\t})\n\t\t{\n\t\t\tclientMutationId\n\t\t}\n\t}`\n\nconst createOrganizationRepositoryMutation = `mutation ($repoName: String!, $repoDescription: String!, $ownerId: ID) {\n\t\tcreateRepository(\n\t\t\tinput: {\n\t\t\t\tname:$repoName,\n\t\t\t\tvisibility: PRIVATE,\n\t\t\t\tdescription: $repoDescription\n\t\t\t\townerId: $ownerId\n\t\t\t})\n\t\t{\n\t\t\tclientMutationId\n\t\t}\n\t}`\n\n// createRepository will create a new repository in github\nfunc createRepository(request *graphql.Request) error {\n\tc := graphql.NewClient(\"https://api.github.com/graphql\")\n\tctx := context.Background()\n\tif err := c.Run(ctx, request, nil); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nconst getOrganizationQuery = `query ($ownerName: String!) {\n\t\torganization(login: $ownerName) {\n\t\t\tid\n\t\t}\n\t}`\n\ntype organizationQueryResponse struct {\n\tOrganization struct {\n\t\tId string\n\t}\n}\n\n// isOrganizationOwned will determine if ownerName is an organization.\n// If ownerName is an organization it's id will be returned.\nfunc isOrganizationOwned(ownerName string, githubApiKey string) (bool, string, error) {\n\toRequest := graphql.NewRequest(getOrganizationQuery)\n\toRequest.Var(\"ownerName\", ownerName)\n\toRequest.Header.Add(\"Authorization\", fmt.Sprintf(\"Bearer %s\", githubApiKey))\n\n\tvar oResponse organizationQueryResponse\n\tc := graphql.NewClient(\"https://api.github.com/graphql\")\n\tctx := context.Background()\n\tif err := c.Run(ctx, oRequest, &oResponse); err != nil {\n\n\t\tnotAnOrgMessage := fmt.Sprintf(\"graphql: Could not resolve to an Organization with the login of '%s'.\", ownerName)\n\t\tif err.Error() == notAnOrgMessage {\n\t\t\treturn false, \"\", nil\n\t\t}\n\t\treturn false, \"\", err\n\t}\n\torganizationId := oResponse.Organization.Id\n\n\treturn true, organizationId, nil\n}\n\ntype initialCommands struct {\n\tdescription string\n\tcommand     string\n\targs        []string\n}\n\n// getInitDefaultBranch return init.defaultBranch value in git config.\n// If init.defaultBranch isn't set,  getInitDefaultBranch return 'main'.\nfunc getInitDefaultBranch() string {\n\tcmd := exec.Command(\"git\", \"config\", \"--get\", \"init.defaultBranch\")\n\n\toutput, err := cmd.CombinedOutput()\n\n\tif err != nil {\n\t\treturn \"main\"\n\t}\n\n\tbranchName := strings.TrimSuffix(string(output), \"\\n\")\n\treturn branchName\n}\n\n// doInitialCommit runs the git commands that initialize and do the first commit to a repository.\nfunc doInitialCommit(ownerName string, repositoryName string) error {\n\tremoteOrigin := fmt.Sprintf(\"git@github.com:%s/%s.git\", ownerName, repositoryName)\n\n\tinitDefaultBranch := getInitDefaultBranch()\n\n\tcommands := []initialCommands{\n\t\t{\n\t\t\tdescription: \"git init\",\n\t\t\tcommand:     \"git\",\n\t\t\targs:        []string{\"init\"},\n\t\t},\n\t\t{\n\t\t\tdescription: \"git add .\",\n\t\t\tcommand:     \"git\",\n\t\t\targs:        []string{\"add\", \".\"},\n\t\t},\n\t\t{\n\t\t\tdescription: \"git commit -m \\\"initial commit by zero\\\"\",\n\t\t\tcommand:     \"git\",\n\t\t\targs:        []string{\"commit\", \"-m\", \"initial commit by zero\"},\n\t\t},\n\t\t{\n\t\t\tdescription: fmt.Sprintf(\"git remote add origin %s\", remoteOrigin),\n\t\t\tcommand:     \"git\",\n\t\t\targs:        []string{\"remote\", \"add\", \"origin\", remoteOrigin},\n\t\t},\n\t\t{\n\t\t\tdescription: fmt.Sprintf(\"git push -u origin %s\", initDefaultBranch),\n\t\t\tcommand:     \"git\",\n\t\t\targs:        []string{\"push\", \"-u\", \"origin\", initDefaultBranch},\n\t\t},\n\t}\n\n\tfor _, command := range commands {\n\t\t// TODO: Debug-level logging?\n\t\t// fmt.Printf(\">> %s\\n\", command.description)\n\n\t\tcmd := exec.Command(command.command, command.args...)\n\t\tcmd.Dir = \"./\" + repositoryName\n\t\tflog.Debugf(\"Running (%s) command in %s, %#v\", command.command, cmd.Dir, command.args)\n\t\t_, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"ERROR: failed to run %s: %s\\n\", command.description, err.Error())\n\t\t\t// this is a partial failure.  some commands may have exec'ed successfully.\n\t\t\tbreak\n\t\t} //else {\n\t\t// TODO: Debug-level logging?\n\t\t// response := string(out)\n\t\t// \tif len(response) > 0 {\n\t\t// \t\tfmt.Println(response)\n\t\t// \t}\n\t\t// }\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"github.com/commitdev/zero/cmd\"\n)\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "pkg/credentials/credentials.go",
    "content": "package credentials\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"regexp\"\n\n\t\"github.com/aws/aws-sdk-go/aws/credentials\"\n\t\"github.com/commitdev/zero/internal/util\"\n)\n\ntype AWSResourceConfig struct {\n\tAccessKeyID     string `key:\"accessKeyId\"`\n\tSecretAccessKey string `key:\"secretAccessKey\"`\n}\n\nfunc awsCredsPath() string {\n\tusr, err := user.Current()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn filepath.Join(usr.HomeDir, \".aws/credentials\")\n}\n\nfunc fetchAWSConfig(awsPath string, profileName string) (error, AWSResourceConfig) {\n\n\tawsCreds, err := credentials.NewSharedCredentials(awsPath, profileName).Get()\n\tif err != nil {\n\t\treturn err, AWSResourceConfig{}\n\t}\n\treturn nil, AWSResourceConfig{\n\t\tAccessKeyID:     awsCreds.AccessKeyID,\n\t\tSecretAccessKey: awsCreds.SecretAccessKey,\n\t}\n}\n\n// FillAWSProfile receives the AWS profile name, then parses\n// the accessKeyId / secretAccessKey values into a map\nfunc FillAWSProfile(pathToCredentialsFile string, profileName string, paramsToFill map[string]string) error {\n\tif pathToCredentialsFile == \"\" {\n\t\tpathToCredentialsFile = awsCredsPath()\n\t}\n\n\terr, awsCreds := fetchAWSConfig(pathToCredentialsFile, profileName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tutil.ReflectStructValueIntoMap(awsCreds, \"key\", paramsToFill)\n\treturn nil\n}\n\n// GetAWSProfiles returns a list of AWS forprofiles set up on the user's sytem\nfunc GetAWSProfiles() ([]string, error) {\n\tusr, err := user.Current()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Load the credentials file to look for profiles\n\tcredsFile := filepath.Join(usr.HomeDir, \".aws/credentials\")\n\tcreds, err := ioutil.ReadFile(credsFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Get all profiles\n\tre := regexp.MustCompile(`\\[(.*)\\]`)\n\tprofileMatches := re.FindAllStringSubmatch(string(creds), -1)\n\tprofiles := make([]string, len(profileMatches))\n\tfor i, p := range profileMatches {\n\t\tprofiles[i] = p[1]\n\t}\n\treturn profiles, nil\n}\n"
  },
  {
    "path": "pkg/credentials/credentials_test.go",
    "content": "package credentials_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/commitdev/zero/pkg/credentials\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestFillAWSProfileCredentials(t *testing.T) {\n\tmockAwsCredentialFilePath := \"../../tests/test_data/aws/mock_credentials.yml\"\n\n\tt.Run(\"fills project credentials\", func(t *testing.T) {\n\t\tparams := map[string]string{}\n\t\terr := credentials.FillAWSProfile(mockAwsCredentialFilePath, \"default\", params)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tassert.Equal(t, \"MOCK1_ACCESS_KEY\", params[\"accessKeyId\"])\n\t\tassert.Equal(t, \"MOCK1_SECRET_ACCESS_KEY\", params[\"secretAccessKey\"])\n\t})\n\n\tt.Run(\"supports non-default profiles\", func(t *testing.T) {\n\t\tparams := map[string]string{}\n\t\terr := credentials.FillAWSProfile(mockAwsCredentialFilePath, \"foobar\", params)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tassert.Equal(t, \"MOCK2_ACCESS_KEY\", params[\"accessKeyId\"])\n\t\tassert.Equal(t, \"MOCK2_SECRET_ACCESS_KEY\", params[\"secretAccessKey\"])\n\t})\n}\n"
  },
  {
    "path": "pkg/util/exit/exit.go",
    "content": "package exit\n\nimport (\n\t\"os\"\n\n\t\"github.com/commitdev/zero/pkg/util/flog\"\n)\n\nconst (\n\t// CodeOK indicates successful execution.\n\tCodeOK = 0\n\n\t// CodeError indicates erroneous execution.\n\tCodeError = 1\n\n\t// CodeFatal indicates erroneous use by user.\n\tCodeFatal = 2\n)\n\n// Fatal terminates execution using fatal exit code.\nfunc Fatal(format string, a ...interface{}) {\n\tflog.Errorf(format, a...)\n\tos.Exit(CodeFatal)\n}\n\n// Error terminates execution using unsuccessful execution exit code.\nfunc Error(format string, a ...interface{}) {\n\tflog.Errorf(format, a...)\n\tos.Exit(CodeError)\n}\n\n// OK terminates execution successfully.\nfunc OK(format string, a ...interface{}) {\n\tflog.Infof(format, a)\n\tos.Exit(CodeOK)\n}\n"
  },
  {
    "path": "pkg/util/flog/log.go",
    "content": "package flog\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kyokomi/emoji\"\n\t\"github.com/logrusorgru/aurora\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nconst LogEnvVariable = \"LOG_LEVEL\"\nconst defaultLogLevel = \"info\"\n\nvar logger = getLogger()\nvar infoFormatter = new(InfoFormatter)\nvar debugFormatter = &logrus.TextFormatter{\n\tDisableLevelTruncation:    true,\n\tFullTimestamp:             true,\n\tEnvironmentOverrideColors: true,\n}\n\nfunc getLogger() *logrus.Logger {\n\tlogger := logrus.New()\n\n\tlvl, ok := os.LookupEnv(LogEnvVariable)\n\tif !ok {\n\t\tlvl = defaultLogLevel\n\t}\n\tlogLevel, _ := logrus.ParseLevel(lvl)\n\tlogger.SetOutput(os.Stdout)\n\tlogger.SetLevel(logLevel)\n\treturn logger\n}\n\n// Warnf logs a formatted error message\nfunc Infof(format string, a ...interface{}) {\n\tlogger.SetFormatter(infoFormatter)\n\tlogger.Info(aurora.Cyan(emoji.Sprintf(format, a...)))\n}\n\nfunc Debugf(format string, a ...interface{}) {\n\tlogger.SetFormatter(debugFormatter)\n\tlogger.Debug(aurora.Green(emoji.Sprintf(format, a...)))\n}\n\n// Infof prints out a timestamp as prefix, Guidef just prints the message\nfunc Guidef(format string, a ...interface{}) {\n\tfmt.Println(aurora.Cyan(emoji.Sprintf(format, a...)))\n}\n\n// Successf logs a formatted success message\nfunc Successf(format string, a ...interface{}) {\n\tlogger.Info(aurora.Green(emoji.Sprintf(\":white_check_mark: \"+format, a...)))\n}\n\n// Warnf logs a formatted warning message\nfunc Warnf(format string, a ...interface{}) {\n\tlogger.Warn(aurora.Yellow(emoji.Sprintf(\":exclamation: \"+format, a...)))\n}\n\n// Warnf logs a formatted error message\nfunc Errorf(format string, a ...interface{}) {\n\tlogger.Error(aurora.Red(emoji.Sprintf(\":exclamation: \"+format, a...)))\n}\n\n// Info formatter is to not display the LOG_LEVEL in front of the command eg. INFO[2020-070-01T15:22:22] Hello World\ntype InfoFormatter struct {\n}\n\nfunc (f *InfoFormatter) Format(entry *logrus.Entry) ([]byte, error) {\n\t// extra line break stops the prompts from overtaking Existing line\n\treturn []byte(entry.Message + \"\\n\"), nil\n}\n"
  },
  {
    "path": "pkg/util/fs/fs.go",
    "content": "package fs\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n)\n\n// CreateDirs creates directories from the given directory path arguments.\nfunc CreateDirs(dirPaths ...string) error {\n\tfor _, path := range dirPaths {\n\t\tif err := os.MkdirAll(path, 0755); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// FileExists checks whether the given path exists and belongs to a file.\nfunc FileExists(path string) (bool, error) {\n\tinfo, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\n\t\treturn false, err\n\t}\n\n\tif info.IsDir() {\n\t\treturn false, fmt.Errorf(\"%v: is a directory, expected file\", path)\n\t}\n\n\treturn true, nil\n}\n\n// PrependPath prepends a path with prefix while disregarding back directories ../\nfunc ReplacePath(p, old, new string) string {\n\treturn path.Clean(strings.Replace(p, old, new, 1))\n}\n\n// PrependPath prepends a path with prefix while disregarding back directories ../\nfunc PrependPath(filepath string, prefix string) string {\n\tre := regexp.MustCompile(`(\\.\\.\\/)+`)\n\tcleanPath := path.Clean(filepath)\n\tbaseDir := re.FindString(cleanPath)\n\tif baseDir == \"\" {\n\t\treturn path.Join(prefix, cleanPath)\n\t}\n\treturn strings.Replace(cleanPath, baseDir, path.Join(baseDir, prefix)+\"/\", 1)\n}\n"
  },
  {
    "path": "pkg/util/fs/fs_test.go",
    "content": "package fs\n\nimport (\n\t\"testing\"\n)\n\nvar replacePathTest = []struct {\n\tpath string\n\told  string\n\tnew  string\n\tout  string\n}{\n\t{\"../../dir/file.ext\", \"../../dir\", \"output\", \"output/file.ext\"},\n\t{\"dir/file.ext\", \"dir\", \"output\", \"output/file.ext\"},\n}\n\nfunc TestReplacePath(t *testing.T) {\n\tfor _, tt := range replacePathTest {\n\t\tt.Run(tt.path, func(t *testing.T) {\n\t\t\tout := ReplacePath(tt.path, tt.old, tt.new)\n\t\t\tif out != tt.out {\n\t\t\t\tt.Errorf(\"got %q, want %q\", out, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar prependPathTests = []struct {\n\tin     string\n\tprefix string\n\tout    string\n}{\n\t{\"../../dir/file.ext\", \"prefix\", \"../../prefix/dir/file.ext\"},\n\t{\"../opps/../../dir/file.ext\", \"prefix\", \"../../prefix/dir/file.ext\"},\n\t{\"../opps/../../dir/file.ext\", \"\", \"../../dir/file.ext\"},\n\t{\"dir/file.ext\", \"prefix\", \"prefix/dir/file.ext\"},\n\t{\"dir/file.ext\", \"../prefix\", \"../prefix/dir/file.ext\"},\n\t{\"./dir/file.ext\", \"prefix\", \"prefix/dir/file.ext\"},\n}\n\nfunc TestPrependPath(t *testing.T) {\n\tfor _, tt := range prependPathTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tout := PrependPath(tt.in, tt.prefix)\n\t\t\tif out != tt.out {\n\t\t\t\tt.Errorf(\"got %q, want %q\", out, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "registry.yaml",
    "content": "- name: EKS + Go + React + Gatsby\n  moduleSources:\n  - /zero-aws-eks-stack\n  - /zero-static-site-gatsby\n  - /zero-backend-go\n  - /zero-frontend-react\n\n- name: EKS + NodeJS + React + Gatsby\n  moduleSources:\n  - /zero-aws-eks-stack\n  - /zero-static-site-gatsby\n  - /zero-backend-node\n  - /zero-frontend-react\n"
  },
  {
    "path": "tests/integration/ci/ci_test.go",
    "content": "package ci_test\n\n// @TODO refactor into new set of integration tests\n// import (\n// \t\"bytes\"\n// \t\"io/ioutil\"\n// \t\"os\"\n// \t\"sync\"\n// \t\"testing\"\n\n// \t\"github.com/commitdev/zero/internal/config\"\n// \t\"github.com/commitdev/zero/internal/generate/ci\"\n// \t\"github.com/commitdev/zero/internal/templator\"\n// \t\"github.com/gobuffalo/packr/v2\"\n// )\n\n// var testData = \"../../test_data/ci/\"\n\n// // setupTeardown removes all the generated test files before and after\n// // the test runs to ensure clean data.\n// func setupTeardown(t *testing.T) func(t *testing.T) {\n// \tos.RemoveAll(\"../../test_data/ci/actual\")\n// \treturn func(t *testing.T) {\n// \t\tos.RemoveAll(\"../../test_data/ci/actual\")\n// \t}\n// }\n\n// func TestGenerateJenkins(t *testing.T) {\n// \tteardown := setupTeardown(t)\n// \tdefer teardown(t)\n\n// \ttemplates := packr.New(\"templates\", \"../../../templates\")\n// \ttestTemplator := templator.NewTemplator(templates)\n\n// \tvar waitgroup sync.WaitGroup\n\n// \ttestConf := &projectconfig.ZeroProjectConfig{}\n// \ttestCI := config.CI{\n// \t\tSystem:       \"jenkins\",\n// \t\tBuildImage:   \"golang/golang\",\n// \t\tBuildTag:     \"1.12\",\n// \t\tBuildCommand: \"make build\",\n// \t\tTestCommand:  \"make test\",\n// \t}\n\n// \terr := ci.Generate(testTemplator.CI, testConf, testCI, testData+\"/actual\", &waitgroup)\n// \tif err != nil {\n// \t\tt.Errorf(\"Error when executing test. %s\", err)\n// \t}\n// \twaitgroup.Wait()\n\n// \tactual, err := ioutil.ReadFile(testData + \"actual/Jenkinsfile\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n// \texpected, err := ioutil.ReadFile(testData + \"/expected/Jenkinsfile\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n\n// \tif !bytes.Equal(expected, actual) {\n// \t\tt.Errorf(\"want:\\n%s\\n\\n, got:\\n%s\\n\\n\", string(expected), string(actual))\n// \t}\n// }\n\n// func TestGenerateCircleCI(t *testing.T) {\n// \tteardown := setupTeardown(t)\n// \tdefer teardown(t)\n\n// \ttemplates := packr.New(\"templates\", \"../../../templates\")\n// \ttestTemplator := templator.NewTemplator(templates)\n\n// \tvar waitgroup sync.WaitGroup\n\n// \ttestConf := &projectconfig.ZeroProjectConfig{}\n// \ttestCI := config.CI{\n// \t\tSystem:       \"circleci\",\n// \t\tBuildImage:   \"golang/golang\",\n// \t\tBuildTag:     \"1.12\",\n// \t\tBuildCommand: \"make build\",\n// \t\tTestCommand:  \"make test\",\n// \t}\n\n// \terr := ci.Generate(testTemplator.CI, testConf, testCI, testData+\"/actual\", &waitgroup)\n// \tif err != nil {\n// \t\tt.Errorf(\"Error when executing test. %s\", err)\n// \t}\n// \twaitgroup.Wait()\n\n// \tactual, err := ioutil.ReadFile(testData + \"actual/.circleci/config.yml\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n// \texpected, err := ioutil.ReadFile(testData + \"/expected/.circleci/config.yml\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n\n// \tif !bytes.Equal(expected, actual) {\n// \t\tt.Errorf(\"want:\\n%s\\n\\ngot:\\n%s\\n\\n\", string(expected), string(actual))\n// \t}\n// }\n\n// func TestGenerateTravisCI(t *testing.T) {\n// \tteardown := setupTeardown(t)\n// \tdefer teardown(t)\n\n// \ttemplates := packr.New(\"templates\", \"../../../templates\")\n// \ttestTemplator := templator.NewTemplator(templates)\n\n// \tvar waitgroup sync.WaitGroup\n\n// \ttestConf := &projectconfig.ZeroProjectConfig{}\n// \ttestCI := config.CI{\n// \t\tSystem:       \"travisci\",\n// \t\tLanguage:     \"go\",\n// \t\tBuildImage:   \"golang/golang\",\n// \t\tBuildTag:     \"1.12\",\n// \t\tBuildCommand: \"make build\",\n// \t\tTestCommand:  \"make test\",\n// \t}\n// \terr := ci.Generate(testTemplator.CI, testConf, testCI, testData+\"/actual\", &waitgroup)\n// \tif err != nil {\n// \t\tt.Errorf(\"Error when executing test. %s\", err)\n// \t}\n// \twaitgroup.Wait()\n\n// \tactual, err := ioutil.ReadFile(testData + \"actual/.travis.yml\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n// \texpected, err := ioutil.ReadFile(testData + \"/expected/.travis.yml\")\n// \tif err != nil {\n// \t\tt.Errorf(\"Error reading created file: %s\", err.Error())\n// \t}\n\n// \tif !bytes.Equal(expected, actual) {\n// \t\tt.Errorf(\"want:\\n%s\\n\\n, got:\\n%s\\n\\n\", string(expected), string(actual))\n// \t}\n// }\n"
  },
  {
    "path": "tests/test_data/apply/project1/Makefile",
    "content": "current_dir:\n\t@echo \"foo: ${foo}\" > project.out\n\t@echo \"repo: ${REPOSITORY}\" >> project.out\n\t@echo \"envVarName of viaEnvVarName: ${viaEnvVarName}\" >> feature.out\n\nsummary:\n\ncheck:\n"
  },
  {
    "path": "tests/test_data/apply/project1/zero-module.yml",
    "content": "name: project1\ndescription: 'project1'\nauthor: 'Commit'\n\ntemplate:\n  strictMode: true\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n  - aws\n  - github\n\nparameters:\n  - field: foo\n    label: foo\n  - field: param1\n    envVarName: viaEnvVarName\n"
  },
  {
    "path": "tests/test_data/apply/project2/Makefile",
    "content": "current_dir:\n\t@echo \"baz: ${baz}\" > project.out\n\nsummary:\n\ncheck:\n"
  },
  {
    "path": "tests/test_data/apply/project2/check.sh",
    "content": "pwd\necho \"custom check\" > check.out"
  },
  {
    "path": "tests/test_data/apply/project2/zero-module.yml",
    "content": "name: project2\ndescription: 'project2'\nauthor: 'Commit'\ncommands:\n  check: sh check.sh\ntemplate:\n  strictMode: true\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n  - aws\n  - github\n\nparameters:\n  - field: baz\n    label: baz\n"
  },
  {
    "path": "tests/test_data/apply/zero-project.yml",
    "content": "name: sample_project\n\nmodules:\n    project1:\n        parameters:\n            foo: bar\n            param1: baz\n        files:\n            dir: project1\n            repo: github.com/commitdev/project1\n            source: project1\n    project2:\n        parameters:\n            baz: qux\n        files:\n            dir: project2\n            repo: github.com/commitdev/project2\n            source: project2\n"
  },
  {
    "path": "tests/test_data/apply-failing/project1/Makefile",
    "content": "current_dir:\n\t@echo \"foo: ${foo}\" > project.out\n\t@echo \"repo: ${REPOSITORY}\" >> project.out\n\nsummary:\n\ncheck:\n\t@$(error \"Failure 1 of 2\")\n"
  },
  {
    "path": "tests/test_data/apply-failing/project1/project.out",
    "content": "foo: bar\nrepo: github.com/commitdev/project1\n"
  },
  {
    "path": "tests/test_data/apply-failing/project1/zero-module.yml",
    "content": "name: project1\ndescription: 'project1'\nauthor: 'Commit'\n\ntemplate:\n  strictMode: true\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n  - aws\n  - github\n\nparameters:\n  - field: foo\n    label: foo\n"
  },
  {
    "path": "tests/test_data/apply-failing/project2/Makefile",
    "content": "REQUIRED_BINS := ls nonexisting-binary\n\ncurrent_dir:\n\t@echo \"baz: ${baz}\" > project.out\n\nsummary:\n\ncheck:\n\t$(foreach bin, $(REQUIRED_BINS),\\\n\t\t$(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))\n"
  },
  {
    "path": "tests/test_data/apply-failing/project2/project.out",
    "content": "baz: qux\n"
  },
  {
    "path": "tests/test_data/apply-failing/project2/zero-module.yml",
    "content": "name: project2\ndescription: 'project2'\nauthor: 'Commit'\n\ntemplate:\n  strictMode: true\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n  - aws\n  - github\n\nparameters:\n  - field: baz\n    label: baz\n"
  },
  {
    "path": "tests/test_data/apply-failing/project3/Makefile",
    "content": "REQUIRED_BINS := ls nonexisting-binary\n\ncurrent_dir:\n\nsummary:\n"
  },
  {
    "path": "tests/test_data/apply-failing/project3/check.sh",
    "content": ">&2 echo \"Check script erroring out\";exit 1;"
  },
  {
    "path": "tests/test_data/apply-failing/project3/project.out",
    "content": "baz: qux\n"
  },
  {
    "path": "tests/test_data/apply-failing/project3/zero-module.yml",
    "content": "name: project3\ndescription: 'project3'\nauthor: 'Commit'\n\ncommands:\n  check: sh check.sh\ntemplate:\n  strictMode: true\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n  - aws\n  - github\n\nparameters:\n  - field: baz\n    label: baz\n"
  },
  {
    "path": "tests/test_data/apply-failing/zero-project.yml",
    "content": "name: sample_project\n\nmodules:\n    project1:\n        parameters:\n            foo: bar\n        files:\n            dir: project1\n            repo: github.com/commitdev/project1\n            source: project1\n    project2:\n        parameters:\n            baz: qux\n        files:\n            dir: project2\n            repo: github.com/commitdev/project2\n            source: project2\n    project3:\n        files:\n            dir: project3\n            repo: github.com/commitdev/project3\n            source: project3\n"
  },
  {
    "path": "tests/test_data/aws/mock_credentials.yml",
    "content": "[default]\naws_access_key_id=MOCK1_ACCESS_KEY\naws_secret_access_key=MOCK1_SECRET_ACCESS_KEY\n\n[foobar]\naws_access_key_id=MOCK2_ACCESS_KEY\naws_secret_access_key=MOCK2_SECRET_ACCESS_KEY\n"
  },
  {
    "path": "tests/test_data/ci/expected/.circleci/config.yml",
    "content": "version: 2.1\njobs:\n  build:\n    docker:\n      - image: golang/golang:1.12\n    steps:\n      - checkout\n      - run:\n        name: Build\n        command: |\n          make build\n\n  test:\n    docker:\n      - image: golang/golang:1.12\n    steps:\n      - checkout\n      - run:\n        name: Test\n        command: |\n          make test\n\n\nworkflow:\n  version: 2.1\n  build_and_test:\n    jobs:\n      - build\n      - test\n"
  },
  {
    "path": "tests/test_data/ci/expected/.travis.yml",
    "content": "language: go\ngo:\n- 1.12\n\nscripts:\n- make build\n- make test\n"
  },
  {
    "path": "tests/test_data/ci/expected/Jenkinsfile",
    "content": "pipeline {\n    agent none\n    stages {\n        stage('Build and Test') {\n            parallel {\n                stage('Build') {\n                    agent {\n                        docker {\n                            image 'golang/golang:1.12'\n                        }\n                    }\n                    steps {\n                        sh 'make build'\n                    }\n                }\n                stage('Test') {\n                    agent {\n                        docker {\n                            image 'golang/golang:1.12'\n                        }\n                    }\n                    steps {\n                        sh 'make test'\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/test_data/configs/commit0_submodules.yml",
    "content": "name: hello-world\n\n# Context will populated automatically or could be added manually\ncontext: \n  cognitoPoolID: 123\n  cognitoClientID: ABC\n\nmodules: \n  # module can be in any format the go-getter supports (path, github, url, etc.)\n  # supports https://github.com/hashicorp/go-getter#url-format\n  # - source: './tests/test_data/modules/ci'\n  #   params: \n  #     ci: github\n\n  - source: './remote_templates/services'\n    # alternatively we can recursively support sub modules\n    modules: \n      - './remote_templates/ci/go'\n"
  },
  {
    "path": "tests/test_data/configs/credentials.yml",
    "content": "another-project:\n    github:\n        accessToken: \"654\"\nmy-project:\n    aws:\n        accessKeyId: AKIAABCD\n        secretAccessKey: ZXCV\n    github:\n        accessToken: \"0987\"\n    circleci:\n        apiKey: SOME_API_KEY\n"
  },
  {
    "path": "tests/test_data/configs/zero-basic.yml",
    "content": "name: hello-world\n\n# Context will populated automatically or could be added manually\ncontext: \n  cognitoPoolID: 123\n  cognitoClientID: ABC\n\nmodules: \n  # module can be in any format the go-getter supports (path, github, url, etc.)\n  # supports https://github.com/hashicorp/go-getter#url-format\n  - source: \"../../tests/test_data/modules/ci\"\n    params:\n      ci: github\n\n  # - source: './remote_templates/services'\n  #   # alternatively we can recursively support sub modules\n  #   modules: \n  #     - './remote_templates/ci/go'\n"
  },
  {
    "path": "tests/test_data/generate/file_to_template.txt",
    "content": "Name is {{.Name}}\nParams.test is {{.Params.test}}\nFiles.Repository is {{.Files.Repository}}\n"
  },
  {
    "path": "tests/test_data/generate/zero-module.yml",
    "content": "name: test-generate\ndescription: 'generation test module'\nauthor: 'Commit'\n\ntemplate:\n  strictMode: true\n  delimiters:\n    - '{{'\n    - '}}'\n  inputDir: '.'\n  outputDir: 'test'\n\nrequiredCredentials:\n\nparameters:\n"
  },
  {
    "path": "tests/test_data/modules/ci/config1.yml",
    "content": "content1: {{ .ci }}"
  },
  {
    "path": "tests/test_data/modules/ci/dir/config2.yml",
    "content": "content2:\n"
  },
  {
    "path": "tests/test_data/modules/ci/zero-module.yml",
    "content": "name: \"CI templates\"\ndescription: \"CI description\"\nauthor: \"CI author\"\nicon: \"\"\nthumbnail: \"\"\nzeroVersion: \">= 3.0.0, < 4.0.0\"\ncommands:\n  check: ls\n\nrequiredCredentials:\n  - aws\n  - circleci\n  - github\n\n# Template variables to populate, these could be overwritten by the file spefic frontmatter variables\ntemplate:\n  # strictMode: true # will only parse files that includes the .tmpl.* extension, otherwise it will copy file\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: 'templates'\n  outputDir: \".circleci\"\n\n# required context parameters: will throw a warning message at the end if any of the context parameters are not present\n# contextRequired:\n#   - cognitoPoolID\n#   - cognitoClientID\n\n# parameters required from user to populate the template params\nparameters:\n  - field: platform\n    label: CI Platform\n    # default: github\n    options:\n      github: Github\n      circleci: Circle CI\n  - field: circleci_api_key\n    label: \"Circle CI API Key to setup your CI/CD for repositories\"\n    conditions:\n    - action: KeyMatchCondition\n      matchField: platform\n      whenValue: \"circlci\"\n  - field: useExistingAwsProfile\n    label: \"Use credentials from an existing AWS profile?\"\n    options:\n      \"yes\": \"Yes\"\n      \"no\": \"No\"\n    omitFromProjectFile: yes\n  - field: profilePicker\n    omitFromProjectFile: yes\n    type: AWSProfilePicker\n    conditions:\n    - action: KeyMatchCondition\n      whenValue: \"yes\"\n      matchField: useExistingAwsProfile\n  - field: accessKeyId\n    label: AWS AccessKeyId\n    envVarName: \"AWS_ACCESS_KEY_ID\"\n    conditions:\n    - action: KeyMatchCondition\n      whenValue: \"no\"\n      matchField: useExistingAwsProfile\n  - field: secretAccessKey\n    envVarName: \"AWS_SECRET_ACCESS_KEY\"\n    label: AWS SecretAccessKey\n    conditions:\n    - action: KeyMatchCondition\n      whenValue: \"no\"\n      matchField: useExistingAwsProfile\n  - field: testExecute\n    execute: echo $AWS_ACCESS_KEY_ID\n"
  },
  {
    "path": "tests/test_data/modules/no-version-constraint/zero-module.yml",
    "content": "name: \"Test module\"\ndescription: \"a module for testing, with no zero version requirement\"\nauthor: \"Test module author\"\nicon: \"\"\nthumbnail: \"\"\n\ntemplate:\n  delimiters:\n    - \"<%\"\n    - \"%>\"\n  inputDir: templates\n  outputDir: test-module-output\n\nrequiredCredentials:\n  - aws\n  - circleci\n  - github\n\nparameters:\n"
  },
  {
    "path": "tests/test_data/projectconfig/zero-project.yml",
    "content": "# Graph shape:\n#    2\n#  /  \\\n# 1    4\n#  \\  /\n#   3 - 5\n\nname: graph_test\n\nmodules:\n    project1:\n        parameters:\n            foo: bar\n    project2:\n        dependsOn:\n        - project1\n        parameters:\n            baz: qux\n    project3:\n        dependsOn:\n        - project1\n        parameters:\n            baz: qux\n    project4:\n        dependsOn:\n        - project2\n        - project3\n        parameters:\n            baz: qux\n    project5:\n        dependsOn:\n        - project3\n        parameters:\n            baz: qux\n"
  },
  {
    "path": "version/version.go",
    "content": "package version\n\n// These values are overridden by makefile during build\nvar (\n\tAppVersion = \"SNAPSHOT\"\n\tAppBuild   = \"SNAPSHOT\"\n)\n"
  }
]