다음을 통해 공유


Android 앱 빌드, 테스트 및 배포

Azure DevOps Services

Android 애플리케이션을 자동으로 빌드, 테스트 및 배포하도록 파이프라인을 설정할 수 있습니다.

필수 조건

다음 항목이 있어야 합니다.

파이프라인 설정

샘플 Android 애플리케이션에 대한 파이프라인을 설정하려면 다음 작업을 수행합니다.

  1. 간단한 Android 애플리케이션에 대한 코드를 가져오려면 다음 리포지토리를 GitHub 계정에 포크합니다.

    https://github.com/MicrosoftDocs/pipelines-android
    
  2. Azure DevOps 조직에 로그인하고, 프로젝트로 이동합니다.

  3. 파이프라인>만들기 파이프라인 또는 새 파이프라인을 선택합니다.

  4. 소스 코드의 위치로 GitHub를 선택합니다.

    Screenshot showing list of repositories to select from.

    로그인할 GitHub로 리디렉션될 수 있습니다. 그렇다면 GitHub 자격 증명을 입력합니다.

  5. 이전에 포크한 -android 리포지토리를 선택합니다.

  6. 승인을 선택하고 다음 화면에 설치 합니다.

    Azure Pipelines는 파이프라인에 대한 YAML 파일을 생성합니다.

  7. 실행을 선택합니다.

  8. 기본 분기에 직접 커밋한 다음 실행을 다시 선택합니다.

  9. 실행이 끝날 때까지 기다립니다.

리포지토리에 사용자 지정할 준비가 된 YAML 파일(azure-pipelines.yml)이 있습니다.

YAML 파일을 변경하려면 파이프라인 페이지에서 파이프라인을 선택한 다음 파일을 편집합니다azure-pipelines.yml.

Gradle을 사용하여 빌드

Gradle은 Android 프로젝트를 빌드하는 데 사용되는 일반적인 빌드 도구입니다. 옵션에 대한 자세한 내용은 Gradle 작업을 참조하세요.

# https://learn.microsoft.com/azure/devops/pipelines/ecosystems/android
pool:
  vmImage: 'macOS-latest'

steps:
- task: Gradle@2
  inputs:
    workingDirectory: ''
    gradleWrapperFile: 'gradlew'
    gradleOptions: '-Xmx3072m'
    publishJUnitResults: false
    testResultsFiles: '**/TEST-*.xml'
    tasks: 'assembleDebug'

빌드 경로 조정

  • 파일이 리포지토리의 루트에 없는 경우 gradlew workingDirectory 값을 조정합니다. 디렉터리 값은 리포지토리의 루트(예: AndroidApps/MyApp 또는 $(system.defaultWorkingDirectory)/AndroidApps/MyApp.)와 유사해야 합니다.

  • 파일이 리포지토리의 루트에 없는 경우 gradlew gradleWrapperFile 값을 조정합니다. 파일 경로 값은 리포지토리의 루트(예: AndroidApps/MyApp/gradlew 또는 $(system.defaultWorkingDirectory)/AndroidApps/MyApp/gradlew.)와 유사해야 합니다.

Gradle 작업 조정

원하는 빌드 변형의 작업 값을 조정합니다(예: assembleDebug/assembleRelease>). 자세한 내용은 다음 Google Android 개발 설명서를 참조하세요.

APK(Android 패키지) 서명 및 맞춤

빌드가 아직 APK에 서명하고 zipalign 하지 않은 경우 YAML에 Android 서명 작업을 추가합니다. 에뮬레이터 대신 디바이스에서 실행하려면 APK에 서명해야 합니다. Zipaligning은 애플리케이션에서 사용하는 RAM을 줄입니다.

Important

비밀 변수에 다음 암호를 각각 저장하는 것이 좋습니다.

- task: AndroidSigning@2
  inputs:
    apkFiles: '**/*.apk'
    jarsign: true
    jarsignerKeystoreFile: 'pathToYourKeystoreFile'
    jarsignerKeystorePassword: '$(jarsignerKeystorePassword)'
    jarsignerKeystoreAlias: 'yourKeystoreAlias'
    jarsignerKeyPassword: '$(jarsignerKeyPassword)'
    zipalign: true

테스트

Android Emulator에서 테스트

Bash 작업을 만들고 에뮬레이터를 설치하고 실행하기 위해 아래 코드를 복사하여 붙여넣습니다. 테스트 환경에 맞게 에뮬레이터 매개 변수를 정렬하는 것을 잊지 마세요. 에뮬레이터는 백그라운드 프로세스로 시작되며 이후 작업에서 사용할 수 있습니다.

#!/usr/bin/env bash

# Install AVD files
echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install 'system-images;android-27;google_apis;x86'

# Create emulator
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n xamarin_android_emulator -k 'system-images;android-27;google_apis;x86' --force

$ANDROID_HOME/emulator/emulator -list-avds

echo "Starting emulator"

# Start emulator in background
nohup $ANDROID_HOME/emulator/emulator -avd xamarin_android_emulator -no-snapshot > /dev/null 2>&1 &
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'

$ANDROID_HOME/platform-tools/adb devices

echo "Emulator started"

Azure 호스팅 디바이스에서 테스트

App Center 테스트 작업을 추가하여 iOS 및 Android 디바이스의 호스트된 랩에서 애플리케이션을 테스트합니다. App Center 무료 평가판이 필요하며 나중에 유료로 변환해야 합니다.

App Center 에 먼저 등록합니다.

# App Center test v1
# Test app packages with Visual Studio App Center.
- task: AppCenterTest@1
  inputs:
    appFile: # string. Alias: app. Required. Binary application file path. 
    artifactsDirectory: '$(Build.ArtifactStagingDirectory)/AppCenterTest' # string. Alias: artifactsDir. Required. Artifacts directory. Default: $(Build.ArtifactStagingDirectory)/AppCenterTest.
  # Prepare Tests
    #prepareTests: true # boolean. Alias: enablePrepare. Prepare tests. Default: true.
    frameworkOption: 'appium' # 'appium' | 'espresso' | 'calabash' | 'uitest' | 'xcuitest'. Alias: framework. Required when enablePrepare = true. Test framework. Default: appium.
    #appiumBuildDirectory: # string. Alias: appiumBuildDir. Required when enablePrepare = true && framework = appium. Build directory. 
    #espressoBuildDirectory: # string. Alias: espressoBuildDir. Optional. Use when enablePrepare = true && framework = espresso. Build directory. 
    #espressoTestApkFile: # string. Alias: espressoTestApkPath. Optional. Use when enablePrepare = true && framework = espresso. Test APK path. 
    #calabashProjectDirectory: # string. Alias: calabashProjectDir. Required when enablePrepare = true && framework = calabash. Project directory. 
    #calabashConfigFile: # string. Optional. Use when enablePrepare = true && framework = calabash. Cucumber config file. 
    #calabashProfile: # string. Optional. Use when enablePrepare = true && framework = calabash. Profile to run. 
    #calabashSkipConfigCheck: false # boolean. Optional. Use when enablePrepare = true && framework = calabash. Skip Configuration Check. Default: false.
    #uiTestBuildDirectory: # string. Alias: uitestBuildDir. Required when enablePrepare = true && framework = uitest. Build directory. 
    #uitestStorePath: # string. Optional. Use when enablePrepare = true && framework = uitest. Store file. 
    #uiTestStorePassword: # string. Alias: uitestStorePass. Optional. Use when enablePrepare = true && framework = uitest. Store password. 
    #uitestKeyAlias: # string. Optional. Use when enablePrepare = true && framework = uitest. Key alias. 
    #uiTestKeyPassword: # string. Alias: uitestKeyPass. Optional. Use when enablePrepare = true && framework = uitest. Key password. 
    #uiTestToolsDirectory: # string. Alias: uitestToolsDir. Optional. Use when enablePrepare = true && framework = uitest. Test tools directory. 
    #signInfo: # string. Optional. Use when framework = calabash || framework = uitest. Signing information. 
    #xcUITestBuildDirectory: # string. Alias: xcuitestBuildDir. Optional. Use when enablePrepare = true && framework = xcuitest. Build directory. 
    #xcUITestIpaFile: # string. Alias: xcuitestTestIpaPath. Optional. Use when enablePrepare = true && framework = xcuitest. Test IPA path. 
    #prepareOptions: # string. Alias: prepareOpts. Optional. Use when enablePrepare = true. Additional options. 
  # Run Tests
    #runTests: true # boolean. Alias: enableRun. Run tests. Default: true.
    credentialsOption: 'serviceEndpoint' # 'serviceEndpoint' | 'inputs'. Alias: credsType. Required when enableRun = true. Authentication method. Default: serviceEndpoint.
    #serverEndpoint: # string. Required when enableRun = true && credsType = serviceEndpoint. App Center service connection. 
    #username: # string. Required when enableRun = true && credsType = inputs. App Center username. 
    #password: # string. Required when enableRun = true && credsType = inputs. App Center password. 
    appSlug: # string. Required when enableRun = true. App slug. 
    devices: # string. Required when enableRun = true. Devices. 
    #series: 'master' # string. Optional. Use when enableRun = true. Test series. Default: master.
    #dsymDirectory: # string. Alias: dsymDir. Optional. Use when enableRun = true. dSYM directory. 
    localeOption: 'en_US' # 'da_DK' | 'nl_NL' | 'en_GB' | 'en_US' | 'fr_FR' | 'de_DE' | 'ja_JP' | 'ru_RU' | 'es_MX' | 'es_ES' | 'user'. Alias: locale. Required when enableRun = true. System language. Default: en_US.
    #userDefinedLocale: # string. Optional. Use when enableRun = true && locale = user. Other locale. 
    #loginOptions: # string. Alias: loginOpts. Optional. Use when enableRun = true && credsType = inputs. Additional options for login. 
    #runOptions: # string. Alias: runOpts. Optional. Use when enableRun = true. Additional options for run. 
    #skipWaitingForResults: false # boolean. Alias: async. Optional. Use when enableRun = true. Do not wait for test result. Default: false.
  # Advanced
    #cliFile: # string. Alias: cliLocationOverride. App Center CLI location. 
    #showDebugOutput: false # boolean. Alias: debug. Enable debug output. Default: false.

빌드 레코드를 사용하여 아티팩트 유지

파일 복사 및 빌드 아티팩트 게시 작업을 추가합니다. APK는 빌드 레코드 또는 테스트와 함께 저장되고 이후 파이프라인에 배포됩니다. 자세한 내용은 아티팩트(Artifacts)를 참조 하세요.

- task: CopyFiles@2
  inputs:
    contents: '**/*.apk'
    targetFolder: '$(build.artifactStagingDirectory)'
- task: PublishBuildArtifacts@1

배포

App Center 추가

App Center 배포 작업을 추가하여 테스터 또는 베타 사용자 그룹에 애플리케이션을 배포하거나 애플리케이션을 Intune 또는 Google Play로 승격합니다. 무료 App Center 계정이 필요합니다(결제가 필요 없음).

# App Center distribute v3
# Distribute app builds to testers and users via Visual Studio App Center.
- task: AppCenterDistribute@3
  inputs:
    serverEndpoint: # string. Required. App Center service connection. 
    appSlug: # string. Required. App slug. 
    appFile: # string. Alias: app. Required. Binary file path. 
    #buildVersion: # string. Build version. 
    releaseNotesOption: 'input' # 'input' | 'file'. Alias: releaseNotesSelection. Required. Create release notes. Default: input.
    releaseNotesInput: # string. Required when releaseNotesSelection = input. Release notes. 
    #releaseNotesFile: # string. Required when releaseNotesSelection = file. Release notes file. 
    #isMandatory: false # boolean. Require users to update to this release. Default: false.
    destinationType: 'groups' # 'groups' | 'store'. Required. Release destination. Default: groups.
    #distributionGroupId: # string. Alias: destinationGroupIds. Optional. Use when destinationType = groups. Destination IDs. 
    #destinationStoreId: # string. Required when destinationType = store. Destination ID. 
    #isSilent: # boolean. Optional. Use when destinationType = groups. Do not notify testers. Release will still be available to install. 
  # Symbols
    #symbolsOption: 'Apple' # 'Apple' | 'Android' | 'UWP'. Alias: symbolsType. Symbols type. Default: Apple.
    #symbolsPath: # string. Optional. Use when symbolsType == AndroidNative || symbolsType = Windows. Symbols path. 
    #appxsymPath: # string. Optional. Use when symbolsType = UWP. Symbols path (*.appxsym). 
    #symbolsDsymFiles: # string. Alias: dsymPath. Optional. Use when symbolsType = Apple. dSYM path. 
    #symbolsMappingTxtFile: # string. Alias: mappingTxtPath. Optional. Use when symbolsType = Android. Mapping file. 
    #nativeLibrariesPath: # string. Optional. Use when symbolsType == Android. Native Library File Path. 
    #symbolsIncludeParentDirectory: # boolean. Alias: packParentFolder. Optional. Use when symbolsType = Apple. Include all items in parent folder.

Google Play 설치

Google Play 확장을 설치하고 다음 작업을 사용하여 Google Play와의 상호 작용을 자동화합니다. 기본적으로 이러한 작업은 구성한 서비스 연결을 사용하여 Google Play에 인증됩니다.

Release

Google Play 릴리스 작업을 추가하여 Google Play 스토어에 새 Android 앱 버전을 릴리스합니다.

- task: GooglePlayRelease@4
  inputs:
    apkFile: '**/*.apk'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    track: 'internal'

홍보

Google Play 승격 작업을 추가하여 이전에 릴리스된 Android 애플리케이션 업데이트를 한 트랙에서 다른 트랙(예: →beta)으로 alpha 승격합니다.

- task: GooglePlayPromote@3
  inputs:
    packageName: 'com.yourCompany.appPackageName'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    sourceTrack: 'internal'
    destinationTrack: 'alpha'

롤아웃 늘리기

Google Play 증가 롤아웃 작업을 추가하여 이전에 트랙에 릴리스 rollout 된 애플리케이션의 출시 비율을 높입니다.

- task: GooglePlayIncreaseRollout@2
  inputs:
    packageName: 'com.yourCompany.appPackageName'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    userFraction: '0.5' # 0.0 to 1.0 (0% to 100%)

상태 업데이트

Google Play 상태 업데이트 작업을 추가하여 이전에 트랙에 릴리스 rollout 된 애플리케이션의 롤아웃 상태 업데이트합니다.

  - task: GooglePlayStatusUpdate@2
    inputs:
      authType: ServiceEndpoint
      packageName: 'com.yourCompany.appPackageName'
      serviceEndpoint: 'yourGooglePlayServiceConnectionName'
      status: 'inProgress' # draft | inProgress | halted | completed

FAQ

Q: 앱 번들을 만들 어떻게 할까요? 있나요?

A: 인라인 스크립트와 보안 파일을 사용하여 앱 번들을 빌드하고 서명할 수 있습니다. 이렇게 하려면 먼저 키 저장소를 다운로드하고 라이브러리에 보안 파일로 저장합니다. 그런 다음 , key.aliaskey.password 변수 그룹에 대한 keystore.password변수를 만듭니다.

다음으로 보안 파일Bash 다운로드 작업을 사용하여 키 저장소를 다운로드하고 앱 번들을 빌드하고 서명합니다.

이 YAML 파일에서 보안 파일을 다운로드 app.keystore 하고 bash 스크립트를 사용하여 앱 번들을 생성합니다. 그런 다음 파일 복사를 사용하여 앱 번들을 복사합니다. 여기에서 빌드 아티팩트 게시를 사용하여 아티팩트를 만들고 저장하거나 Google Play 확장을 사용하여 게시합니다.

- task: DownloadSecureFile@1
  name: keyStore
  displayName: "Download keystore from secure files"
  inputs:
    secureFile: app.keystore

- task: Bash@3
  displayName: "Build and sign App Bundle"
  inputs:
    targetType: "inline"
    script: |
      msbuild -restore $(Build.SourcesDirectory)/myAndroidApp/*.csproj -t:SignAndroidPackage -p:AndroidPackageFormat=aab -p:Configuration=$(buildConfiguration) -p:AndroidKeyStore=True -p:AndroidSigningKeyStore=$(keyStore.secureFilePath) -p:AndroidSigningStorePass=$(keystore.password) -p:AndroidSigningKeyAlias=$(key.alias) -p:AndroidSigningKeyPass=$(key.password)

- task: CopyFiles@2
  displayName: 'Copy deliverables'
  inputs:
    SourceFolder: '$(Build.SourcesDirectory)/myAndroidApp/bin/$(buildConfiguration)'
    Contents: '*.aab'
    TargetFolder: 'drop'