Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
628
scripts/ci/setup_release_automation
Normal file
628
scripts/ci/setup_release_automation
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
#!/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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue