diff --git a/.github/workflows/deploy-functions.yml b/.github/workflows/deploy-functions.yml index 89d0e6d..f65cc01 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,29 @@ 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 - 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) + - name: Prepare service account key + id: prepare 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 + if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then + service_account_b64="${{ secrets.FIREBASE_PROD_SERVICE_ACCOUNT_B64 }}" + else + service_account_b64="${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT_B64 }}" + fi - - 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 + if [[ -z "$service_account_b64" ]]; then + echo "::error::Missing service account secret" + exit 1 + fi - - 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 + if ! echo "$service_account_b64" | base64 --decode > /dev/null 2>&1; then + echo "::error::Service account secret is not valid base64. Please ensure it is base64-encoded before storing as a secret." + exit 1 fi - - name: Deploy Firebase Functions (Debug Mode) - run: firebase deploy --only functions --debug + echo "service_account_b64=$service_account_b64" >> $GITHUB_OUTPUT - - 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 + - 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 }} diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 0d1bf14..272930e 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -1,16 +1,19 @@ -name: Lint PR Title +name: "Lint PR" on: - pull_request: - types: [opened, edited, synchronize, reopened] - -permissions: - pull-requests: 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: @@ -33,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 diff --git a/README.md b/README.md index f95eabc..33a35b7 100644 --- a/README.md +++ b/README.md @@ -4,111 +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 }} - 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)] }} -``` - -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 - service_account_json: ${{ secrets.FIREBASE_STAGING_SERVICE_ACCOUNT }} - project_id: my-project-staging + project_id: my-firebase-project + service_account_json_b64: ${{ inputs.service_account_b64 }} # Must be base64 encoded ``` +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 | -| `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` | +| `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 d50ed09..700d9ca 100644 --- a/action.yml +++ b/action.yml @@ -15,19 +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 - service_account_json: - description: 'Firebase service account JSON' - required: true project_id: description: 'Firebase project ID' required: true + service_account_json_b64: + description: 'Firebase service account JSON (base64 encoded)' + required: true runs: using: 'composite' @@ -57,13 +50,10 @@ runs: - name: Authenticate with GCP service account key shell: bash run: | - echo "${{ inputs.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 @@ -100,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