Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  setup_release_automation   Sprache: C

 

#!/usr/bin/env python

# See docs/CI/Release_Automation.md for more details
# Run this from the .signing directory with all the keys and properties files in it.

# python -m venv venv; source venv/bin/activate; pip install requests pynacl

import os
import json
import base64
import argparse
import requests
import nacl.encoding
import nacl.public
import textwrap

PUBLISH_APPROVERS = ["kewisch", "coreycb", "wmontwe"]

CHANNEL_ENVIRONMENTS = {
    "thunderbird_release": {
        "branch": "release",
        "variables": {
            "RELEASE_TYPE": "release",
            "MATRIX_INCLUDE": [
                {
                    "appName": "thunderbird",
                    "releaseTarget": "ftp|github",
                    "packageFormat": "apk",
                    "packageFlavor": "foss",
                },
                {
                    "appName": "thunderbird",
                    "releaseTarget": "play",
                    "playTargetTrack": "internal",
                    "packageFormat": "aab",
                    "packageFlavor": "full",
                },
                {
                    "appName": "k9mail",
                    "releaseTarget": "ftp|github",
                    "packageFormat": "apk",
                    "packageFlavor": "foss",
                },
                {
                    "appName": "k9mail",
                    "releaseTarget": "play",
                    "playTargetTrack": "internal",
                    "packageFormat": "apk",
                    "packageFlavor": "full",
                },
            ],
        },
    },
    "thunderbird_beta": {
        "branch": "beta",
        "variables": {
            "RELEASE_TYPE": "beta",
            "MATRIX_INCLUDE": [
                {
                    "appName": "thunderbird",
                    "releaseTarget": "ftp|github",
                    "packageFormat": "apk",
                    "packageFlavor": "foss",
                },
                {
                    "appName": "thunderbird",
                    "releaseTarget": "play",
                    "playTargetTrack": "internal",
                    "packageFormat": "aab",
                    "packageFlavor": "full",
                },
            ],
        },
    },
    "thunderbird_daily": {
        "branch": "main",
        "variables": {
            "RELEASE_TYPE": "daily",
            "MATRIX_INCLUDE": [
                {
                    "appName": "thunderbird",
                    "releaseTarget": "ftp",
                    "packageFormat": "apk",
                    "packageFlavor": "foss",
                },
                {
                    "appName": "thunderbird",
                    "releaseTarget": "play",
                    "packageFormat": "aab",
                    "playTargetTrack": "internal",
                    "packageFlavor": "full",
                },
            ],
        },
    },
}


SIGNING_ENVIRONMENTS = {
    "k9mail_release_foss": {
        "props": "k9.release.signing.properties",
        "branch": "release",
    },
    "k9mail_release_full": {
        "props": "k9.release.signing.properties",
        "branch": "release",
    },
    "k9mail_beta_foss": {
        "props": "k9.release.signing.properties",
        "branch": "beta",
    },
    "k9mail_beta_full": {
        "props": "k9.release.signing.properties",
        "branch": "beta",
    },
    "thunderbird_daily_foss": {
        "props": "tb.daily.signing.properties",
        "branch": "main",
    },
    "thunderbird_daily_full": {
        "props": "tb.daily.upload.properties",
        "branch": "main",
    },
    "thunderbird_beta_foss": {
        "props": "tb.beta.signing.properties",
        "branch": "beta",
    },
    "thunderbird_beta_full": {
        "props": "tb.beta.upload.properties",
        "branch": "beta",
    },
    "thunderbird_release_foss": {
        "props": "tb.release.signing.properties",
        "branch": "release",
    },
    "thunderbird_release_full": {
        "props": "tb.release.upload.properties",
        "branch": "release",
    },
}


# Function to read the key properties file
def read_key_properties(file_path):
    key_properties = {}
    with open(file_path, "r") as file:
        for line in file:
            if "=" in line:
                key, value = line.strip().split("=", 1)
                final_key = key.split(".")[-1]
                key_properties[final_key] = value
    return key_properties


# Function to base64 encode the .jks file
def encode_jks_file(jks_file_path):
    with open(jks_file_path, "rb") as file:
        encoded_key = base64.b64encode(file.read()).decode("utf-8")
    return encoded_key


# Function to get the public key from GitHub for encryption
def get_github_public_key(repo, environment_name):
    url = f"https://api.github.com/repos/{repo}/environments/{environment_name}/secrets/public-key"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        data = response.json()
        return [data["key_id"], data["key"]]
    else:
        raise Exception(
            f"Failed to fetch public key from GitHub. Response: {response.status_code}, {response.text}"
        )


# Function to encrypt a secret using the GitHub public key
def encrypt_secret(public_key: str, secret_value: str):
    public_key_bytes = base64.b64decode(public_key)
    sealed_box = nacl.public.SealedBox(nacl.public.PublicKey(public_key_bytes))
    encrypted_secret = sealed_box.encrypt(secret_value.encode("utf-8"))
    return base64.b64encode(encrypted_secret).decode("utf-8")


# Function to set encrypted secret in GitHub environment
def set_github_environment_secret(
    repo, secret_name, encrypted_value, key_id, environment_name
):
    url = f"https://api.github.com/repos/{repo}/environments/{environment_name}/secrets/{secret_name}"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    data = {"encrypted_value": encrypted_value, "key_id": key_id}
    response = requests.put(url, headers=headers, json=data)
    if response.status_code == 201:
        print(f"\tSecret {secret_name} created successfully in {environment_name}.")
    elif response.status_code == 204:
        print(f"\tSecret {secret_name} updated successfully in {environment_name}.")
    else:
        raise Exception(
            f"Failed to create secret {secret_name} in {environment_name}. Response: {response.status_code}, {response.text}"
        )


def print_github_environment_variable(repo, environment_name):
    url = (
        f"https://api.github.com/repos/{repo}/environments/{environment_name}/variables"
    )
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    response = requests.get(url, headers=headers)
    data = response.json()

    if response.status_code == 200:
        for variable in data["variables"]:
            value = variable["value"]
            if value[0] in "{[":
                try:
                    value = textwrap.indent(
                        json.dumps(json.loads(value), indent=2), "\t\t"
                    ).lstrip()
                except:
                    pass

            print(f"\t{variable['name']}={value}")
    else:
        raise Exception(
            f"Unexpected response getting variables from {environment_name}: {response.status_code} {response.text}"
        )


def set_github_environment_variable(repo, name, value, environment_name):
    url = (
        f"https://api.github.com/repos/{repo}/environments/{environment_name}/variables"
    )
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    data = {"name": name, "value": value}
    response = requests.post(url, headers=headers, json=data)
    if response.status_code == 201:
        print(f"\tVariable {name} created successfully in {environment_name}.")
    elif response.status_code == 409:
        url = f"https://api.github.com/repos/{repo}/environments/{environment_name}/variables/{name}"
        response = requests.patch(url, headers=headers, json=data)
        if response.status_code == 204:
            print(f"\tVariable {name} updated successfully in {environment_name}.")
        else:
            raise Exception(
                f"Failed to update variable {name} in {environment_name}. Response: {response.status_code}, {response.text}"
            )
    else:
        raise Exception(
            f"Failed to create variable {name} in {environment_name}. Response: {response.status_code}, {response.text}"
        )


def print_github_environment(repo, environment_name):
    url = f"https://api.github.com/repos/{repo}/environments/{environment_name}"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        data = response.json()
        print(f"Environment {environment_name}")
        print("\tProtection rules")
        needs_branch_policies = False
        for rule in data["protection_rules"]:
            if rule["type"] == "branch_policy":
                continue

            print(f"\t\tType: {rule['type']}")
            if rule["type"] == "required_reviewers":
                reviewers = ", ".join(
                    map(
                        lambda reviewer: reviewer["reviewer"]["login"],
                        rule["reviewers"],
                    )
                )
                print(f"\t\t\tReviewers: {reviewers}")

        print(f"\t\tBranch policy: {data['deployment_branch_policy']}")
        if (
            data["deployment_branch_policy"]
            and data["deployment_branch_policy"]["custom_branch_policies"]
        ):
            url += "/deployment-branch-policies"
            response = requests.get(url, headers=headers)
            if response.status_code == 200:
                policies = map(
                    lambda policy: policy["name"], response.json()["branch_policies"]
                )
                print("\t\tBranches: " + ", ".join(policies))

    else:
        raise Exception(
            f"Unexpected response getting variables from {environment_name}: {response.status_code} {response.text}"
        )


# Function to create GitHub environment if it doesn't exist
def create_github_environment(repo, environment_name, branches=None, approvers=None):
    url = f"https://api.github.com/repos/{repo}/environments/{environment_name}"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    data = {}
    if branches:
        data["deployment_branch_policy"] = {
            "custom_branch_policies": True,
            "protected_branches": False,
        }

    if approvers:
        reviewers = map(
            lambda approver: {
                "type": "User",
                "id": get_user_id_from_username(approver),
            },
            approvers,
        )
        data["reviewers"] = list(reviewers)

    response = requests.put(url, headers=headers, json=data)
    if response.status_code == 200:
        print(f"Environment {environment_name} created successfully.")
    elif response.status_code == 409:
        print(f"Environment {environment_name} already exists.")
    else:
        raise Exception(
            f"Failed to create environment {environment_name}. Response: {response.status_code}, {response.text}"
        )

    for branch in branches or []:
        url = f"https://api.github.com/repos/{repo}/environments/{environment_name}/deployment-branch-policies"
        data = {"name": branch, "type": "branch"}
        response = requests.post(url, headers=headers, json=data)

        if response.status_code == 200:
            print(
                f"\tBranch protection on {branch} for {environment_name} created successfully."
            )
        elif response.status_code == 409:
            print(
                f"\tBranch protection on {branch} for {environment_name} already exists."
            )
        else:
            raise Exception(
                f"Failed to create branch protection for {branch} on {environment_name}. Response: {response.status_code}, {response.text}"
            )


# Function to get the GitHub user ID from a username
def get_user_id_from_username(username):
    url = f"https://api.github.com/users/{username}"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        user_data = response.json()
        return user_data["id"]
    else:
        print(
            f"Failed to fetch user ID for username '{username}'. Response: {response.status_code}, {response.text}"
        )
        return None


def create_approver_environment(repo, environment_name, approvers):

    reviewers = map(
        lambda approver: {"type": "User", "id": get_user_id_from_username(approver)},
        approvers,
    )

    url = f"https://api.github.com/repos/{repo}/environments/{environment_name}"
    headers = {
        "Authorization": f"token {GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json",
    }
    data = {"reviewers": list(reviewers)}
    response = requests.put(url, headers=headers, json=data)
    if response.status_code == 200:
        print(f"Environment {environment_name} created successfully.")
    elif response.status_code == 409:
        print(f"Environment {environment_name} already exists.")
    else:
        raise Exception(
            f"Failed to create environment {environment_name}. Response: {response.status_code}, {response.text}"
        )


def create_signing_environment(repo, environment, branch, props_file):
    # Read the key.properties file
    key_props = read_key_properties(props_file)

    KEY_ALIAS = key_props.get("keyAlias")
    KEY_PASSWORD = key_props.get("keyPassword")
    KEY_STORE_PASSWORD = key_props.get("storePassword")

    if not all([KEY_ALIAS, KEY_PASSWORD, KEY_STORE_PASSWORD]):
        print(
            "Missing values in key.properties file. Please ensure all fields are present."
        )
        return

    # Base64 encode the JKS file to create SIGNING_KEY
    SIGNING_KEY = encode_jks_file(key_props.get("storeFile"))

    # Create the environment if it doesn't exist
    create_github_environment(repo, environment, branches=[branch])

    # Fetch the public key from GitHub for the specific environment
    key_id, public_key = get_github_public_key(repo, environment)

    # Encrypt the secrets using the public key
    encrypted_signing_key = encrypt_secret(public_key, SIGNING_KEY)
    encrypted_key_alias = encrypt_secret(public_key, KEY_ALIAS)
    encrypted_key_password = encrypt_secret(public_key, KEY_PASSWORD)
    encrypted_key_store_password = encrypt_secret(public_key, KEY_STORE_PASSWORD)

    # Set the encrypted secrets in the GitHub environment
    secrets_to_set = {
        "SIGNING_KEY": encrypted_signing_key,
        "KEY_ALIAS": encrypted_key_alias,
        "KEY_PASSWORD": encrypted_key_password,
        "KEY_STORE_PASSWORD": encrypted_key_store_password,
    }

    for secret_name, encrypted_value in secrets_to_set.items():
        set_github_environment_secret(
            repo, secret_name, encrypted_value, key_id, environment
        )


def make_bot_environment(repo, environment):
    key_id, public_key = get_github_public_key(repo, environment)

    with open("botmobile.key.pem") as fp:
        encrypted_bot_key = encrypt_secret(public_key, fp.read())
    with open("botmobile.clientid.txt") as fp:
        bot_client_id = fp.read().strip()
    with open("botmobile.userid.txt") as fp:
        bot_user_id = fp.read().strip()


    set_github_environment_secret(
        repo, "BOT_PRIVATE_KEY", encrypted_bot_key, key_id, environment
    )

    set_github_environment_variable(repo, "BOT_CLIENT_ID", bot_client_id, environment)
    set_github_environment_variable(repo, "BOT_USER_ID", bot_user_id, environment)


def create_channel_environment(repo, environment, branch, variables):
    create_github_environment(repo, environment, branches=[branch])

    for name, value in variables.items():
        if isinstance(value, dict) or isinstance(value, list):
            value = json.dumps(value)

        set_github_environment_variable(repo, name, value, environment)


def create_release_environment(repo, branches):
    environment = "publish_release"

    create_github_environment(repo, environment, branches=branches)

    key_id, public_key = get_github_public_key(repo, environment)

    with open("play-store-account.json") as fp:
        encrypted_play_account = encrypt_secret(public_key, fp.read())

    set_github_environment_secret(
        repo, "PLAY_STORE_ACCOUNT", encrypted_play_account, key_id, environment
    )


def create_matrix_environment(repo, branches):
    environment = "notify_matrix"

    create_github_environment(repo, environment, branches=branches)

    key_id, public_key = get_github_public_key(repo, environment)

    with open("matrix-account.json") as fp:
        mxdata = json.load(fp)
        encrypted_token = encrypt_secret(public_key, mxdata["token"])

    set_github_environment_secret(
        repo, "MATRIX_NOTIFY_TOKEN", encrypted_token, key_id, environment
    )

    set_github_environment_variable(
        repo, "MATRIX_NOTIFY_HOMESERVER", mxdata["homeserver"], environment
    )
    set_github_environment_variable(
        repo, "MATRIX_NOTIFY_ROOM", mxdata["room"], environment
    )
    set_github_environment_variable(
        repo, "MATRIX_NOTIFY_USER_MAP", json.dumps(mxdata["userMap"]), environment
    )


def main():
    # Argument parsing for positional inputs and repo flag
    parser = argparse.ArgumentParser(
        description="Set GitHub environment secrets for specific or all environments."
    )
    parser.add_argument(
        "--repo",
        "-r",
        required=True,
        help="GitHub repository in the format 'owner/repo'.",
    )
    parser.add_argument(
        "--print", "-p", action="store_true", help="Print instead of set"
    )
    parser.add_argument(
        "--skip", "-s", action="append", help="Skip this named environment"
    )
    parser.add_argument(
        "--only", "-o", action="append", help="Only include this named environment"
    )

    args = parser.parse_args()

    global GITHUB_TOKEN
    GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
    if not GITHUB_TOKEN:
        raise Exception(
            "GITHUB_TOKEN environment variable is not set. Please set it before running the script."
        )

    if args.skip and args.only:
        print("Error: Cannot supply both skip and only")
        return

    includeset = set(
        list(CHANNEL_ENVIRONMENTS.keys())
        + list(SIGNING_ENVIRONMENTS.keys())
        + ["publish_hold", "publish_release", "notify_matrix", "botmobile"]
    )
    if args.skip:
        for skip in args.skip:
            includeset.remove(skip)

    if args.only:
        includeset = set(args.only)

    # Publish hold environment
    if "publish_hold" in includeset:
        if args.print:
            print_github_environment(args.repo, "publish_hold")
        else:
            create_github_environment(
                args.repo, "publish_hold", approvers=PUBLISH_APPROVERS
            )

    # Channel environments
    for environment_name, data in CHANNEL_ENVIRONMENTS.items():
        if environment_name not in includeset:
            continue

        if args.print:
            print(f"Environment {environment_name}")
            print_github_environment_variable(args.repo, environment_name)
        else:
            create_channel_environment(args.repo, environment_name, **data)
            make_bot_environment(args.repo, environment_name)

    # Signing environments
    for environment_name, data in SIGNING_ENVIRONMENTS.items():
        if environment_name not in includeset:
            continue

        if args.print:
            print_github_environment(args.repo, environment_name)
        else:
            if not os.path.exists(data["props"]):
                print(f"Skipping {environment_name}: Missing key .properties file")
                continue

            create_signing_environment(
                args.repo, environment_name, data["branch"], data["props"]
            )

    # Publish environment
    if "publish_release" in includeset:
        if args.print:
            print_github_environment(args.repo, "publish_release")
        else:
            create_release_environment(args.repo, ["main", "beta", "release"])
            make_bot_environment(args.repo, "publish_release")

    # Botmobile environment
    if "botmobile" in includeset:
        if args.print:
            print_github_environment(args.repo, "botmobile")
        else:
            create_github_environment(args.repo, "botmobile", branches=["main"])
            make_bot_environment(args.repo, "botmobile")

    # Notify
    if "notify_matrix" in includeset:
        if args.print:
            print_github_environment(args.repo, "notify_matrix")
        else:
            create_matrix_environment(args.repo, ["main", "beta", "release"])


if __name__ == "__main__":
    main()

Messung
CHZG
839410092

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104848
#Domains=416966