I'm attempting to set up Firebase App Distribution in our Azure Pipeline for our app, using a service account for authentication. However, I'm encountering the following error:
/bin/bash --noprofile --norc /Users/runner/work/_temp/9862a4f1-6542-4b7b-9dbb-9782561da5bb.sh Attempting Firebase authentication with service account... ##[debug]Agent environment resources - Disk: / Available 124774.09 MB out of 332471.96 MB, Memory: Used 7722.00 MB out of 14332.00 MB, CPU: Usage 99.30% Error: Failed to authenticate, have you run firebase login? Firebase authentication failed. ##[debug]Exit code 1 received from tool '/bin/bash' ##[debug]STDIO streams have closed for tool '/bin/bash' ##[error]Bash exited with code '1'.
I have ensured that the service account key is provided, and the environment variable GOOGLE_APPLICATION_CREDENTIALS is set. Below is my pipeline YAML configuration:
trigger: none pool: vmImage: 'macOS-latest' parameters: - name: appVariant displayName: 'App Variant' type: string default: '{{appName}}' values: - '{{appName}}' - 'BCDC' - name: firebaseEnv displayName: 'Firebase Environment' type: string default: 'UAT' values: - 'UAT' - 'Pilot' - name: testGroup displayName: 'Test Group' type: string default: 'Android_UAT_Testers' values: - 'Android_UAT_Testers' - 'Android_Pilot_Testers' - name: releaseNotes displayName: 'Release Notes' type: string default: 'No release notes provided.' stages: - stage: Build jobs: - job: BuildJob timeoutInMinutes: 120 variables: GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle steps: - task: CmdLine@2 inputs: script: "sudo rm -rf $ANDROID_HOME/ndk/ && sudo rm -rf $ANDROID_HOME/ndk-bundle/" - ${{ if eq(parameters.appVariant, '{{appName}}') }}: - ${{ if eq(parameters.firebaseEnv, 'UAT') }}: - task: Gradle@2 displayName: 'Gradle Compile {{appName}} for UAT' inputs: gradleWrapperFile: 'gradlew' tasks: 'assembleUatOne{{appName}}GoogleDebug' - ${{ if eq(parameters.firebaseEnv, 'Pilot') }}: - task: Gradle@2 displayName: 'Gradle Compile {{appName}} for Pilot' inputs: gradleWrapperFile: 'gradlew' tasks: 'assemblePilotOne{{appName}}GoogleDebug' - ${{ if eq(parameters.appVariant, 'BCDC') }}: - ${{ if eq(parameters.firebaseEnv, 'UAT') }}: - task: Gradle@2 displayName: 'Gradle Compile BCDC for UAT' inputs: gradleWrapperFile: 'gradlew' tasks: 'assembleUatOne{{appName}}BcdcGoogleDebug' - ${{ if eq(parameters.firebaseEnv, 'Pilot') }}: - task: Gradle@2 displayName: 'Gradle Compile BCDC for Pilot' inputs: gradleWrapperFile: 'gradlew' tasks: 'assemblePilotOne{{appName}}BcdcGoogleDebug' - task: CopyFiles@2 inputs: SourceFolder: $(Build.SourcesDirectory) contents: '**/*.apk' targetFolder: '$(build.artifactStagingDirectory)' overWrite: true - task: PublishBuildArtifacts@1 inputs: pathtoPublish: '$(Build.ArtifactStagingDirectory)/app/build/outputs/apk/' artifactName: 'apks' publishLocation: 'container' - task: CmdLine@2 displayName: 'Check Service Account and Credentials' inputs: script: | echo "$(MOBILEFIREBASEACCOUNTSERVICE)" > $(Pipeline.Workspace)/service-account.json export GOOGLE_APPLICATION_CREDENTIALS=$(Pipeline.Workspace)/service-account.json echo "GOOGLE_APPLICATION_CREDENTIALS is set to: $GOOGLE_APPLICATION_CREDENTIALS" echo "Service account JSON file:" cat $(Pipeline.Workspace)/service-account.json | jq '.' if [ -f "$(Pipeline.Workspace)/service-account.json" ]; then echo "Service account file exists." else echo "Service account file does NOT exist." exit 1 fi - task: CmdLine@2 displayName: 'Install Firebase CLI' inputs: script: 'npm install -g firebase-tools' - task: CmdLine@2 displayName: 'Authenticate Firebase CLI with Service Account' inputs: script: | echo "Attempting Firebase authentication with service account..." if npx firebase projects:list; then echo "Firebase authentication successful." else echo "Firebase authentication failed." exit 1 fi - task: CmdLine@2 displayName: 'Distribute APK to Firebase (UAT)' condition: eq('${{ parameters.firebaseEnv }}', 'UAT') inputs: script: | npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/debug/app-uat-one{{appName}}-google-debug.apk" \ --app $(FIREBASE_APP_ID) \ --groups ${{ parameters.testGroup }} \ --release-notes "${{ parameters.releaseNotes }}" \ --debug - task: CmdLine@2 displayName: 'Distribute APK to Firebase (Pilot)' condition: eq('${{ parameters.firebaseEnv }}', 'Pilot') inputs: script: | npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/release/app-uat-one{{appName}}-google-release.apk" \ --app $(FIREBASE_APP_ID) \ --groups ${{ parameters.testGroup }} \ --release-notes "${{ parameters.releaseNotes }}" \ --debug
Error Details: It appears that Firebase CLI isn't authenticating properly using the service account credentials. The error suggests running firebase login, but since I'm using a service account, this shouldn't be necessary. What I've Tried: Verified the service account has sufficient permissions for Firebase App Distribution. Ensured that the environment variable GOOGLE_APPLICATION_CREDENTIALS is correctly set. Ran the command npx firebase projects:list to verify authentication, but it fails with the same error.
trigger: none
pool:
vmImage: 'macOS-latest'
parameters:
- name: appVariant
displayName: 'App Variant'
type: string
default: '{{appName}}'
values:
- '{{appName}}'
- 'BCDC'
- name: firebaseEnv
displayName: 'Firebase Environment'
type: string
default: 'UAT'
values:
- 'UAT'
- 'Pilot'
- name: testGroup
displayName: 'Test Group'
type: string
default: 'Android_UAT_Testers'
values:
- 'Android_UAT_Testers'
- 'Android_Pilot_Testers'
- name: releaseNotes
displayName: 'Release Notes'
type: string
default: 'No release notes provided.'
stages:
- stage: Build
jobs:
- job: BuildJob
timeoutInMinutes: 120
variables:
GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle
steps:
- task: CmdLine@2
inputs:
script: "sudo rm -rf $ANDROID_HOME/ndk/ && sudo rm -rf $ANDROID_HOME/ndk-bundle/"
- ${{ if eq(parameters.appVariant, '{{appName}}') }}:
- ${{ if eq(parameters.firebaseEnv, 'UAT') }}:
- task: Gradle@2
displayName: 'Gradle Compile {{appName}} for UAT'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assembleUatOne{{appName}}GoogleDebug'
- ${{ if eq(parameters.firebaseEnv, 'Pilot') }}:
- task: Gradle@2
displayName: 'Gradle Compile {{appName}} for Pilot'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assemblePilotOne{{appName}}GoogleDebug'
- ${{ if eq(parameters.appVariant, 'BCDC') }}:
- ${{ if eq(parameters.firebaseEnv, 'UAT') }}:
- task: Gradle@2
displayName: 'Gradle Compile BCDC for UAT'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assembleUatOne{{appName}}BcdcGoogleDebug'
- ${{ if eq(parameters.firebaseEnv, 'Pilot') }}:
- task: Gradle@2
displayName: 'Gradle Compile BCDC for Pilot'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assemblePilotOne{{appName}}BcdcGoogleDebug'
- task: CopyFiles@2
inputs:
SourceFolder: $(Build.SourcesDirectory)
contents: '**/*.apk'
targetFolder: '$(build.artifactStagingDirectory)'
overWrite: true
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/app/build/outputs/apk/'
artifactName: 'apks'
publishLocation: 'container'
- task: CmdLine@2
displayName: 'Check Service Account and Credentials'
inputs:
script: |
echo "$(MOBILEFIREBASEACCOUNTSERVICE)" > $(Pipeline.Workspace)/service-account.json
export GOOGLE_APPLICATION_CREDENTIALS=$(Pipeline.Workspace)/service-account.json
echo "GOOGLE_APPLICATION_CREDENTIALS is set to: $GOOGLE_APPLICATION_CREDENTIALS"
echo "Service account JSON file:"
cat $(Pipeline.Workspace)/service-account.json | jq '.'
if [ -f "$(Pipeline.Workspace)/service-account.json" ]; then
echo "Service account file exists."
else
echo "Service account file does NOT exist."
exit 1
fi
- task: CmdLine@2
displayName: 'Install Firebase CLI'
inputs:
script: 'npm install -g firebase-tools'
- task: CmdLine@2
displayName: 'Authenticate Firebase CLI with Service Account'
inputs:
script: |
echo "Attempting Firebase authentication with service account..."
if npx firebase projects:list; then
echo "Firebase authentication successful."
else
echo "Firebase authentication failed."
exit 1
fi
- task: CmdLine@2
displayName: 'Distribute APK to Firebase (UAT)'
condition: eq('${{ parameters.firebaseEnv }}', 'UAT')
inputs:
script: |
npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/debug/app-uat-one{{appName}}-google-debug.apk" \
--app $(FIREBASE_APP_ID) \
--groups ${{ parameters.testGroup }} \
--release-notes "${{ parameters.releaseNotes }}" \
--debug
- task: CmdLine@2
displayName: 'Distribute APK to Firebase (Pilot)'
condition: eq('${{ parameters.firebaseEnv }}', 'Pilot')
inputs:
script: |
npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/release/app-uat-one{{appName}}-google-release.apk" \
--app $(FIREBASE_APP_ID) \
--groups ${{ parameters.testGroup }} \
--release-notes "${{ parameters.releaseNotes }}" \
--debug
From your YAML sample, you are setting the environment variable: GOOGLE_APPLICATION_CREDENTIALS with the command: export GOOGLE_APPLICATION_CREDENTIALS=$(Pipeline.Workspace)/service-account.json
.
In this case, the environment variable can only be used in the current CommandLine task. It cannot be passed to the next tasks.
Since you are running the command: npx firebase projects:list
in different tasks, it cannot read the environment variable: GOOGLE_APPLICATION_CREDENTIALS
.
To solve this issue, you can use the logging command to define pipeline variable.
From:
export GOOGLE_APPLICATION_CREDENTIALS=$(Pipeline.Workspace)/service-account.json
To:
echo "##vso[task.setvariable variable=GOOGLE_APPLICATION_CREDENTIALS]$(Pipeline.Workspace)/service-account.json"
Example:
- task: CmdLine@2
displayName: 'Check Service Account and Credentials'
inputs:
script: |
echo "$(MOBILEFIREBASEACCOUNTSERVICE)" > $(Pipeline.Workspace)/service-account.json
echo "##vso[task.setvariable variable=GOOGLE_APPLICATION_CREDENTIALS]$(Pipeline.Workspace)/service-account.json"
echo "GOOGLE_APPLICATION_CREDENTIALS is set to: $GOOGLE_APPLICATION_CREDENTIALS"
echo "Service account JSON file:"
cat $(Pipeline.Workspace)/service-account.json | jq '.'
if [ -f "$(Pipeline.Workspace)/service-account.json" ]; then
echo "Service account file exists."
else
echo "Service account file does NOT exist."
exit 1
fi
In this case, the environment variable: GOOGLE_APPLICATION_CREDENTIALS can be read in the next tasks.
Or you can also set the environment variable for all related Commandline tasks via the env
argument.
env:
GOOGLE_APPLICATION_CREDENTIALS: $(Pipeline.Workspace)/service-account.json
For example:
stages:
- stage: Build
jobs:
- job: BuildJob
timeoutInMinutes: 120
variables:
GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle
steps:
- task: CmdLine@2
inputs:
script: "sudo rm -rf $ANDROID_HOME/ndk/ && sudo rm -rf $ANDROID_HOME/ndk-bundle/"
- ${{ if eq(parameters.appVariant, '{{appName}}') }}:
- ${{ if eq(parameters.firebaseEnv, 'UAT') }}:
- task: Gradle@2
displayName: 'Gradle Compile {{appName}} for UAT'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assembleUatOne{{appName}}GoogleDebug'
- ${{ if eq(parameters.firebaseEnv, 'Pilot') }}:
- task: Gradle@2
displayName: 'Gradle Compile {{appName}} for Pilot'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assemblePilotOne{{appName}}GoogleDebug'
- ${{ if eq(parameters.appVariant, 'BCDC') }}:
- ${{ if eq(parameters.firebaseEnv, 'UAT') }}:
- task: Gradle@2
displayName: 'Gradle Compile BCDC for UAT'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assembleUatOne{{appName}}BcdcGoogleDebug'
- ${{ if eq(parameters.firebaseEnv, 'Pilot') }}:
- task: Gradle@2
displayName: 'Gradle Compile BCDC for Pilot'
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'assemblePilotOne{{appName}}BcdcGoogleDebug'
- task: CopyFiles@2
inputs:
SourceFolder: $(Build.SourcesDirectory)
contents: '**/*.apk'
targetFolder: '$(build.artifactStagingDirectory)'
overWrite: true
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/app/build/outputs/apk/'
artifactName: 'apks'
publishLocation: 'container'
- task: CmdLine@2
displayName: 'Check Service Account and Credentials'
inputs:
script: |
echo "$(MOBILEFIREBASEACCOUNTSERVICE)" > $(Pipeline.Workspace)/service-account.json
export GOOGLE_APPLICATION_CREDENTIALS=$(Pipeline.Workspace)/service-account.json
echo "GOOGLE_APPLICATION_CREDENTIALS is set to: $GOOGLE_APPLICATION_CREDENTIALS"
echo "Service account JSON file:"
cat $(Pipeline.Workspace)/service-account.json | jq '.'
if [ -f "$(Pipeline.Workspace)/service-account.json" ]; then
echo "Service account file exists."
else
echo "Service account file does NOT exist."
exit 1
fi
- task: CmdLine@2
displayName: 'Install Firebase CLI'
inputs:
script: 'npm install -g firebase-tools'
- task: CmdLine@2
displayName: 'Authenticate Firebase CLI with Service Account'
inputs:
script: |
echo "Attempting Firebase authentication with service account..."
if npx firebase projects:list; then
echo "Firebase authentication successful."
else
echo "Firebase authentication failed."
exit 1
fi
env:
GOOGLE_APPLICATION_CREDENTIALS: $(Pipeline.Workspace)/service-account.json
- task: CmdLine@2
displayName: 'Distribute APK to Firebase (UAT)'
condition: eq('${{ parameters.firebaseEnv }}', 'UAT')
inputs:
script: |
npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/debug/app-uat-one{{appName}}-google-debug.apk" \
--app $(FIREBASE_APP_ID) \
--groups ${{ parameters.testGroup }} \
--release-notes "${{ parameters.releaseNotes }}" \
--debug
env:
GOOGLE_APPLICATION_CREDENTIALS: $(Pipeline.Workspace)/service-account.json
- task: CmdLine@2
displayName: 'Distribute APK to Firebase (Pilot)'
condition: eq('${{ parameters.firebaseEnv }}', 'Pilot')
inputs:
script: |
npx firebase appdistribution:distribute "$(build.artifactStagingDirectory)/app/build/outputs/apk/uatOne{{appName}}Google/release/app-uat-one{{appName}}-google-release.apk" \
--app $(FIREBASE_APP_ID) \
--groups ${{ parameters.testGroup }} \
--release-notes "${{ parameters.releaseNotes }}" \
--debug
env:
GOOGLE_APPLICATION_CREDENTIALS: $(Pipeline.Workspace)/service-account.json