Skip to content

Instantly share code, notes, and snippets.

@anshajk
Created January 24, 2021 16:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anshajk/2ac022116732d7e2c0b3f9a66a0841c4 to your computer and use it in GitHub Desktop.
Save anshajk/2ac022116732d7e2c0b3f9a66a0841c4 to your computer and use it in GitHub Desktop.
Continuous deployment using GitHub Actions for Firebase Remote Config
name: remote-config-deployer
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
deploy:
runs-on: ubuntu-latest
needs: build
if: success() && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Publish
env:
PROJECT_ID: ${{ secrets.PROJECT_ID }}
CREDENTIALS: ${{ secrets.CREDENTIALS }}
run: |
python remote_config_manager.py --action=publish
import argparse
import requests
import io
from oauth2client.service_account import ServiceAccountCredentials
import json
import os
PROJECT_ID = os.getenv("PROJECT_ID")
CREDENTIALS = json.loads(os.getenv("CREDENTIALS"))
BASE_URL = "https://firebaseremoteconfig.googleapis.com"
REMOTE_CONFIG_ENDPOINT = "v1/projects/" + PROJECT_ID + "/remoteConfig"
REMOTE_CONFIG_URL = BASE_URL + "/" + REMOTE_CONFIG_ENDPOINT
SCOPES = ["https://www.googleapis.com/auth/firebase.remoteconfig"]
# [START retrieve_access_token]
def _get_access_token():
"""Retrieve a valid access token that can be used to authorize requests.
:return: Access token.
"""
credentials = ServiceAccountCredentials.from_json_keyfile_dict(CREDENTIALS, SCOPES)
access_token_info = credentials.get_access_token()
return access_token_info.access_token
# [END retrieve_access_token]
def _get(save=False):
"""Retrieve the current Firebase Remote Config template from server.
Retrieve the current Firebase Remote Config template from server and store it
locally.
"""
headers = {"Authorization": "Bearer " + _get_access_token()}
resp = requests.get(REMOTE_CONFIG_URL, headers=headers)
if save != False and resp.status_code == 200:
with io.open("config.json", "wb") as f:
f.write(resp.text.encode("utf-8"))
print("Retrieved template has been written to config.json")
return resp.headers["ETag"]
def _listVersions():
"""Print the last 5 Remote Config version's metadata."""
headers = {"Authorization": "Bearer " + _get_access_token()}
resp = requests.get(REMOTE_CONFIG_URL + ":listVersions?pageSize=5", headers=headers)
if resp.status_code == 200:
print("Versions:")
print(resp.text)
else:
print("Request to print template versions failed.")
print(resp.text)
def _rollback(version):
"""Roll back to an available version of Firebase Remote Config template.
:param version: The version of the template to roll back to.
"""
headers = {"Authorization": "Bearer " + _get_access_token()}
json = {"version_number": version}
resp = requests.post(REMOTE_CONFIG_URL + ":rollback", headers=headers, json=json)
if resp.status_code == 200:
print("Rolled back to version: " + version)
print(resp.text)
print("ETag from server: {}".format(resp.headers["ETag"]))
else:
print("Request to roll back to version " + version + " failed.")
print(resp.text)
def _publish():
"""Publish local template to Firebase server.
Args:
etag: ETag for safe (avoid race conditions) template updates.
* can be used to force template replacement.
"""
etag = _get()
with open("remoteconfig.template.json", "r", encoding="utf-8") as f:
content = f.read()
headers = {
"Authorization": "Bearer " + _get_access_token(),
"Content-Type": "application/json; UTF-8",
"If-Match": etag,
}
resp = requests.put(
REMOTE_CONFIG_URL, data=content.encode("utf-8"), headers=headers
)
if resp.status_code == 200:
print("Template has been published.")
print("ETag from server: {}".format(resp.headers["ETag"]))
else:
print("Unable to publish template.")
print(resp.text)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--action")
parser.add_argument("--etag")
parser.add_argument("--version")
args = parser.parse_args()
if args.action and args.action == "get":
_get()
elif args.action and args.action == "publish":
_publish()
elif args.action and args.action == "versions":
_listVersions()
elif args.action and args.action == "rollback" and args.version:
_rollback(args.version)
else:
print(
"""Invalid command. Please use one of the following commands:
python configure.py --action=get
python configure.py --action=publish
python configure.py --action=versions
python configure.py --action=rollback --version=<TEMPLATE_VERSION_NUMBER>"""
)
if __name__ == "__main__":
main()
oauth2client
requests
python-dotenv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment