From 7751f001bd32860f8b9553c31bb8b1e1a88f7ae2 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sat, 19 Apr 2025 23:44:40 -0700 Subject: [PATCH 01/11] feat: Use shared action for local deploy --- .github/workflows/deploy-functions.yml | 89 ++------------------------ 1 file changed, 5 insertions(+), 84 deletions(-) diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index 89d0e6d..f02ed27 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -29,7 +29,6 @@ concurrency: jobs: deploy: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 @@ -41,87 +40,9 @@ jobs: exit 1 fi - - name: Set up Node.js for Firebase CLI - uses: actions/setup-node@v3 - with: - node-version: '20' - - - name: Install Firebase CLI - run: | - npm install -g firebase-tools - firebase --version - - - name: Install uv package manager - uses: astral-sh/setup-uv@v5 + - name: Deploy Firebase Functions + uses: ./ with: - version: "0.6.4" - - - name: Install Python 3.11 with uv - run: uv python install 3.11 - - - name: Use uv to install dependencies in /functions/venv - working-directory: functions - run: | - uv venv venv --python 3.11 - source venv/bin/activate - uv pip install --upgrade pip - uv sync --active - uv pip freeze > requirements.txt - deactivate - - # Store key in the runner's environment variable to persist across steps - - name: Authenticate with GCP service account key - env: - SERVICE_ACCOUNT_JSON: ${{ github.event.inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} - run: | - echo "$SERVICE_ACCOUNT_JSON" > service-account.json - echo "GOOGLE_APPLICATION_CREDENTIALS=service-account.json" >> $GITHUB_ENV - - - name: Authenticate GitHub Actions service account - run: | - gcloud auth activate-service-account --key-file=service-account.json - - - name: Verify service account ADC (Debug) - run: | - gcloud config get-value account - gcloud auth list - - - name: Verify Enabled APIs (Debug) - run: | - gcloud services list --enabled --project=${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} - - - name: List Firebase Projects (Debug Mode) - run: firebase projects:list --debug - - - name: Verify Service Account IAM Policy (Debug) - run: | - PROJECT_ID=${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} - echo "Verifying IAM policy for project: $PROJECT_ID" - gcloud projects get-iam-policy $PROJECT_ID --format=json - - - name: Select Firebase Project - id: select_project - run: | - if [ "${{ github.event.inputs.environment }}" = "prod" ]; then - firebase use prod --non-interactive - else - firebase use staging --non-interactive - fi - - - name: Deploy Firebase Functions (Debug Mode) - run: firebase deploy --only functions --debug - - - name: Post-deployment summary - env: - ENVIRONMENT: ${{ github.event.inputs.environment }} - TRIGGER: ${{ github.event_name }} - REF_NAME: ${{ github.ref_name }} - run: | - echo "### Deployment Summary ๐Ÿš€" >> $GITHUB_STEP_SUMMARY - echo "* **Environment**: $ENVIRONMENT" >> $GITHUB_STEP_SUMMARY - echo "* **Trigger**: $TRIGGER" >> $GITHUB_STEP_SUMMARY - echo "* **Branch/Ref**: $REF_NAME" >> $GITHUB_STEP_SUMMARY - - - name: Cleanup credentials - if: always() - run: rm -f service-account.json \ No newline at end of file + environment: ${{ github.event.inputs.environment }} + service_account_json: ${{ github.event.inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} + project_id: ${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} From 290d44811c528ac9bd99fcf9f0ae7c78685ec0d1 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sat, 19 Apr 2025 23:49:41 -0700 Subject: [PATCH 02/11] Fix lint permissions --- .github/workflows/lint-pr-title.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 0d1bf14..0a9e6fd 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -6,6 +6,7 @@ on: permissions: pull-requests: read + contents: read jobs: lint-pr-title: From 84540346c0acd3ea74f09d65b92289462834ccf5 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sat, 19 Apr 2025 23:58:58 -0700 Subject: [PATCH 03/11] Fix lint --- .github/workflows/lint-pr-title.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 0a9e6fd..272930e 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -1,17 +1,19 @@ -name: Lint PR Title +name: "Lint PR" on: - pull_request: - types: [opened, edited, synchronize, reopened] - -permissions: - pull-requests: read - contents: read + pull_request_target: + types: + - opened + - edited + - synchronize + - reopened jobs: - lint-pr-title: - name: Validate PR Title + main: + name: Validate PR title runs-on: ubuntu-latest + permissions: + pull-requests: read steps: - uses: amannn/action-semantic-pull-request@v5 env: @@ -34,6 +36,3 @@ jobs: โœ… feat: Add new deployment option โœ… fix(functions): Resolve authentication issue โŒ feat: add new deployment option - wip: true - validateSingleCommit: false - validateSingleCommitMatchesPrTitle: false From 9dc3c475402645006019d9538e8bb0b7c5e60087 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sun, 20 Apr 2025 00:13:33 -0700 Subject: [PATCH 04/11] Fix deploy translation error --- .github/workflows/deploy-functions.yml | 1 - README.md | 18 ++++++++++-------- action.yml | 7 +++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index f02ed27..aebeeff 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -44,5 +44,4 @@ jobs: uses: ./ with: environment: ${{ github.event.inputs.environment }} - service_account_json: ${{ github.event.inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} project_id: ${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} diff --git a/README.md b/README.md index f95eabc..94ed323 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ jobs: - uses: ./ # Uses local action for testing with: environment: ${{ github.event_name == 'push' && github.ref_name || inputs.environment }} - service_account_json: ${{ secrets[format('FIREBASE_{0}_SERVICE_ACCOUNT', github.event_name == 'push' && github.ref_name || inputs.environment)] }} project_id: ${{ vars[format('FIREBASE_{0}_PROJECT_ID', github.event_name == 'push' && github.ref_name || inputs.environment)] }} ``` @@ -67,18 +66,21 @@ When using in your own repository, reference a specific version: with: functions_dir: 'src/functions' # Default is 'functions' environment: staging - service_account_json: ${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} project_id: my-project-staging ``` +Note: The action requires the following secrets to be defined in your repository: + +- `FIREBASE_STAGING_SERVICE_ACCOUNT`: Service account JSON for staging environment +- `FIREBASE_PROD_SERVICE_ACCOUNT`: Service account JSON for production environment + ## Inputs -| Input | Description | Required | Default | -| ---------------------- | ------------------------------------------------- | -------- | ----------- | -| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | -| `environment` | Environment to deploy to (staging/prod) | Yes | N/A | -| `service_account_json` | Firebase service account JSON | Yes | N/A | -| `project_id` | Firebase project ID | Yes | N/A | +| Input | Description | Required | Default | +| --------------- | ------------------------------------------------- | -------- | ----------- | +| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | +| `environment` | Environment to deploy to (staging/prod) | Yes | N/A | +| `project_id` | Firebase project ID | Yes | N/A | ## Prerequisites diff --git a/action.yml b/action.yml index d50ed09..9bd720c 100644 --- a/action.yml +++ b/action.yml @@ -22,9 +22,6 @@ inputs: options: - staging - prod - service_account_json: - description: 'Firebase service account JSON' - required: true project_id: description: 'Firebase project ID' required: true @@ -56,8 +53,10 @@ runs: - name: Authenticate with GCP service account key shell: bash + env: + SERVICE_ACCOUNT_JSON: ${{ inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} run: | - echo "${{ inputs.service_account_json }}" > service-account.json + echo "$SERVICE_ACCOUNT_JSON" > service-account.json echo "GOOGLE_APPLICATION_CREDENTIALS=service-account.json" >> $GITHUB_ENV - name: Authenticate GitHub Actions service account From cbcdd5a73c76a81fb0b383d5964bf42237a4ec9f Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sun, 20 Apr 2025 08:58:29 -0700 Subject: [PATCH 05/11] Improvements --- .github/workflows/deploy-functions.yml | 11 ++- README.md | 105 ++++--------------------- action.yml | 20 ++--- 3 files changed, 29 insertions(+), 107 deletions(-) diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index aebeeff..bec907e 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -40,8 +40,17 @@ jobs: exit 1 fi + - name: Prepare service account + id: prepare + run: | + if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then + echo "service_account_b64=$(echo '${{ secrets.FIREBASE_PROD_SERVICE_ACCOUNT }}' | base64)" >> $GITHUB_OUTPUT + else + echo "service_account_b64=$(echo '${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }}' | base64)" >> $GITHUB_OUTPUT + fi + - name: Deploy Firebase Functions uses: ./ with: - environment: ${{ github.event.inputs.environment }} project_id: ${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} + service_account_json_b64: ${{ steps.prepare.outputs.service_account_b64 }} diff --git a/README.md b/README.md index 94ed323..33a35b7 100644 --- a/README.md +++ b/README.md @@ -4,113 +4,36 @@ A GitHub Action for deploying Python-based Firebase Cloud Functions. This reposi ## Features -- ๐Ÿ Automatic Python version detection from pyproject.toml -- ๐Ÿ“ฆ Dependency management using UV package manager -- ๐Ÿ”„ Environment-based deployments (staging/prod) +- ๐Ÿ Python dependency management from pyproject.toml - ๐Ÿ” Secure handling of service account credentials -- ๐Ÿ“ Detailed deployment summaries -- ๐Ÿงช Self-testing repository structure - -## Repository Structure - -This repository is structured to serve two purposes: - -1. Provide the composite action implementation -2. Serve as a live example and test environment - -``` -. -โ”œโ”€โ”€ action.yml # The composite action definition -โ”œโ”€โ”€ .github/workflows/ # Contains workflow using the action -โ”‚ โ””โ”€โ”€ deploy-functions.yml -โ”œโ”€โ”€ functions/ # Example Firebase Functions -โ”‚ โ”œโ”€โ”€ main.py -โ”‚ โ””โ”€โ”€ pyproject.toml -โ””โ”€โ”€ README.md # Documentation -``` +- Detailed deployment summaries ## Usage -The workflow in this repository (.github/workflows/deploy-functions.yml) demonstrates the recommended usage: - -```yaml -name: Test & Deploy Python Functions - -on: - push: - branches: [staging, prod] - workflow_dispatch: - inputs: - environment: - type: choice - options: [staging, prod] - description: 'Environment to deploy to' - required: true - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: ./ # Uses local action for testing - with: - environment: ${{ github.event_name == 'push' && github.ref_name || inputs.environment }} - project_id: ${{ vars[format('FIREBASE_{0}_PROJECT_ID', github.event_name == 'push' && github.ref_name || inputs.environment)] }} -``` - -When using in your own repository, reference a specific version: +Basic usage: ```yaml - uses: digital-wisdom/deploy-firebase-python@v1 with: - functions_dir: 'src/functions' # Default is 'functions' - environment: staging - project_id: my-project-staging + project_id: my-firebase-project + service_account_json_b64: ${{ inputs.service_account_b64 }} # Must be base64 encoded ``` -Note: The action requires the following secrets to be defined in your repository: - -- `FIREBASE_STAGING_SERVICE_ACCOUNT`: Service account JSON for staging environment -- `FIREBASE_PROD_SERVICE_ACCOUNT`: Service account JSON for production environment +The action requires the service account JSON to be base64 encoded. How you provide this encoded value is up to your workflow design. ## Inputs -| Input | Description | Required | Default | -| --------------- | ------------------------------------------------- | -------- | ----------- | -| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | -| `environment` | Environment to deploy to (staging/prod) | Yes | N/A | -| `project_id` | Firebase project ID | Yes | N/A | +| Input | Description | Required | Default | +| -------------------------- | ------------------------------------------------- | -------- | ----------- | +| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | +| `to_deploy` | Firebase resource to deploy (e.g. functions) | No | `functions` | +| `project_id` | Firebase project ID | Yes | N/A | +| `service_account_json_b64` | Base64-encoded Firebase service account JSON | Yes | N/A | ## Prerequisites -1. **Firebase Project Setup** - - - Create Firebase projects for your environments - - Generate service account keys - - Store service account JSON in GitHub Secrets - - Store project IDs in GitHub Variables - -2. **Python Project Structure** - - Valid pyproject.toml in your functions directory - - Python version specified in requires-python - - Dependencies listed in project dependencies - -## Project Structure - -Your project should look something like this: - -``` -. -โ”œโ”€โ”€ .github -โ”‚ โ””โ”€โ”€ workflows -โ”‚ โ””โ”€โ”€ deploy.yml -โ”œโ”€โ”€ functions -โ”‚ โ”œโ”€โ”€ main.py -โ”‚ โ”œโ”€โ”€ pyproject.toml -โ”‚ โ””โ”€โ”€ other_files.py -โ””โ”€โ”€ firebase.json -``` +- Firebase service account key +- Python project with pyproject.toml in functions directory ## Environment Variables diff --git a/action.yml b/action.yml index 9bd720c..700d9ca 100644 --- a/action.yml +++ b/action.yml @@ -15,16 +15,12 @@ inputs: description: 'Firebase resource to deploy (e.g. functions, hosting)' required: false default: 'functions' - environment: - description: 'Environment to deploy to' - required: true - type: choice - options: - - staging - - prod project_id: description: 'Firebase project ID' required: true + service_account_json_b64: + description: 'Firebase service account JSON (base64 encoded)' + required: true runs: using: 'composite' @@ -53,16 +49,11 @@ runs: - name: Authenticate with GCP service account key shell: bash - env: - SERVICE_ACCOUNT_JSON: ${{ inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} run: | - echo "$SERVICE_ACCOUNT_JSON" > service-account.json + echo '${{ inputs.service_account_json_b64 }}' | base64 -d | jq '.' > service-account.json echo "GOOGLE_APPLICATION_CREDENTIALS=service-account.json" >> $GITHUB_ENV - - - name: Authenticate GitHub Actions service account - shell: bash - run: | gcloud auth activate-service-account --key-file=service-account.json + rm -f service-account.json - name: Verify service account ADC (Debug) shell: bash @@ -99,7 +90,6 @@ runs: shell: bash run: | echo "### Deployment Summary ๐Ÿš€" >> $GITHUB_STEP_SUMMARY - echo "* **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY echo "* **Project**: ${{ inputs.project_id }}" >> $GITHUB_STEP_SUMMARY echo "* **Resources**: ${{ inputs.to_deploy }}" >> $GITHUB_STEP_SUMMARY echo "* **Functions Directory**: ${{ inputs.functions_dir }}" >> $GITHUB_STEP_SUMMARY From 68166075d8dafa7de7a0f51a2816b2131e8d3020 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Sun, 20 Apr 2025 09:16:10 -0700 Subject: [PATCH 06/11] Try to fix formatting --- .github/workflows/deploy-functions.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index bec907e..3a8dcd5 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -44,11 +44,19 @@ jobs: id: prepare run: | if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then - echo "service_account_b64=$(echo '${{ secrets.FIREBASE_PROD_SERVICE_ACCOUNT }}' | base64)" >> $GITHUB_OUTPUT + sa_json="${{ secrets.FIREBASE_PROD_SERVICE_ACCOUNT }}" else - echo "service_account_b64=$(echo '${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }}' | base64)" >> $GITHUB_OUTPUT + sa_json="${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }}" fi + if [[ -z "$sa_json" ]]; then + echo "::error::Missing service account secret" + exit 1 + fi + + service_account_b64=$(printf '%s' "$sa_json" | base64 | tr -d '\n') + echo "service_account_b64=$service_account_b64" >> $GITHUB_OUTPUT + - name: Deploy Firebase Functions uses: ./ with: From 2485f06d7b5c9a88392f4d04a84662cfc56a6c95 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Tue, 20 May 2025 17:12:34 -0700 Subject: [PATCH 07/11] Use google auth action --- .github/workflows/deploy-functions.yml | 25 ++++++------------------- action.yml | 18 +++++------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index 3a8dcd5..76ef598 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -26,6 +26,10 @@ concurrency: group: deploy-${{ github.ref }} cancel-in-progress: false +env: + PROJECT_ID: ${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} + CREDENTIALS_JSON: ${{ github.event.inputs.environment == 'prod' && secrets.FIREBASE_PROD_SERVICE_ACCOUNT || secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} + jobs: deploy: runs-on: ubuntu-latest @@ -40,25 +44,8 @@ jobs: exit 1 fi - - name: Prepare service account - id: prepare - run: | - if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then - sa_json="${{ secrets.FIREBASE_PROD_SERVICE_ACCOUNT }}" - else - sa_json="${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }}" - fi - - if [[ -z "$sa_json" ]]; then - echo "::error::Missing service account secret" - exit 1 - fi - - service_account_b64=$(printf '%s' "$sa_json" | base64 | tr -d '\n') - echo "service_account_b64=$service_account_b64" >> $GITHUB_OUTPUT - - name: Deploy Firebase Functions uses: ./ with: - project_id: ${{ github.event.inputs.environment == 'prod' && 'hello-wisdom-prod' || 'hello-wisdom-staging' }} - service_account_json_b64: ${{ steps.prepare.outputs.service_account_b64 }} + project_id: ${{ env.PROJECT_ID }} + service_account_json: ${{ env.CREDENTIALS_JSON }} diff --git a/action.yml b/action.yml index 700d9ca..12640f3 100644 --- a/action.yml +++ b/action.yml @@ -18,8 +18,8 @@ inputs: project_id: description: 'Firebase project ID' required: true - service_account_json_b64: - description: 'Firebase service account JSON (base64 encoded)' + service_account_json: + description: 'Firebase service account JSON' required: true runs: @@ -48,12 +48,9 @@ runs: ln -s .venv venv - name: Authenticate with GCP service account key - shell: bash - run: | - echo '${{ inputs.service_account_json_b64 }}' | base64 -d | jq '.' > service-account.json - echo "GOOGLE_APPLICATION_CREDENTIALS=service-account.json" >> $GITHUB_ENV - gcloud auth activate-service-account --key-file=service-account.json - rm -f service-account.json + uses: 'google-github-actions/auth@v2' + with: + credentials_json: '${{ inputs.service_account_json }}' - name: Verify service account ADC (Debug) shell: bash @@ -93,8 +90,3 @@ runs: echo "* **Project**: ${{ inputs.project_id }}" >> $GITHUB_STEP_SUMMARY echo "* **Resources**: ${{ inputs.to_deploy }}" >> $GITHUB_STEP_SUMMARY echo "* **Functions Directory**: ${{ inputs.functions_dir }}" >> $GITHUB_STEP_SUMMARY - - - name: Cleanup credentials - if: always() - shell: bash - run: rm -f service-account.json From bb5c18fa759348bfb85932427560afcea231e6e6 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Tue, 5 Aug 2025 11:32:13 -0700 Subject: [PATCH 08/11] Enable Makefile usage --- .github/workflows/deploy-functions.yml | 1 + CLAUDE.md | 82 ++++++++++++++++++++++++++ README.md | 43 +++++++++++--- action.yml | 16 +++-- 4 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 CLAUDE.md diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index 76ef598..0b8cd37 100644 --- a/.github/workflows/deploy-functions.yml +++ b/.github/workflows/deploy-functions.yml @@ -49,3 +49,4 @@ jobs: with: project_id: ${{ env.PROJECT_ID }} service_account_json: ${{ env.CREDENTIALS_JSON }} + deploy_command: "firebase deploy --only functions" diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c2777c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,82 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Overview + +This is a GitHub Action for deploying Python-based Firebase Cloud Functions. The repository serves dual purposes: +1. GitHub Action implementation (`action.yml`) +2. Live example Firebase function (`functions/`) + +## Key Commands + +### Development Commands +```bash +# Install Python dependencies (in functions/ directory) +cd functions && uv sync + +# Generate requirements.txt for Firebase deployment +cd functions && uv pip freeze > requirements.txt + +# Start Firebase emulators for local development +firebase emulators:start + +# Deploy to Firebase (default) +firebase deploy --only functions + +# Deploy specific functions +firebase deploy --only functions:api,functions:webhook + +# Deploy using make +make deploy-staging + +# Select Firebase project +firebase use +``` + +### Testing and Validation +```bash +# Test the action locally (requires service account) +firebase deploy --only functions --debug + +# List available Firebase projects +firebase projects:list + +# Check Firebase emulator status +firebase emulators:start --only functions,auth +``` + +## Architecture + +### GitHub Action Structure +- `action.yml`: Composite action definition with steps for deployment +- Uses `uv` for Python dependency management +- Integrates with `google-github-actions/auth@v2` for authentication +- Supports configurable deployment commands via `deploy_command` input +- Can execute Firebase CLI commands or make targets + +### Firebase Function Structure +- `functions/main.py`: Simple Flask-based HTTP function +- `functions/pyproject.toml`: Python dependencies (firebase-admin, firebase-functions, flask) +- `firebase.json`: Firebase configuration with Python 3.11 runtime +- Emulator ports: Functions (5001), Auth (9099) + +### Key Dependencies +- Python 3.10-3.11 (Firebase Functions constraint) +- `uv` package manager for dependency management +- Firebase CLI for deployment +- Node.js 20 for Firebase CLI + +## Development Workflow + +1. Modify functions in `functions/main.py` +2. Update dependencies in `functions/pyproject.toml` if needed +3. Run `uv sync` to update lockfile +4. Test locally with Firebase emulators +5. Deploy with `firebase deploy --only functions` + +## Security Notes + +- Service account credentials are handled securely via GitHub Actions +- Credentials are automatically cleaned up after deployment +- Uses Application Default Credentials (ADC) pattern \ No newline at end of file diff --git a/README.md b/README.md index 33a35b7..8d818d8 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,46 @@ Basic usage: - uses: digital-wisdom/deploy-firebase-python@v1 with: project_id: my-firebase-project - service_account_json_b64: ${{ inputs.service_account_b64 }} # Must be base64 encoded + service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} + deploy_command: "firebase deploy --only functions" ``` -The action requires the service account JSON to be base64 encoded. How you provide this encoded value is up to your workflow design. +## Examples + +### Basic Firebase deployment: +```yaml +- uses: digital-wisdom/deploy-firebase-python@v1 + with: + project_id: my-firebase-project + service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} +``` + +### Deploy specific functions: +```yaml +- uses: digital-wisdom/deploy-firebase-python@v1 + with: + project_id: my-firebase-project + service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} + deploy_command: "firebase deploy --only functions:api,functions:webhook" +``` + +### Using make for deployment: +```yaml +- uses: digital-wisdom/deploy-firebase-python@v1 + with: + project_id: my-firebase-project + service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} + deploy_command: "make deploy-staging" +``` ## Inputs -| Input | Description | Required | Default | -| -------------------------- | ------------------------------------------------- | -------- | ----------- | -| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | -| `to_deploy` | Firebase resource to deploy (e.g. functions) | No | `functions` | -| `project_id` | Firebase project ID | Yes | N/A | -| `service_account_json_b64` | Base64-encoded Firebase service account JSON | Yes | N/A | +| Input | Description | Required | Default | +| --------------------- | ------------------------------------------------- | -------- | ------------------------------ | +| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | +| `deploy_command` | Deployment command to execute | No | `firebase deploy --only functions` | +| `project_id` | Firebase project ID | Yes | N/A | +| `service_account_json`| Firebase service account JSON | Yes | N/A | ## Prerequisites diff --git a/action.yml b/action.yml index 12640f3..0687fe7 100644 --- a/action.yml +++ b/action.yml @@ -11,10 +11,10 @@ inputs: description: 'Directory containing functions and pyproject.toml' required: false default: 'functions' - to_deploy: - description: 'Firebase resource to deploy (e.g. functions, hosting)' + deploy_command: + description: 'Deployment command to execute (e.g. "firebase deploy --only functions", "make deploy")' required: false - default: 'functions' + default: 'firebase deploy --only functions' project_id: description: 'Firebase project ID' required: true @@ -25,6 +25,12 @@ inputs: runs: using: 'composite' steps: + - name: Install build tools for make support + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y build-essential + - name: Set up Node.js for Firebase CLI uses: actions/setup-node@v3 with: @@ -81,12 +87,12 @@ runs: - name: Deploy Firebase Resources shell: bash run: | - firebase deploy --only ${{ inputs.to_deploy }} --debug + ${{ inputs.deploy_command }} - name: Post-deployment summary shell: bash run: | echo "### Deployment Summary ๐Ÿš€" >> $GITHUB_STEP_SUMMARY echo "* **Project**: ${{ inputs.project_id }}" >> $GITHUB_STEP_SUMMARY - echo "* **Resources**: ${{ inputs.to_deploy }}" >> $GITHUB_STEP_SUMMARY + echo "* **Deploy Command**: ${{ inputs.deploy_command }}" >> $GITHUB_STEP_SUMMARY echo "* **Functions Directory**: ${{ inputs.functions_dir }}" >> $GITHUB_STEP_SUMMARY From 47bafec11de7b4d30ab9faa6764e6597551b7673 Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Tue, 5 Aug 2025 12:12:41 -0700 Subject: [PATCH 09/11] Update for versatility and my specific make deploy --- README.md | 18 +++++++++++++----- action.yml | 42 ++++++++++++------------------------------ 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 8d818d8..8d5ab8a 100644 --- a/README.md +++ b/README.md @@ -43,24 +43,32 @@ Basic usage: ```yaml - uses: digital-wisdom/deploy-firebase-python@v1 with: - project_id: my-firebase-project service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} deploy_command: "make deploy-staging" ``` +### Custom functions directory: +```yaml +- uses: digital-wisdom/deploy-firebase-python@v1 + with: + service_account_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} + functions_dir: "backend/functions" + project_id: my-firebase-project +``` + ## Inputs | Input | Description | Required | Default | | --------------------- | ------------------------------------------------- | -------- | ------------------------------ | -| `functions_dir` | Directory containing functions and pyproject.toml | No | `functions` | -| `deploy_command` | Deployment command to execute | No | `firebase deploy --only functions` | -| `project_id` | Firebase project ID | Yes | N/A | | `service_account_json`| Firebase service account JSON | Yes | N/A | +| `deploy_command` | Deployment command to execute | No | `firebase deploy --only functions` | +| `project_id` | Firebase project ID (runs `firebase use` if specified) | No | N/A | +| `functions_dir` | Directory with pyproject.toml (creates requirements.txt if missing) | No | `functions` | ## Prerequisites - Firebase service account key -- Python project with pyproject.toml in functions directory +- requirements.txt or pyproject.toml in functions_dir ## Environment Variables diff --git a/action.yml b/action.yml index 0687fe7..5173334 100644 --- a/action.yml +++ b/action.yml @@ -7,20 +7,20 @@ branding: color: 'orange' inputs: - functions_dir: - description: 'Directory containing functions and pyproject.toml' - required: false - default: 'functions' + service_account_json: + description: 'Firebase service account JSON' + required: true deploy_command: - description: 'Deployment command to execute (e.g. "firebase deploy --only functions", "make deploy")' + description: 'Deploym command (e.g. "firebase deploy --only functions", "make deploy")' required: false default: 'firebase deploy --only functions' project_id: - description: 'Firebase project ID' - required: true - service_account_json: - description: 'Firebase service account JSON' - required: true + description: 'Firebase project ID (will firebase use if specified)' + required: false + functions_dir: + description: 'Directory containing functions and pyproject.toml (if requirements.txt is not present, will create with uv)' + required: false + default: 'functions' runs: using: 'composite' @@ -46,6 +46,7 @@ runs: uses: astral-sh/setup-uv@v5 - name: Setup Python from pyproject.toml + if: ${{ inputs.functions_dir && !hashFiles(inputs.functions_dir + '/requirements.txt') }} shell: bash run: | cd ${{ inputs.functions_dir }} @@ -58,28 +59,9 @@ runs: with: credentials_json: '${{ inputs.service_account_json }}' - - name: Verify service account ADC (Debug) - shell: bash - run: | - gcloud config get-value account - gcloud auth list - - - name: Verify Enabled APIs (Debug) - shell: bash - run: | - gcloud services list --enabled --project=${{ inputs.project_id }} - - - name: List Firebase Projects (Debug Mode) - shell: bash - run: firebase projects:list --debug - - - name: Verify Service Account IAM Policy (Debug) - shell: bash - run: | - echo "Verifying IAM policy for project: ${{ inputs.project_id }}" - gcloud projects get-iam-policy ${{ inputs.project_id }} --format=json - name: Select Firebase Project + if: ${{ inputs.project_id }} shell: bash run: | firebase use ${{ inputs.project_id }} --non-interactive From de73be3b58b57a27266bc977cf5462eeb15243cb Mon Sep 17 00:00:00 2001 From: Kelly Johnson Date: Tue, 5 Aug 2025 12:21:36 -0700 Subject: [PATCH 10/11] Correct way to test for requirements.txt file (hopefully) --- action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 5173334..d509d36 100644 --- a/action.yml +++ b/action.yml @@ -46,7 +46,9 @@ runs: uses: astral-sh/setup-uv@v5 - name: Setup Python from pyproject.toml - if: ${{ inputs.functions_dir && !hashFiles(inputs.functions_dir + '/requirements.txt') }} + env: + REQUIREMENTS_PATH: ${{ inputs.functions_dir }}/requirements.txt + if: ${{ !hashFiles(env.REQUIREMENTS_PATH) }} shell: bash run: | cd ${{ inputs.functions_dir }} From 828f14b8e479fd870c46207e0858dbae1bf93b83 Mon Sep 17 00:00:00 2001 From: Angelita Garcia <136642765+aquitzia@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:25:56 -0700 Subject: [PATCH 11/11] fix: typo in action.yml --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index d509d36..ca2f592 100644 --- a/action.yml +++ b/action.yml @@ -11,7 +11,7 @@ inputs: description: 'Firebase service account JSON' required: true deploy_command: - description: 'Deploym command (e.g. "firebase deploy --only functions", "make deploy")' + description: 'Deploy command (e.g. "firebase deploy --only functions", "make deploy")' required: false default: 'firebase deploy --only functions' project_id: