Запуск команд git против других репозиториев

В настоящее время я создаю конвейер для DevOps Azure, чтобы проверить и применить конфигурацию Terraform к другой подписке.

В моей конфигурации terraform используются модули, которые "размещены" в других репозиториях того же проекта Azure DevOps, что и конфигурация terraform.

К сожалению, когда я пытаюсь выполнить terraform init чтобы получить эти модули, конвейерная задача "зависает" там в ожидании ввода учетных данных.

Как рекомендовано в документации конвейера по запуску команд Git в скрипте, я попытался добавить checkout шаг с persistCredentials:true приписывать.

Из того, что я вижу в журнале задачи (см. Ниже), информация об учетных данных добавляется специально к текущему репо и не может использоваться для других репо.

Команда выполняется при добавленииpersistCredentials:true

2018-10-22T14:06:54.4347764Z ##[command]git config http.https://my-org@dev.azure.com/my-org/my-project/_git/my-repo.extraheader "AUTHORIZATION: bearer ***"

Вывод задачи инициализации terraform

2018-10-22T14:09:24.1711473Z terraform init -input=false
2018-10-22T14:09:24.2761016Z Initializing modules...
2018-10-22T14:09:24.2783199Z - module.my-module
2018-10-22T14:09:24.2786455Z   Getting source "git::https://my-org@dev.azure.com/my-org/my-project/_git/my-module-repo?ref=1.0.2"

Как настроить учетные данные git для работы с другими репозиториями?

7 ответов

У меня была такая же проблема, в итоге я сделал токенизацию SYSTEM_ACCESSTOKENв конфигурации терраформ. Я использовал задачу Tokenzization в Azure DevOps, где__ префикс и суффикс используются для идентификации и замены токенов фактическими переменными (они настраиваются, но я считаю, что двойные подчеркивания лучше всего не мешают любому имеющемуся у меня коду)

- task: qetza.replacetokens.replacetokens-task.replacetokens@3
    displayName: 'Replace tokens'
    inputs:
      targetFiles: |
       **/*.tfvars
       **/*.tf
      tokenPrefix: '__'
      tokenSuffix: '__'

Что-то типа find $(Build.SourcesDirectory)/ -type f -name 'main.tf' -exec sed -i 's~__SYSTEM_ACCESSTOKEN__~$(System.AccessToken)~g' {} \; также будет работать, если у вас нет возможности устанавливать собственные расширения в свою организацию DevOps.

Мой terraform main.tf выглядит так:

module "app" {
  source = "git::https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules//azure/app-service?ref=__app-service-module-ver__"
  ....
}

Это не красиво, но выполняет свою работу. Источник модуля (на момент написания) не поддерживает ввод переменных из terraform. Итак, что мы можем сделать, так это использовать Terrafile, это проект с открытым исходным кодом, помогающий не отставать от модулей и различных версий одного и того же модуля, который вы можете использовать, сохраняя простой файл YAML рядом с вашим кодом. Кажется, что он больше не поддерживается, но он просто работает: https://github.com/coretech/terrafile мой пример Terrafile:

app:
    source:  "https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules"
    version: "feature/handle-twitter"
app-stable:
    source:  "https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules"
    version: "1.0.5"

Terrafile по умолчанию загружает ваши модули в каталог./vendor, чтобы вы могли указать источник вашего модуля на что-то вроде:

module "app" {
  source = "./vendor/modules/app-stable/azure/app_service"
  ....
}

Теперь вам просто нужно выяснить, как выполнить terrafileв каталоге, где находится Terrafile. Мой пример azure.pipelines.yml:

- script: curl -L https://github.com/coretech/terrafile/releases/download/v0.6/terrafile_0.6_Linux_x86_64.tar.gz | tar xz -C $(Agent.ToolsDirectory)
  displayName: Install Terrafile

- script: |
    cd $(Build.Repository.LocalPath)
    $(Agent.ToolsDirectory)/terrafile
  displayName: Download required modules

У вас есть по существу два способа сделать это.

Предварительное условие

Убедитесь, что вы прочитали и, в зависимости от ваших потребностей, применили раздел « Включить сценарии для запуска команд Git » в документе «Выполнение команд Git в сценарии».

