Skip to main content

GitHub Actions Importer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ

GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•ด GitHub Actions(์œผ)๋กœ์˜ Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ž๋™ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

GitHub Actions Importer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ ์ •๋ณด

์•„๋ž˜ ์ง€์นจ์€ GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋„๋ก ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค.

ํ•„์ˆ˜ ์กฐ๊ฑด

  • GitHub Actions ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜ํ•  ํ”„๋กœ์ ํŠธ ๋ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณด์œ ํ•œ Azure DevOps ๊ณ„์ • ๋˜๋Š” ์กฐ์ง.
  • ๊ณ„์ • ๋˜๋Š” ์กฐ์ง์˜ Azure DevOps personal access token์„(๋ฅผ) ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์•ก์„ธ์Šค ๊ถŒํ•œ.
  • Linux ๊ธฐ๋ฐ˜ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ํ•„์š”ํ•œ ๋„๊ตฌ๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค.

    ์ฐธ๊ณ  ํ•ญ๋ชฉ

    GitHub Actions Importer ์ปจํ…Œ์ด๋„ˆ์™€ CLI๋Š” CI ํ”Œ๋žซํผ๊ณผ ๋™์ผํ•œ ์„œ๋ฒ„์— ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์ œํ•œ ์‚ฌํ•ญ

GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps์—์„œ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๊ฒฝ์šฐ ๋ช‡ ๊ฐ€์ง€ ์ œํ•œ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • GitHub Actions Importer์—๋Š” Azure DevOps Services ๋˜๋Š” Azure DevOps Server 2019์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Azure DevOps API ๋ฒ„์ „ 5.0์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์˜ Azure DevOps Server๋Š” ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์†Œ์Šค ์ฝ”๋“œ ๊ฒ€์‚ฌ์™€ ๊ฐ™์ด Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์— ์•”์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€๋˜๋Š” ์ž‘์—…์€ GitHub Actions Importer ๊ฐ์‚ฌ์— GUID ์ด๋ฆ„์œผ๋กœ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. GUID์— ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹์€ ์ž‘์—… ์ด๋ฆ„์„ ์ฐพ์œผ๋ ค๋ฉด ์ด URL(https://dev.azure.com/:organization/_apis/distributedtask/tasks/:guid)์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆ˜๋™ ์ž‘์—…

ํŠน์ • Azure DevOps ๊ตฌ๋ฌธ์€ Azure DevOps์—์„œ GitHub Actions ๊ตฌ์„ฑ์œผ๋กœ ์ˆ˜๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ๋‹ค์Œ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

  • ์กฐ์ง, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฐ ํ™˜๊ฒฝ ๋น„๋ฐ€
  • OIDC ์—ฐ๊ฒฐ, GitHub Apps, personal access tokens ๋“ฑ์˜ ์„œ๋น„์Šค ์—ฐ๊ฒฐ
  • ์•Œ ์ˆ˜ ์—†๋Š” ์ž‘์—…
  • ์ž์ฒด ํ˜ธ์ŠคํŒ… ์—์ด์ „ํŠธ
  • ํ™˜๊ฒฝ
  • ๋ฐฐํฌ ์ „ ์Šน์ธ

์ˆ˜๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Azure Pipelines์—์„œ GitHub Actions๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

์ง€์›๋˜์ง€ ์•Š๋Š” ์ž‘์—…

GitHub Actions Importer์€(๋Š”) ๋‹ค์Œ ์ž‘์—… ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ๋ฐฐํฌ ์ „ ๊ฒŒ์ดํŠธ
  • ๋ฐฐํฌ ํ›„ ๊ฒŒ์ดํŠธ
  • ๋ฐฐํฌ ํ›„ ์Šน์ธ
  • ์ผ๋ถ€ ๋ฆฌ์†Œ์Šค ํŠธ๋ฆฌ๊ฑฐ

GitHub Actions Importer CLI ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ ์„ค์น˜ํ•˜๊ธฐ

  1. GitHub Actions Importer CLI ํ™•์žฅ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

    Bash
    gh extension install github/gh-actions-importer
    
  2. ๋‹ค์Œ ํ™•์žฅ์ด ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ:

    $ gh actions-importer -h
    Options:
      -?, -h, --help  Show help and usage information
    
    Commands:
      update     Update to the latest version of GitHub Actions Importer.
      version    Display the version of GitHub Actions Importer.
      configure  Start an interactive prompt to configure credentials used to authenticate with your CI server(s).
      audit      Plan your CI/CD migration by analyzing your current CI/CD footprint.
      forecast   Forecast GitHub Actions usage from historical pipeline utilization.
      dry-run    Convert a pipeline to a GitHub Actions workflow and output its yaml file.
      migrate    Convert a pipeline to a GitHub Actions workflow and open a pull request with the changes.
    

์ž๊ฒฉ ์ฆ๋ช… ๊ตฌ์„ฑํ•˜๊ธฐ

configure CLI ๋ช…๋ น์€ Azure DevOps ๋ฐ GitHub(์œผ)๋กœ ์ž‘์—…ํ•  ๋•Œ GitHub Actions Importer์— ํ•„์š”ํ•œ ์ž๊ฒฉ ์ฆ๋ช… ๋ฐ ์˜ต์…˜์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  1. GitHub personal access token (classic)์„(๋ฅผ) ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ฐœ์ธ์šฉ ์•ก์„ธ์Šค ํ† ํฐ ๊ด€๋ฆฌ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

    ์ด ํ† ํฐ์—๋Š” workflow ๋ฒ”์œ„๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ํ† ํฐ์„ ๋งŒ๋“  ํ›„ ๋ณต์‚ฌํ•˜๊ณ  ๋‚˜์ค‘์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ˆ์ „ํ•œ ์œ„์น˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

  2. Azure DevOps personal access token์„(๋ฅผ) ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Azure DevOps ์„ค๋ช…์„œ์˜ personal access tokens ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. ํ† ํฐ์—๋Š” ๋‹ค์Œ ๋ฒ”์œ„๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    • ์—์ด์ „ํŠธ ํ’€: Read
    • ๋นŒ๋“œ: Read
    • ์ฝ”๋“œ: Read
    • ๋ฆด๋ฆฌ์Šค: Read
    • ์„œ๋น„์Šค ์—ฐ๊ฒฐ: Read
    • ์ž‘์—… ๊ทธ๋ฃน: Read
    • ๋ณ€์ˆ˜ ๊ทธ๋ฃน: Read

    ํ† ํฐ์„ ๋งŒ๋“  ํ›„ ๋ณต์‚ฌํ•˜๊ณ  ๋‚˜์ค‘์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ˆ์ „ํ•œ ์œ„์น˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

  3. ํ„ฐ๋ฏธ๋„์—์„œ GitHub Actions Importer configure CLI ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    gh actions-importer configure
    

    configure ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค:

    • "์–ด๋–ค CI ๊ณต๊ธ‰์ž๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์žˆ๋‚˜์š”?"์— ๋Œ€ํ•ด ํ™”์‚ดํ‘œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps์„(๋ฅผ) ์„ ํƒํ•˜๊ณ Space์„ ๋ˆŒ๋Ÿฌ ์„ ํƒํ•œ ๋‹ค์ŒEnter๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.
    • "Personal access token for GitHub"์— ์•ž์„œ ๋งŒ๋“  personal access token (classic)์˜ ๊ฐ’์„ ์ž…๋ ฅํ•˜๊ณ  EnterEnter๋‹ค.
    • โ€œGitHub ์ธ์Šคํ„ด์Šค์˜ ๊ธฐ๋ณธ URLโ€์˜ ๊ฒฝ์šฐ ์ž…๋ ฅ์„ ๋ˆŒ๋Ÿฌ ๊ธฐ๋ณธ๊ฐ’(https://github.com)์„ ์ˆ˜๋ฝํ•ฉ๋‹ˆ๋‹ค.
    • "Azure DevOps์šฉ Personal access token"์˜ ๊ฒฝ์šฐ, ์ด์ „์— ๋งŒ๋“  Azure DevOps personal access token์˜ ๊ฐ’์„ ์ž…๋ ฅํ•˜๊ณ  Enter ํ‚ค๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.
    • "Azure DevOps ์ธ์Šคํ„ด์Šค์˜ ๊ธฐ๋ณธ URL"์˜ ๊ฒฝ์šฐ, Enter ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ๊ธฐ๋ณธ๊ฐ’(https://dev.azure.com)์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • "Azure DevOps ์กฐ์ง ์ด๋ฆ„"์˜ ๊ฒฝ์šฐ, Azure DevOps ์กฐ์ง์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜๊ณ  Enter ํ‚ค๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.
    • "Azure DevOps ํ”„๋กœ์ ํŠธ ์ด๋ฆ„"์˜ ๊ฒฝ์šฐ, Azure DevOps ํ”„๋กœ์ ํŠธ์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜๊ณ  Enter ํ‚ค๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.

    configure ๋ช…๋ น์˜ ์˜ˆ๊ฐ€ ์•„๋ž˜์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค:

    $ gh actions-importer configure
    โœ” Which CI providers are you configuring?: Azure DevOps
    Enter the following values (leave empty to omit):
    โœ” Personal access token for GitHub: ***************
    โœ” Base url of the GitHub instance: https://github.com
    โœ” Personal access token for Azure DevOps: ***************
    โœ” Base url of the Azure DevOps instance: https://dev.azure.com
    โœ” Azure DevOps organization name: :organization
    โœ” Azure DevOps project name: :project
    Environment variables successfully updated.
    
  4. ํ„ฐ๋ฏธ๋„์—์„œ GitHub Actions Importer update CLI ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ GitHub Packages Container registry์— ์—ฐ๊ฒฐํ•˜๊ณ , ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๊ฐ€ ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    gh actions-importer update
    

    ๋ช…๋ น์˜ ์ถœ๋ ฅ์€ ์•„๋ž˜์™€ ๋น„์Šทํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

    Updating ghcr.io/actions-importer/cli:latest...
    ghcr.io/actions-importer/cli:latest up-to-date
    

Azure DevOps ๊ฐ์‚ฌ ์ˆ˜ํ–‰

audit ๋ช…๋ น์œผ๋กœ Azure DevOps ์กฐ์ง์˜ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋žต์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

audit ๋ช…๋ น์€ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  1. Azure DevOps ์กฐ์ง์— ์ •์˜๋œ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  2. ๊ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ•ด๋‹นํ•˜๋Š” GitHub Actions ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  3. GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•˜์—ฌ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์˜ ์™„์ „ํ•˜๊ณ  ๋ณต์žกํ•œ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์„ ์š”์•ฝํ•˜๋Š” ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ๋ช…๋ น ์‹คํ–‰

Azure DevOps ์กฐ์ง ๊ฐ์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

gh actions-importer audit azure-devops --output-dir tmp/audit

๊ฐ์‚ฌ ๊ฒฐ๊ณผ ๊ฒ€์‚ฌํ•˜๊ธฐ

์ง€์ •๋œ ์ถœ๋ ฅ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ํŒŒ์ผ์—๋Š” ๊ฐ์‚ฌ์˜ ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์š”์•ฝ์€ audit_summary.md ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

๊ฐ์‚ฌ ์š”์•ฝ์—๋Š” ๋‹ค์Œ์˜ ์„น์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Pipelines

"ํŒŒ์ดํ”„๋ผ์ธ" ์„น์…˜์—๋Š” GitHub Actions Importer์ด(๊ฐ€) ์ˆ˜ํ–‰ํ•œ ๋ณ€ํ™˜๋ฅ ์— ๋Œ€ํ•œ ๊ฐœ๋žต์ ์ธ ํ†ต๊ณ„๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์— "ํŒŒ์ดํ”„๋ผ์ธ" ์„น์…˜์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ์šฉ์–ด๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„ฑ๊ณตํ•œ ํŒŒ์ดํ”„๋ผ์ธ์—๋Š” ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ๋ฌธ์˜ 100%๊ฐ€ ์žˆ๊ณ  ๊ฐœ๋ณ„ ํ•ญ๋ชฉ์€ ํ•ด๋‹น GitHub Actions(์œผ)๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.
  • ๋ถ€๋ถ„์ ์œผ๋กœ ์„ฑ๊ณตํ•œ ํŒŒ์ดํ”„๋ผ์ธ์—๋Š” ๋ชจ๋“  ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ๋ฌธ์ด ๋ณ€ํ™˜๋˜์ง€๋งŒ ํ•ด๋‹น GitHub Actions์œผ๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š์€ ์ผ๋ถ€ ๊ฐœ๋ณ„ ํ•ญ๋ชฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ง€์› ๋˜์ง€ ์•Š๋Š” ํŒŒ์ดํ”„๋ผ์ธ์€ GitHub Actions Importer์—์„œ ์ง€์›๋˜์ง€ ์•Š๋Š” ์ •์˜ ํ˜•์‹์ž…๋‹ˆ๋‹ค.
  • ์‹คํŒจํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ€ํ™˜ํ•  ๋•Œ๋Š” ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์ด ํ˜„์ƒ์€ ๋‹ค์Œ์˜ 3๊ฐ€์ง€ ์ด์œ ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํŒŒ์ดํ”„๋ผ์ธ์ด ์›๋ž˜ ์ž˜๋ชป ๊ตฌ์„ฑ๋˜์–ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • GitHub Actions Importer์„(๋ฅผ) ๋ณ€ํ™˜ํ•  ๋•Œ ๋‚ด๋ถ€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํŒŒ์ดํ”„๋ผ์ธ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†๊ฒŒ ๋˜๋Š” ์‹คํŒจํ•œ ๋„คํŠธ์›Œํฌ ์‘๋‹ต์ด ์žˆ์œผ๋ฉฐ, ์ด๋Š” ์ข…์ข… ์ž˜๋ชป๋œ ์ž๊ฒฉ ์ฆ๋ช… ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋นŒ๋“œ ๋‹จ๊ณ„

"๋นŒ๋“œ ๋‹จ๊ณ„" ์„น์…˜์—๋Š” ๋ชจ๋“  ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋ณ„ ๋นŒ๋“œ ๋‹จ๊ณ„ ๋ฐ GitHub Actions Importer์—์„œ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ฐœ์ˆ˜์— ๋Œ€ํ•œ ๊ฐœ์š”๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ "ํŒŒ์ดํ”„๋ผ์ธ" ์„น์…˜์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ์šฉ์–ด๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์•Œ๋ ค์ง„ ๋นŒ๋“œ ๋‹จ๊ณ„๋Š” ํ•ด๋‹น ๋™์ž‘์œผ๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.
  • ์•Œ๋ ค์ง„ ๋นŒ๋“œ ๋‹จ๊ณ„๋Š” ํ•ด๋‹น ๋™์ž‘์œผ๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š์€ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.
  • ์ง€์›๋˜์ง€ ์•Š๋Š” ๋นŒ๋“œ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.
    • GitHub Actions์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • GitHub Actions๊ณผ(์™€) ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ์ž‘์—…์€ ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ์—์„œ ์‚ฌ์šฉ๋œ ์ž‘์—…์˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— ์ค‘์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • GitHub Enterprise Server์„(๋ฅผ) ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šค์™€ ๋™๊ธฐํ™”ํ•  ์ž‘์—… ๋ชฉ๋ก์„ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ๋˜๋Š” ์ž‘์—…์˜ ์กฐ์ง ์ˆ˜์ค€ ํ—ˆ์šฉ ๋ชฉ๋ก์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—… ๋ชฉ๋ก์€ ๋ณด์•ˆ ๋˜๋Š” ๊ทœ์ • ์ค€์ˆ˜ ํŒ€์—์„œ ๊ฒ€ํ† ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ๋Š” ํฌ๊ด„์ ์ธ ์ž‘์—… ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

์ˆ˜๋™ ์ž‘์—…

"์ˆ˜๋™ ์ž‘์—…" ์„น์…˜์—๋Š” GitHub Actions Importer์ด(๊ฐ€) ์ž๋™์œผ๋กœ ์™„๋ฃŒํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์ˆ˜๋™์œผ๋กœ ์™„๋ฃŒํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์— ๋Œ€ํ•œ ๊ฐœ์š”๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ "ํŒŒ์ดํ”„๋ผ์ธ" ์„น์…˜์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ์šฉ์–ด๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋น„๋ฐ€์€ ๋ณ€ํ™˜๋œ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋˜๋Š” ์กฐ์ง ์ˆ˜์ค€์˜ ๋น„๋ฐ€์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒŒ์ดํ”„๋ผ์ธ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋ ค๋ฉด GitHub Actions์—์„œ ์ด๋Ÿฌํ•œ ๋น„๋ฐ€์„ ์ˆ˜๋™์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ GitHub Actions์—์„œ ๋น„๋ฐ€ ์‚ฌ์šฉ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.
  • ์ž์ฒด ํ˜ธ์ŠคํŒ… ์‹คํ–‰๊ธฐ๋Š” GitHubํ˜ธ์Šคํ‹ฐ๋“œ ๋Ÿฌ๋„ˆ๊ฐ€ ์•„๋‹Œ ๋ณ€ํ™˜๋œ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์ฐธ์กฐ๋˜๋Š” ์‹คํ–‰๊ธฐ์˜ ๋ ˆ์ด๋ธ”์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ œ๋Œ€๋กœ ์ž‘๋™์‹œํ‚ค๋ ค๋ฉด ์ด๋Ÿฌํ•œ ์‹คํ–‰๊ธฐ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Files

๊ฐ์‚ฌ ๋ณด๊ณ ์„œ์˜ ๋งˆ์ง€๋ง‰ ์„น์…˜์—๋Š” ๊ฐ์‚ฌ ์ค‘ ๋””์Šคํฌ์— ๊ธฐ๋ก๋œ ๋ชจ๋“  ํŒŒ์ผ์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ฐ ํŒŒ์ดํ”„๋ผ์ธ ํŒŒ์ผ์—๋Š” ๋‹ค์Œ์„ ํฌํ•จํ•˜๋ฉฐ, ๊ฐ์‚ฌ์— ํฌํ•จ๋œ ๋‹ค์–‘ํ•œ ํŒŒ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • GitHub์— ์ •์˜๋œ ์›๋ž˜ ํŒŒ์ดํ”„๋ผ์ธ์ž…๋‹ˆ๋‹ค.
  • ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ๋„คํŠธ์›Œํฌ ์‘๋‹ต์ž…๋‹ˆ๋‹ค.
  • ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.
  • ์‹คํŒจํ•œ ํŒŒ์ดํ”„๋ผ์ธ ๋ณ€ํ™˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์Šคํƒ ์ถ”์ ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ workflow_usage.csv ํŒŒ์ผ์—๋Š” ์„ฑ๊ณต์ ์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ฐ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ์ž‘์—…, ๋น„๋ฐ€ ๋ฐ ์‹คํ–‰๊ธฐ์˜ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ๋ชฉ๋ก์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ž‘์—…, ๋น„๋ฐ€ ๋˜๋Š” ์‹คํ–‰๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์›Œํฌํ”Œ๋กœ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณด์•ˆ ๊ฒ€ํ† ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž ์žฌ์ ์ธ GitHub Actions ์‚ฌ์šฉ๋Ÿ‰ ์˜ˆ์ธก

forecast ๋ช…๋ น์œผ๋กœ Azure DevOps์—์„œ ์™„๋ฃŒ๋œ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰์˜ ๋ฉ”ํŠธ๋ฆญ์„ ๊ณ„์‚ฐํ•˜์—ฌ ์ž ์žฌ์ ์ธ GitHub Actions ์‚ฌ์šฉ๋Ÿ‰์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ธก ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

์ž ์žฌ์ ์ธ GitHub Actions ์‚ฌ์šฉ๋Ÿ‰์„ ์˜ˆ์ธกํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ GitHub Actions Importer์€(๋Š”) ์˜ˆ์ธก ๋ณด๊ณ ์„œ์— ์ด์ „ 7์ผ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

gh actions-importer forecast azure-devops --output-dir tmp/forecast_reports

์˜ˆ์ธก ๋ณด๊ณ ์„œ ๊ฒ€์‚ฌํ•˜๊ธฐ

์ง€์ •๋œ ์ถœ๋ ฅ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ forecast_report.md ํŒŒ์ผ์— ์˜ˆ์ธก ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์˜ˆ์ƒ ๋ณด๊ณ ์„œ์— ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ์šฉ์–ด์ž…๋‹ˆ๋‹ค:

  • ์ž‘์—… ์ˆ˜๋Š” ์™„๋ฃŒ๋œ ์ž‘์—…์˜ ์ด ๊ฐœ์ˆ˜์ž…๋‹ˆ๋‹ค.

  • ํŒŒ์ดํ”„๋ผ์ธ ์ˆ˜๋Š” ์‚ฌ์šฉ๋œ ๊ณ ์œ  ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ˆ˜์ž…๋‹ˆ๋‹ค.

  • ์‹คํ–‰ ์‹œ๊ฐ„: ์€ ๋Ÿฌ๋„ˆ๊ฐ€ ์ž‘์—…์— ์†Œ๋น„ํ•œ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด ๋ฉ”ํŠธ๋ฆญ์„ ์‚ฌ์šฉํ•˜์—ฌ GitHubํ˜ธ์Šคํ‹ฐ๋“œ ์‹คํ–‰๊ธฐ์˜ ๋น„์šฉ์„ ๊ณ„ํšํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์ด ๋ฉ”ํŠธ๋ฆญ์€ GitHub Actions์— ์ง€์ถœํ•ด์•ผ ํ•˜๋Š” ๊ธˆ์•ก๊ณผ ์ƒ๊ด€ ๊ด€๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธˆ์•ก์€ ์ž‘์—… ์‹œ๊ฐ„(๋ถ„) ๋™์•ˆ ์‚ฌ์šฉ๋˜๋Š” ํ•˜๋“œ์›จ์–ด์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. GitHub Actions ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์šฉ์„ ์ถ”์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ ์‹œ๊ฐ„: ๋ฉ”ํŠธ๋ฆญ์€ ์ž‘์—…์ด ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋Ÿฌ๋„ˆ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐ ์†Œ์š”๋˜๋Š” ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

  • ๋™์‹œ ์ž‘์—… ๋ฉ”ํŠธ๋ฆญ์€ ํŠน์ • ์‹œ๊ฐ„์— ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์˜ ์–‘์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”ํŠธ๋ฆญ์€ ๊ตฌ์„ฑํ•ด์•ผ ํ•˜๋Š” ๋Ÿฌ๋„ˆ ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ด๋Ÿฌํ•œ ๋ฉ”ํŠธ๋ฆญ์€ Azure DevOps์˜ ๊ฐ ์‹คํ–‰๊ธฐ ํ์— ๋”ฐ๋ผ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ํ˜ธ์ŠคํŠธํ˜• ๋˜๋Š” ์ž์ฒด ํ˜ธ์ŠคํŒ…ํ˜• ๋Ÿฐ๋„ˆ ๋˜๋Š” ๊ณ ์‚ฌ์–‘ ๋˜๋Š” ์ €์‚ฌ์–‘ ์ปดํ“จํ„ฐ๊ฐ€ ํ˜ผํ•ฉ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ํŠนํžˆ ์œ ์šฉํ•˜๋ฏ€๋กœ ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ๋Ÿฐ๋„ˆ์— ํŠน์ •ํ•œ ๋ฉ”ํŠธ๋ฆญ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œํ—˜ ์‹คํ–‰ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ˆ˜ํ–‰

dry-run ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์„ ํ•ด๋‹น GitHub Actions ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋“œ๋ผ์ด๋Ÿฐ์€ ์ง€์ •๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ถœ๋ ฅ ํŒŒ์ผ์„ ๋งŒ๋“ค์ง€๋งŒ ํŒŒ์ดํ”„๋ผ์ธ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์œ„ํ•œ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์—ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

GitHub Actions Importer์ด(๊ฐ€) ์•Œ ์ˆ˜ ์—†๋Š” ๋นŒ๋“œ ๋‹จ๊ณ„ ๋˜๋Š” ๋ถ€๋ถ„์ ์œผ๋กœ ์„ฑ๊ณตํ•œ ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ๊ฐ™์ด ์ž๋™์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†๋Š” ํ•ญ๋ชฉ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ณ€ํ™˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€ํ™˜๊ธฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€ํ™˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GitHub Actions ๊ฐ€์ ธ์˜ค๊ธฐ ํ™•์žฅ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์— ๋Œ€ํ•œ ๋“œ๋ผ์ด ๋Ÿฐ ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

Azure DevOps ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋“œ๋ผ์ด ๋Ÿฐ์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ pipeline_id์„(๋ฅผ) ๋ณ€ํ™˜ํ•˜๋ ค๋Š” ํŒŒ์ดํ”„๋ผ์ธ์˜ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

gh actions-importer dry-run azure-devops pipeline --pipeline-id :pipeline_id --output-dir tmp/dry-run

์ง€์ •๋œ ์ถœ๋ ฅ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ๋“œ๋ผ์ด๋Ÿฐ ๋กœ๊ทธ ๋ฐ ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆด๋ฆฌ์Šค ํŒŒ์ดํ”„๋ผ์ธ์— ๋Œ€ํ•œ ๋“œ๋ผ์ด ๋Ÿฐ ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

Azure DevOps ๋ฆด๋ฆฌ์Šค ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋“œ๋ผ์ด ๋Ÿฐ์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ pipeline_id์„(๋ฅผ) ๋ณ€ํ™˜ํ•˜๋ ค๋Š” ํŒŒ์ดํ”„๋ผ์ธ์˜ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

gh actions-importer dry-run azure-devops release --pipeline-id :pipeline_id --output-dir tmp/dry-run

์ง€์ •๋œ ์ถœ๋ ฅ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ๋“œ๋ผ์ด๋Ÿฐ ๋กœ๊ทธ ๋ฐ ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์‚ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ˆ˜ํ–‰

migrate ๋ช…๋ น์œผ๋กœ Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ€ํ™˜ํ•˜๊ณ  ํ•ด๋‹นํ•˜๋Š” GitHub Actions ์›Œํฌํ”Œ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์— ๋Œ€ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

Azure DevOps ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ target-url ๊ฐ’์„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ URL๋กœ ๋ฐ”๊พธ๊ณ , pipeline_id์„(๋ฅผ) ๋ณ€ํ™˜ํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์˜ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

gh actions-importer migrate azure-devops pipeline --pipeline-id :pipeline_id --target-url https://github.com/octo-org/octo-repo --output-dir tmp/migrate

๋ช…๋ น์˜ ์ถœ๋ ฅ์—๋Š” ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ๋ฅผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•˜๋Š” ํ’€ ๋ฆฌํ€˜์ŠคํŠธ์˜ URL์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ์ถœ๋ ฅ์˜ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค:

$ gh actions-importer migrate azure-devops pipeline --target-url https://github.com/octo-org/octo-repo --output-dir tmp/migrate --azure-devops-project my-azure-devops-project
[2022-08-20 22:08:20] Logs: 'tmp/migrate/log/actions-importer-20220916-014033.log'
[2022-08-20 22:08:20] Pull request: 'https://github.com/octo-org/octo-repo/pull/1'

๋ฆด๋ฆฌ์Šค ํŒŒ์ดํ”„๋ผ์ธ์— ๋Œ€ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

Azure DevOps ๋ฆด๋ฆฌ์Šค ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions(์œผ)๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ target-url ๊ฐ’์„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ URL๋กœ ๋ฐ”๊พธ๊ณ , pipeline_id์„(๋ฅผ) ๋ณ€ํ™˜ํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์˜ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

gh actions-importer migrate azure-devops release --pipeline-id :pipeline_id --target-url https://github.com/octo-org/octo-repo --output-dir tmp/migrate

๋ช…๋ น์˜ ์ถœ๋ ฅ์—๋Š” ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ๋ฅผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•˜๋Š” ํ’€ ๋ฆฌํ€˜์ŠคํŠธ์˜ URL์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ์ถœ๋ ฅ์˜ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค:

$ gh actions-importer migrate azure-devops release --target-url https://github.com/octo-org/octo-repo --output-dir tmp/migrate --azure-devops-project my-azure-devops-project
[2022-08-20 22:08:20] Logs: 'tmp/migrate/log/actions-importer-20220916-014033.log'
[2022-08-20 22:08:20] Pull request: 'https://github.com/octo-org/octo-repo/pull/1'

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ๊ฒ€์‚ฌํ•˜๊ธฐ

migrate ๋ช…๋ น์˜ ์„ฑ๊ณต์ ์ธ ์‹คํ–‰์˜ ์ถœ๋ ฅ์—๋Š” ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ๋ฅผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•˜๋Š” ์ƒˆ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์— ๋Œ€ํ•œ ๋งํฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์˜ ๋ช‡ ๊ฐ€์ง€ ์ค‘์š” ์š”์†Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ์„ค๋ช…์—์„œ ์ˆ˜๋™์œผ๋กœ ์™„๋ฃŒํ•ด์•ผ ํ•˜๋Š” ๋‹จ๊ณ„๋ฅผ ๋‚˜์—ดํ•˜๋Š” ์„น์…˜์€ ํŒŒ์ดํ”„๋ผ์ธ์„ GitHub Actions๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด ์„น์…˜์—์„œ๋Š” ์›Œํฌํ”Œ๋กœ์— ์‚ฌ์šฉ๋˜๋Š” ๋น„๋ฐ€์„ ๋งŒ๋“ค๋„๋ก ์ง€์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์—์„œ Files changed ํƒญ์„ ์„ ํƒํ•˜์—ฌ GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ถ”๊ฐ€๋  ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์„ ๋ด…๋‹ˆ๋‹ค.

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ๊ฒ€์‚ฌ๋ฅผ ๋งˆ์น˜๋ฉด ๋ณ‘ํ•ฉํ•˜์—ฌ ์›Œํฌํ”Œ๋กœ๋ฅผ GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ์กฐ

์ด ์„น์…˜์—๋Š” GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋•Œ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ์„ ํƒํ˜• ์ธ์ˆ˜, ์ง€์›๋˜๋Š” ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ ์ฐธ์กฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ํ™˜๊ฒฝ ๋ณ€์ˆ˜

GitHub Actions Importer์€(๋Š”) ์ธ์ฆ ๊ตฌ์„ฑ์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ณ€์ˆ˜๋Š” configure ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋”ฐ๋ฅด๋ฉด ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์ •๋ณด๋Š” ์ž๊ฒฉ ์ฆ๋ช… ๊ตฌ์„ฑ ์„น์…˜์„ ์ฐธ์กฐํ•˜์„ธ์š”.

GitHub Actions Importer์€(๋Š”) ๋‹ค์Œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps ์ธ์Šคํ„ด์Šค์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

  • GITHUB_ACCESS_TOKEN: ๋ณ€ํ™˜๋œ ์›Œํฌํ”Œ๋กœ(workflow ๋ฒ”์œ„ ํ•„์š”)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” personal access token (classic)์ž…๋‹ˆ๋‹ค.
  • GITHUB_INSTANCE_URL: ๋Œ€์ƒ GitHub ์ธ์Šคํ„ด์Šค์˜ URL์ž…๋‹ˆ๋‹ค(์˜ˆ: https://github.com).
  • AZURE_DEVOPS_ACCESS_TOKEN: Azure DevOps ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์ฆํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” personal access token์ž…๋‹ˆ๋‹ค. ์ด ํ† ํฐ์—๋Š” ๋‹ค์Œ ๋ฒ”์œ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ๋นŒ๋“œ: Read
    • ์—์ด์ „ํŠธ ํ’€: Read
    • ์ฝ”๋“œ: Read
    • ๋ฆด๋ฆฌ์Šค: Read
    • ์„œ๋น„์Šค ์—ฐ๊ฒฐ: Read
    • ์ž‘์—… ๊ทธ๋ฃน: Read
    • ๋ณ€์ˆ˜ ๊ทธ๋ฃน: Read
  • AZURE_DEVOPS_PROJECT: ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋•Œ ์‚ฌ์šฉํ•  ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ๋˜๋Š” GUID์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด ๊ฐ์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์ด๋Š” ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.
  • AZURE_DEVOPS_ORGANIZATION: Azure DevOps ์ธ์Šคํ„ด์Šค์Šค์˜ ์กฐ์ง ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • AZURE_DEVOPS_INSTANCE_URL: Azure DevOps ์ธ์Šคํ„ด์Šค์˜ URL(์˜ˆ: https://dev.azure.com)์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” .env.local ํŒŒ์ผ์— ์ง€์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ํŒŒ์ผ์€ ์‹คํ–‰ ์‹œ GitHub Actions Importer์— ์˜ํ•ด ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

์„ ํƒํ˜• ์ธ์ˆ˜

GitHub Actions Importer ํ•˜์œ„ ๋ช…๋ น๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‚ฌ์šฉ์ž ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์„ ํƒ์  ์ธ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

--source-file-path

--source-file-path ์ธ์ˆ˜๋ฅผ forecast, dry-run ๋˜๋Š” migrate ํ•˜์œ„ ๋ช…๋ น๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ GitHub Actions Importer์€(๋Š”) ์†Œ์Šค ์ œ์–ด์—์„œ ํŒŒ์ดํ”„๋ผ์ธ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. --source-file-path ์ธ์ˆ˜๋Š” GitHub Actions Importer์— ์ง€์ •๋œ ์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

gh actions-importer dry-run azure-devops pipeline --output-dir ./output/ --source-file-path ./path/to/azure_devops/pipeline.yml

--config-file-path

--config-file-path ์ธ์ˆ˜๋ฅผ audit, dry-run ๋ฐ migrate ํ•˜์œ„ ๋ช…๋ น๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ GitHub Actions Importer์€(๋Š”) ์†Œ์Šค ์ œ์–ด์—์„œ ํŒŒ์ดํ”„๋ผ์ธ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. --config-file-path ์ธ์ˆ˜๋Š” GitHub Actions Importer์— ์ง€์ •๋œ ์†Œ์Šค ํŒŒ์ผ์„ ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.

--config-file-path ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€ํ™˜๋˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ ๋˜๋Š” ๋ณตํ•ฉ ์ž‘์—…์„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ์˜ˆ

์ด ์˜ˆ์‹œ์—์„œ GitHub Actions Importer์€(๋Š”) ์ง€์ •๋œ YAML ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์†Œ์Šค ํŒŒ์ผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

gh actions-importer audit azure-devops pipeline --output-dir ./output/ --config-file-path ./path/to/azure_devops/config.yml

๊ตฌ์„ฑ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ์‚ฌํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ ํŒŒ์ผ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์ด์–ด์•ผ ํ•˜๋ฉฐ, ๊ฐ repository_slug ๊ฐ’์€ ๊ณ ์œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

source_files:
  - repository_slug: azdo-project/1
    path: file.yml
  - repository_slug: azdo-project/2
    paths: path.yml

Azure DevOps ์กฐ์ง ์ด๋ฆ„, ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ๋ฐ ํŒŒ์ดํ”„๋ผ์ธ ID๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ํŒŒ์ดํ”„๋ผ์ธ์— ๋Œ€ํ•ด repository_slug์„(๋ฅผ) ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋“ค ๋“ค์–ด my-organization-name/my-project-name/42์ž…๋‹ˆ๋‹ค.

๋“œ๋ผ์ด ์‹คํ–‰ ์˜ˆ

์ด ์˜ˆ์ œ์—์„œ GitHub Actions Importer๋Š” ์ง€์ •๋œ YAML ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์†Œ์Šค ํŒŒ์ผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋“œ๋ผ์ด๋Ÿฐ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ดํ”„๋ผ์ธ์€ ๊ตฌ์„ฑ ํŒŒ์ผ์˜ repository_slug์„(๋ฅผ) --azure-devops-organization ๋ฐ --azure-devops-project ์˜ต์…˜ ๊ฐ’๊ณผ ์ผ์น˜์‹œ์ผœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ path์€ ์ง€์ •๋œ ์†Œ์Šค ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

gh actions-importer dry-run azure-devops pipeline --output-dir ./output/ --config-file-path ./path/to/azure_devops/config.yml
๋ณ€ํ™˜๋˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ ๋ฐ ๋ณตํ•ฉ ์ž‘์—…์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ง€์ •

GitHub Actions Importer์€(๋Š”) --config-file-path ์ธ์ˆ˜์— ์ œ๊ณต๋œ YAML ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€ํ™˜๋˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ์™€ ๋ณตํ•ฉ ์ž‘์—…์ด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๋ ค๋ฉด --config-file-path ์ธ์ˆ˜ ์—†์ด ๊ฐ์‚ฌ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

gh actions-importer audit azure-devops --output-dir ./output/

์ด ๋ช…๋ น์˜ ์ถœ๋ ฅ์—๋Š” GitHub Actions Importer์— ์˜ํ•ด ๋ณ€ํ™˜๋œ ๋ชจ๋“  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ ๋ฐ ๋ณตํ•ฉ ์ž‘์—…์˜ ๋ชฉ๋ก์„ ํฌํ•จํ•œ config.yml(์ด)๋ผ๋Š” ์ด๋ฆ„์˜ ํŒŒ์ผ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด config.yml ํŒŒ์ผ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‚ด์šฉ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

reusable_workflows:
  - name: my-reusable-workflow.yml
    target_url: https://github.com/octo-org/octo-repo
    ref: main

composite_actions:
  - name: my-composite-action.yml
    target_url: https://github.com/octo-org/octo-repo
    ref: main

์ด ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ ๋˜๋Š” ๋ณตํ•ฉ ์ž‘์—…์„ ์ถ”๊ฐ€ํ•  ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฐ ์ฐธ์กฐ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ --config-file-path ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ config.yml ํŒŒ์ผ์„ GitHub Actions Importer์— ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด migrate ๋ช…๋ น์„ ์‹คํ–‰ํ•  ๋•Œ ์ด ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ํŒŒ์ผ์— ์ •์˜๋œ ๊ฐ ๊ณ ์œ  ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋Œ€ํ•œ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

gh actions-importer migrate azure-devops pipeline --config-file-path config.yml --target-url https://github.com/my-org/my-repo

Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์— ์ง€์›๋˜๋Š” ๊ตฌ๋ฌธ

๋‹ค์Œ ํ‘œ์—๋Š” GitHub Actions Importer๊ฐ€ ํ˜„์žฌ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ ์œ ํ˜•์ด ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

Azure PipelinesGitHub Actions์ƒํƒœ
condition
  • jobs.<job_id>.if
  • jobs.<job_id>.steps[*].if
์ง€์› ์—ฌ๋ถ€
์ปจํ…Œ์ด๋„ˆ
  • jobs.<job_id>.container
  • jobs.<job_id>.name
์ง€์› ์—ฌ๋ถ€
continuousIntegration
  • on.<push>.<branches>
  • on.<push>.<tags>
  • on.<push>.paths
์ง€์› ์—ฌ๋ถ€
์ž‘์—…(job)
  • jobs.<job_id>
์ง€์› ์—ฌ๋ถ€
pullRequest
  • on.<pull_request>.<branches>
  • on.<pull_request>.paths
์ง€์› ์—ฌ๋ถ€
stage(๋‹จ๊ณ„)
  • jobs
์ง€์› ์—ฌ๋ถ€
steps
  • jobs.<job_id>.steps
์ง€์› ์—ฌ๋ถ€
์ „๋žต
  • jobs.<job_id>.strategy.fail-fast
  • jobs.<job_id>.strategy.max-parallel
  • jobs.<job_id>.strategy.matrix
์ง€์› ์—ฌ๋ถ€
timeoutInMinutes
  • jobs.<job_id>.timeout-minutes
์ง€์› ์—ฌ๋ถ€
variables
  • env
  • jobs.<job_id>.env
  • jobs.<job_id>.steps.env
์ง€์› ์—ฌ๋ถ€
์ˆ˜๋™ ๋ฐฐํฌ
  • jobs.<job_id>.environment
๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
ํ’€
  • runners
  • self hosted runners
๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
services
  • jobs.<job_id>.services
๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
์ „๋žต
  • jobs.<job_id>.strategy
๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
ํŠธ๋ฆฌ๊ฑฐ
  • on
๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
pullRequest
  • on.<pull_request>.<tags>
์ง€์›๋˜์ง€ ์•Š์Œ
์Šค์ผ€์ค„
  • on.schedule
  • on.workflow_run
์ง€์›๋˜์ง€ ์•Š์Œ
ํŠธ๋ฆฌ๊ฑฐ
  • on.<event_name>.types
์ง€์›๋˜์ง€ ์•Š์Œ

์ง€์›๋˜๋Š” Azure DevOps ์ž‘์—…์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ github/gh-actions-importer ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋งคํ•‘

GitHub Actions Importer์€(๋Š”) ์•„๋ž˜ ํ‘œ์˜ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ Azure DevOps ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ GitHub Actions ๋‚ด์˜ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Azure PipelinesGitHub Actions
$(Agent.BuildDirectory)${{ runner.workspace }}
$(Agent.HomeDirectory)${{ env.HOME }}
$(Agent.JobName)${{ github.job }}
$(Agent.OS)${{ runner.os }}
$(Agent.ReleaseDirectory)${{ github.workspace}}
$(Agent.RootDirectory)${{ github.workspace }}
$(Agent.ToolsDirectory)${{ runner.tool_cache }}
$(Agent.WorkFolder)${{ github.workspace }}
$(Build.ArtifactStagingDirectory)${{ runner.temp }}
$(Build.BinariesDirectory)${{ github.workspace }}
$(Build.BuildId)${{ github.run_id }}
$(Build.BuildNumber)${{ github.run_number }}
$(Build.DefinitionId)${{ github.workflow }}
$(Build.DefinitionName)${{ github.workflow }}
$(Build.PullRequest.TargetBranch)${{ github.base_ref }}
$(Build.PullRequest.TargetBranch.Name)${{ github.base_ref }}
$(Build.QueuedBy)${{ github.actor }}
$(Build.Reason)${{ github.event_name }}
$(Build.Repository.LocalPath)${{ github.workspace }}
$(Build.Repository.Name)${{ github.repository }}
$(Build.Repository.Provider)GitHub
$(Build.Repository.Uri)${{ github.server.url }}/${{ github.repository }}
$(Build.RequestedFor)${{ github.actor }}
$(Build.SourceBranch)${{ github.ref }}
$(Build.SourceBranchName)${{ github.ref }}
$(Build.SourceVersion)${{ github.sha }}
$(Build.SourcesDirectory)${{ github.workspace }}
$(Build.StagingDirectory)${{ runner.temp }}
$(Pipeline.Workspace)${{ runner.workspace }}
$(Release.DefinitionEnvironmentId)${{ github.job }}
$(Release.DefinitionId)${{ github.workflow }}
$(Release.DefinitionName)${{ github.workflow }}
$(Release.Deployment.RequestedFor)${{ github.actor }}
$(Release.DeploymentID)${{ github.run_id }}
$(Release.EnvironmentId)${{ github.job }}
$(Release.EnvironmentName)${{ github.job }}
$(Release.Reason)${{ github.event_name }}
$(Release.RequestedFor)${{ github.actor }}
$(System.ArtifactsDirectory)${{ github.workspace }}
$(System.DefaultWorkingDirectory)${{ github.workspace }}
$(System.HostType)build
$(System.JobId)${{ github.job }}
$(System.JobName)${{ github.job }}
$(System.PullRequest.PullRequestId)${{ github.event.number }}
$(System.PullRequest.PullRequestNumber)${{ github.event.number }}
$(System.PullRequest.SourceBranch)${{ github.ref }}
$(System.PullRequest.SourceRepositoryUri)${{ github.server.url }}/${{ github.repository }}
$(System.PullRequest.TargetBranch)${{ github.event.base.ref }}
$(System.PullRequest.TargetBranchName)${{ github.event.base.ref }}
$(System.StageAttempt)${{ github.run_number }}
$(System.TeamFoundationCollectionUri)${{ github.server.url }}/${{ github.repository }}
$(System.WorkFolder)${{ github.workspace }}

ํ…œํ”Œ๋ฆฟ

GitHub Actions Importer์„(๋ฅผ) ์‚ฌ์šฉํ•˜์—ฌ Azure DevOps ํ…œํ”Œ๋ฆฟ์„ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œํ•œ ์‚ฌํ•ญ

GitHub Actions Importer์€(๋Š”) ๋ช‡ ๊ฐ€์ง€ ์ œํ•œ ํ•˜์— Azure DevOps ํ…œํ”Œ๋ฆฟ์„ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • stages ๋ฐ deployments์—์„œ ์‚ฌ์šฉ๋˜๋Š” Azure DevOps ํ…œํ”Œ๋ฆฟ๊ณผ jobs ํ‚ค๋Š” GitHub Actions์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›Œํฌํ”Œ๋กœ ๋‹ค์‹œ ์‚ฌ์šฉ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.
  • steps ํ‚ค์—์„œ ์‚ฌ์šฉ๋˜๋Š” Azure DevOps ํ…œํ”Œ๋ฆฟ์€ ๋ณตํ•ฉ ์ž‘์—…์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ณตํ•ฉ ์ž‘์—… ๋งŒ๋“ค๊ธฐ์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.
  • ํ˜„์žฌ ๋‹ค๋ฅธ ์ž‘์—… ํ…œํ”Œ๋ฆฟ์„ ์ฐธ์กฐํ•˜๋Š” ์ž‘์—… ํ…œํ”Œ๋ฆฟ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, GitHub Actions Importer์€(๋Š”) ํ…œํ”Œ๋ฆฟ์„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋Š” ๋‹ค๋ฅธ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ์ด ๊ตฌ๋ฌธ์€ GitHub Actions์—์„œ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ค‘์ฒฉ๋œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋Š” ์ˆ˜๋™์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ…œํ”Œ๋ฆฟ์ด ์™ธ๋ถ€ Azure DevOps ์กฐ์ง ๋˜๋Š” GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ํ…œํ”Œ๋ฆฟ์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ œ๊ณตํ•˜๋Š” --credentials-file ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ถ”๊ฐ€ ์ธ์ˆ˜ ๋ฐ ์„ค์ •์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.
  • ๋‹ค์Œ ์ฃผ์˜ ์‚ฌํ•ญ๊ณผ ํ•จ๊ป˜ each ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ YAML์„ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ค‘์ฒฉ๋œ each ๋ธ”๋ก์€ ์ง€์›๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์œ„ each ๋ธ”๋ก๋„ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • each ๋ฐ ํฌํ•จ๋œ if ์กฐ๊ฑด์€ GitHub Actions์ด(๊ฐ€) ์ด ์‚ฝ์ž… ์Šคํƒ€์ผ์„ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ณ€ํ™˜ ์‹œ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.
    • elseif ๋ธ”๋ก์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ˆ˜๋™์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ค‘์ฒฉ๋œ if ๋ธ”๋ก์€ ์ง€์›๋˜์ง€๋งŒ if ์กฐ๊ฑด ํ•˜์—์„œ ์ค‘์ฒฉ๋œ if/elseif/else ๋ธ”๋ก์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ๋ฏธ๋ฆฌ ์ •์˜๋œ Azure DevOps ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” if ๋ธ”๋ก์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ง€์›๋˜๋Š” ํ…œํ”Œ๋ฆฟ

GitHub Actions Importer์€(๋Š”) ์•„๋ž˜ ํ‘œ์— ๊ธฐ์žฌ๋œ ํ…œํ”Œ๋ฆฟ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Azure PipelinesGitHub Actions์ƒํƒœ
ํ…œํ”Œ๋ฆฟ์—์„œ ํ™•์žฅํ•˜๊ธฐReusable workflow์ง€์› ์—ฌ๋ถ€
์ž‘์—… ํ…œํ”Œ๋ฆฟReusable workflow์ง€์› ์—ฌ๋ถ€
์Šคํ…Œ์ด์ง€ ํ…œํ”Œ๋ฆฟReusable workflow์ง€์› ์—ฌ๋ถ€
๋‹จ๊ณ„ ํ…œํ”Œ๋ฆฟComposite action์ง€์› ์—ฌ๋ถ€
ํด๋ž˜์‹ ํŽธ์ง‘๊ธฐ ๋‚ด ์ž‘์—… ๊ทธ๋ฃน์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„์ง€์› ์—ฌ๋ถ€
๋‹ค๋ฅธ Azure DevOps ์กฐ์ง, ํ”„๋กœ์ ํŠธ ๋˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ํ…œํ”Œ๋ฆฟ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„์ง€์› ์—ฌ๋ถ€
GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ํ…œํ”Œ๋ฆฟ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„์ง€์› ์—ฌ๋ถ€
๋ณ€์ˆ˜ ํ…œํ”Œ๋ฆฟenv์ง€์› ์—ฌ๋ถ€
์กฐ๊ฑด๋ถ€ ์‚ฝ์ž…์ž‘์—…/๋‹จ๊ณ„์˜ if ์กฐ๊ฑด๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
๋ฐ˜๋ณต์  ์‚ฝ์ž…ํ•ด๋‹น ์—†์Œ๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” ํ…œํ”Œ๋ฆฟ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ

ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ ๊ฒฝ๋กœ ์ด๋ฆ„

GitHub Actions Importer์€(๋Š”) ํŒŒ์ผ ์ด๋ฆ„์— ๋ณ€์ˆ˜, ๋งค๊ฐœ ๋ณ€์ˆ˜ ๋ฐ ๋ฐ˜๋ณต ์‹์ด ์žˆ๋Š” ์ƒ๋Œ€ ๋˜๋Š” ๋™์  ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ์žˆ๋Š” ํ…œํ”Œ๋ฆฟ์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ๋ณธ๊ฐ’ ์ง‘ํ•ฉ์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ณ€์ˆ˜ ํŒŒ์ผ ๊ฒฝ๋กœ ์ด๋ฆ„ ์˜ˆ์‹œ
# File: azure-pipelines.yml
variables:
- template: 'templates/vars.yml'

steps:
- template: "./templates/$"
# File: templates/vars.yml
variables:
  one: 'simple_step.yml'
๋งค๊ฐœ ๋ณ€์ˆ˜ ํŒŒ์ผ ๊ฒฝ๋กœ ์ด๋ฆ„ ์˜ˆ์‹œ
parameters:
- name: template
  type: string
  default: simple_step.yml

steps:
- template: "./templates/${{ parameters.template }}"
๋ฐ˜๋ณต ํŒŒ์ผ ๊ฒฝ๋กœ ์ด๋ฆ„ ์˜ˆ์‹œ
parameters:
- name: steps
  type: object
  default:
  - build_step
  - release_step
steps:
- ${{ each step in parameters.steps }}:
    - template: "$-variables.yml"

ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ ๋ณ€์ˆ˜

GitHub Actions Importer์€(๋Š”) ์•„๋ž˜ ํ‘œ์— ๊ธฐ์žฌ๋œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Azure PipelinesGitHub Actions์ƒํƒœ
stringinputs.string์ง€์› ์—ฌ๋ถ€
๋ฒˆํ˜ธinputs.number์ง€์› ์—ฌ๋ถ€
๋ถ€์šธ ๊ฐ’inputs.boolean์ง€์› ์—ฌ๋ถ€
๊ฐœ์ฒดfromJSON ์‹์ด ์žˆ๋Š” inputs.string๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
stepstep๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
stepListstep๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
์ž‘์—…(job)job๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
jobListjob๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
ํ™•์ธjob๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
deploymentListjob๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
stage(๋‹จ๊ณ„)job๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ
stageListjob๋ถ€๋ถ„์ ์œผ๋กœ ์ง€์›๋จ

์ฐธ๊ณ  ํ•ญ๋ชฉ

์ด ๋งค๊ฐœ ๋ณ€์ˆ˜ ์œ ํ˜•์„ ํฌํ•จํ•˜๋ฉฐ step ํ‚ค์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ…œํ”Œ๋ฆฟ์€ ํ…œํ”Œ๋ฆฟ ๋‹จ๊ณ„์˜ ์‹œ์ž‘ ๋˜๋Š” ๋์—์„œ ๋‹จ๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋ณตํ•ฉ ์ž‘์—…์œผ๋กœ ์ง๋ ฌํ™”๋ฉ๋‹ˆ๋‹ค. ์ด ๋งค๊ฐœ ๋ณ€์ˆ˜ ์œ ํ˜•์„ ํฌํ•จํ•˜๋ฉฐ stage, deployment, job ํ‚ค์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ…œํ”Œ๋ฆฟ์€ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š๊ณ  ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์›Œํฌํ”Œ๋กœ๋กœ ์ง๋ ฌํ™”๋ฉ๋‹ˆ๋‹ค.

๋ถ€๋ถ„์€ MIT ๋ผ์ด์„ ์Šค์— ๋”ฐ๋ผ https://github.com/github/gh-actions-importer/์—์„œ ์กฐ์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

MIT License

Copyright (c) 2022 GitHub

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.