Решение №1: динамически вставить System.AccessToken(или PAT, но я бы не рекомендовал) во время выполнения конвейера

Вы можете сделать это либо:

  • вставка заменяющего токена, такого как __SYSTEM_ACCESSTOKEN__в вашем коде (как предлагает Нильсас ) и используйте код замены токена или qetza.replacetokens.replacetokens-task.replacetokensзадача вставить значение. Недостатком этого решения является то, что вам также придется заменить токен при локальном запуске.
  • используя некоторый код для замены всех git::https://dev.azure.comтекст с git::https://YOUR_ACCESS_TOKEN@dev.azure.com.

Я использовал второй подход, используя следующий сценарий задачи bash (он ищет файлы, но вы можете адаптироваться к файлам без особых изменений):

      - bash: |
    find $(Build.SourcesDirectory)/ -type f -name 'terragrunt.hcl' -exec sed -i 's~git::https://dev.azure.com~git::https://$(System.AccessToken)@dev.azure.com~g' {} \;

Абу Белай предлагает сценарий PowerShell для выполнения чего-то подобного.

Однако этот тип решения не работает, если модули в вашем репозитории модулей называют себя модулями в другом репо, как это было в нашем случае.

Решение № 2: глобальное добавление токена доступа в URL-адрес ваших репозиториев модулей

Таким образом, все репозитории модулей, вызываемые непосредственно вашим кодом или косвенно вызываемые кодом вызываемых модулей, смогут использовать ваш токен доступа. Я сделал это, добавив следующий шаг перед вашим / terragruntзвонки:

      - bash: |
    git config --global http.https://dev.azure.com/<your-org>/<your-first-repo-project>/_git/<your-first-repo>.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"
    git config --global http.https://dev.azure.com/<your-org>/<your-second-repo-project>/_git/<your-second-repo>.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"

Вам нужно будет установить для каждого из вызываемых репозиториев.

Имейте в виду, что вам может понадобиться отключить terraformвызовы, если ваш конвейер устанавливает несколько раз для одного и того же работника. Это потому что gitможно спутать с несколькими extraheaderдекларация. Вы делаете это, добавляя к следующему шагу:

      - bash: |
    git config --global --unset-all http.https://dev.azure.com/<your-org>/<your-first-repo-project>/_git/<your-first-repo>.extraheader
    git config --global --unset-all http.https://dev.azure.com/<your-org>/<your-second-repo-project>/_git/<your-second-repo>.extraheader 

Я сделал это

_ado_token.ps1

# used in Azure DevOps to allow terrform to auth with Azure DevOps GIT repos
$tfmodules = Get-ChildItem $PSScriptRoot -Recurse -Filter "*.tf"
foreach ($tfmodule in $tfmodules) {
    $content = [System.IO.File]::ReadAllText($tfmodule.FullName).Replace("git::https://myorg@","git::https://" + $env:SYSTEM_ACCESSTOKEN +"@")
    [System.IO.File]::WriteAllText($tfmodule.FullName, $content)
}

azure-pipelines.yml

- task: PowerShell@2
  env: 
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)
  inputs:
    filePath: '_ado_token.ps1'
    pwsh: true
  displayName: '_ado_token.ps1'

Это сценарий:

У вас есть два репозитория git в одном проекте.

содержит код модуля terraform.

содержит код terraform, который указывает (с определенным тегом, например1.0.0) в качестве источника модуля.

Yaml вашего конвейера находится в формате .

Тогда вот как ваш модульный блокRepo2будет выглядеть так:

      module "alz" {
  source  = "git::https://dev.azure.com/<org>/<project>/_git/Repo1?ref=<tag>"
  .
  .
}

В вашем конвейере Azure

Сначала вы объявите ресурс репозитория дляRepo1:

      resources:
  repositories:
  - repository: Repo1
    type: git
    name: "<project>/Repo1"

Затем извлеките оба репозитория в конвейере, но сохраните учетные данные для repo2.

        # Checkout Repo2  
  - checkout : self 

  # Checkout Repo1  
  - checkout : Repo1
    persistCredentials: 'true'

Даже если вы избежите очередиpersistCredentials: 'true', обязательно проверьте оба репозитория. Если вы просто оформляете заказself, то есть только Repo2, он может не работать при получении данных из Repo1 и выдаст ошибку:

      │ remote: TF401019: The Git repository with name or identifier
│ Repo1 does not exist or you do not have
│ permissions for the operation you are attempting.

Теперь установите учетные данные, т. е. System.AccessToken передterraform init. Вы можете сделать это двумя способами.

Опция 1:

        - bash: |
      git config --global http.https://dev.azure.com/<org>/<project>/_git/Repo1.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"
    displayName: Set Credential

  # Terraform Init
  - task: TerraformTaskV4@4
    inputs:
      provider: 'azurerm'
      command: 'init'
      workingDirectory: '$(working_dir)'
      backendServiceArm: $(sc_tf_backend)
      backendAzureRmResourceGroupName: $(backendRGName)
      backendAzureRmStorageAccountName: $(backendSAName)
      backendAzureRmContainerName: $(backendContainerName)
      backendAzureRmKey: $(backendBlobKey)

Вариант 2:

        - bash: |
      MY_TOKEN=$(System.AccessToken)
      git config --global url."https://${MY_TOKEN}@dev.azure.com".insteadOf "https://dev.azure.com"
    displayName: Set Credential

  # Terraform Init
  - task: TerraformTaskV4@4
    inputs:
      provider: 'azurerm'
      command: 'init'
      workingDirectory: '$(working_dir)'
      backendServiceArm: $(sc_tf_backend)
      backendAzureRmResourceGroupName: $(backendRGName)
      backendAzureRmStorageAccountName: $(backendSAName)
      backendAzureRmContainerName: $(backendContainerName)
      backendAzureRmKey: $(backendBlobKey)

Насколько я понимаю, лучший способ сделать это точно такой же, как и с любым другим поставщиком Git. Только для Azure DevOps я когда-либо сталкивался с подход. Я всегда использовал это, и после того, как не смог получить удовлетворительный результат с другими предложенными подходами, я вернулся к нему:

      - script: |
    MY_TOKEN=foobar
    git config --global url."https://${MY_TOKEN}@dev.azure.com".insteadOf "https://dev.azure.com"
 

Я решил проблему, создав шаблон конвейера, который запускает встроенный скрипт powershell. Затем я извлекаю шаблон в качестве шаблона Pipeline как «ресурс» при использовании любого модуля terraform из другого репозитория. Сценарий выполнит рекурсивный поиск всех файлов .tf. Затем используйте регулярное выражение для обновления всех URL-адресов источника модуля.

Я выбрал REGEX, а не токенизацию URL-адреса модуля, потому что это гарантирует, что модули могут быть загружены на машине разработки без каких-либо изменений в исходном коде.

      parameters:
- name: terraform_directory
  type: string

steps:
  - task: PowerShell@2
    displayName: Tokenize TF-Module Sources
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
    inputs:
      targetType: 'inline'
      
      script: |
        $regex = "https://*(.+)dev.azure.com"
        $tokenized_url = "https://token:$($env:SYSTEM_ACCESSTOKEN)@dev.azure.com"

        Write-Host "Recursive Search in ${{ parameters.terraform_directory }}"
        $tffiles = Get-ChildItem -Path "${{ parameters.terraform_directory }}" -Filter "*main.tf" -Recurse -Force

        Write-Host "Found $($tffiles.Count) files ending with 'main.tf'"
        if ($tffiles) { Write-Host $tffiles }

        $tffiles | % {
          Write-Host "Updating file $($_.FullName)"
          $content = Get-Content $_.FullName

          Write-Host "Replace Strings: $($content | Select-String -Pattern $regex)"
          
          $content -replace $regex, $tokenized_url | Set-Content $_.FullName -Force

          Write-Host "Updated content"
          Write-Host (Get-Content $_.FullName)
        }

Я не думаю, что ты можешь. Обычно вы создаете другую сборку и ссылаетесь на артефакты из этой сборки, чтобы использовать ее в своем текущем определении. Таким образом, вам не нужно подключаться к другому Git-репозиторию

Другие вопросы по тегам