utils.release
release.py
Common code useful for release pipelines.
Example Usage: from utils import release from utils.release import get_new_build_release as _version
1#!/usr/bin/env python3 2""" 3release.py 4 5Common code useful for release pipelines. 6 7Example Usage: 8 from utils import release 9 from utils.release import get_new_build_release as _version 10""" 11 12import datetime 13# from subprocess_tee import run as _run 14import os 15import sys 16from subprocess_tee import run as _run 17try: 18 import loggy 19except ImportError: 20 from utils import loggy 21try: 22 from .common import resolve_gocd_pipeline_variable 23except ImportError: 24 from utils.common import resolve_gocd_pipeline_variable 25 26 27from enum import Enum 28 29 30class Release(Enum): 31 DEV = "develop" 32 DEVELOP = "develop" 33 QA = "qa" 34 STAGE = "staging" 35 STAGING = "staging" 36 DEMO = "demo" 37 PROD = "master" 38 HOTFIX = "hotfix" 39 40 41def get_semver(): 42 """ 43 get_semver() 44 45 Generates a new build version string using Semantic Versioning using environment variables. 46 47 Uses the following Environment variables to generate the version string: 48 49 * VERSION_MAJOR 50 * VERSION_MINOR 51 * GO_PIPELINE_COUNTER 52 53 Returns: None if environment variables are not set 54 """ 55 if os.environ.get('VERSION_MAJOR') and os.environ.get('VERSION_MINOR') and os.environ.get('GO_PIPELINE_COUNTER'): 56 return f"{os.environ.get('VERSION_MAJOR')}.{os.environ.get('VERSION_MINOR')}.{os.environ.get('GO_PIPELINE_COUNTER')}" 57 58 return None 59 60 61def get_version(): 62 """ 63 get_version() 64 65 Generates a new build version string using `get_new_build_release()` 66 67 NOTE: This is the `default` version format for ALL of our backend versioning. 68 69 """ 70 return get_new_build_release() 71 72 73def get_new_build_release(): 74 """ 75 get_new_build_release() 76 77 Generates a new build version string in the format: YY.MM.DD.GO_PIPELINE_COUNTER 78 79 returns: String 80 """ 81 now = datetime.datetime.now().strftime("%y.%m.%d.") 82 version = f"{now}{os.environ.get('GO_PIPELINE_COUNTER', '0')}" 83 loggy.info(f"release.get_new_build_release(): Generated build: {version}") 84 return version 85 86 87def _check_for_multiple_materials(): 88 """ 89 _check_for_multiple_materials() 90 91 Checks for and exits if multiple materials in a GoCD pipeline are found. 92 We currently do not support this in our release functions. 93 94 returns: False 95 """ 96 counter = sum(x.startswith('GO_REVISION_') for x in os.environ.keys()) 97 98 if counter > 1: 99 loggy.error("release.get_commit_short_hash(): Error: (" + counter + ") Materials found in Env. We only support 1 Material right now.") 100 sys.exit(1) 101 102 return False 103 104 105def get_commit_short_hash(): 106 """ 107 get_commit_short_hash() 108 109 Grabs the git commit hash from ENV vars in a GoCD pipeline and returns the first 8 chars. 110 111 returns: String 112 """ 113 _check_for_multiple_materials() 114 return next(val for key, val in os.environ.items() if key.startswith('GO_REVISION_'))[0:8] 115 116 117def get_source_branch(): 118 """ 119 get_source_branch() 120 121 Grabs the git branch from ENV vars in a GoCD pipeline 122 123 returns: String 124 """ 125 _check_for_multiple_materials() 126 try: 127 return next(val for key, val in os.environ.items() if key.startswith('GO_MATERIAL_BRANCH')) 128 except StopIteration as e: 129 loggy.error(f"release.get_source_branch(): No branches found. GO_MATERIAIL_BRANCH_x env vars not found. Is this a GoCD pipeline? {str(e)}") 130 return None 131 132 133def get_last_tag(): 134 """ 135 get_last_tag() 136 137 Grabs the latest tag in a git repo. 138 139 returns: String or None 140 """ 141 _run("git fetch --all --tags", check=False, shell=True) 142 latest_tag = _run("git for-each-ref refs/tags --sort=-taggerdate --count=1 --format=\"%(refname)\"", check=False, shell=True) 143 144 resolved = latest_tag.stdout.strip().split('/')[-1] if latest_tag else None 145 146 loggy.info(f"release.get_last_tag(): Returning latest tag as: {resolved}") 147 return resolved 148 149 150def git_promote(version=None, source=None, dest=None, tag=None) -> bool: 151 """ 152 git_promote() 153 154 Git promote a release from one branch to another. 155 156 version: Defaults to `get_new_build_release()`, which generates a version 157 string in the following format: YYYY.MM.BUILD_NUMBER 158 159 source: Defaults to `develop` 160 161 dest: Defaults to `qa` 162 163 tag: Default is None. If set, we will tag the release version. 164 """ 165 source_branch = 'develop' if source is None else resolve_gocd_pipeline_variable(source) 166 dest_branch = 'qa' if dest is None else resolve_gocd_pipeline_variable(dest) 167 version = get_new_build_release() if version is None else resolve_gocd_pipeline_variable(version) 168 promote_branch = f"promote/{version}" 169 170 _run("git status", check=False, shell=True) 171 _run("git config -l", check=False, shell=True) 172 _skip_merge = False 173 174 try: 175 _run(f"git checkout {dest_branch}", check=True, shell=True) 176 _run(f"git pull origin {dest_branch}", check=True, shell=True) 177 except Exception as e: 178 # dest branch doesn't exist. creating it 179 loggy.error("release.git_promote(): Exception: " + str(e)) 180 _run(f"git checkout -b {dest_branch}", check=True, shell=True) 181 _run(f"git push origin {dest_branch}", check=True, shell=True) 182 183 _skip_merge = True 184 pass 185 186 if not _skip_merge: 187 _run(f"git checkout {source_branch}", check=True, shell=True) 188 _run(f"git pull origin {source_branch}", check=True, shell=True) 189 190 try: 191 loggy.info(f"release.git_promote(): Creating temp release branch {promote_branch}") 192 _run(f"git checkout -b {promote_branch}", check=True, shell=True) 193 except Exception as e: 194 # branch probably already exists... 195 loggy.error("release.git_promote(): Exception: " + str(e)) 196 _run(f"git branch -D {promote_branch}", check=True, shell=True) 197 _run(f"git checkout -b {promote_branch}", check=True, shell=True) 198 pass 199 200 _run(f"git merge --strategy=ours --no-edit {dest_branch}", check=True, shell=True) 201 _run(f"git checkout {dest_branch}", check=True, shell=True) 202 _run(f"git merge --squash {promote_branch}", check=True, shell=True) 203 204 try: 205 output = _run(f"git commit -m \"GoCD merge {source_branch} to {dest_branch} for promotion {version}\"", check=False, shell=True) 206 207 if output.returncode != 0: 208 if 'nothing to commit' in output.stderr or 'nothing to commit' in output.stdout: 209 loggy.info("release.git_commit(): Nothing to commit. Skipping push and proceeding as success.") 210 else: 211 loggy.error("release.git_commit(): Git commit failure. Exiting. now") 212 loggy.error(f"release.git_promote(): {output.stdout}") 213 loggy.error(f"release.git_promote(): {output.stderr}") 214 loggy.error(f"release.git_promote(): {str(output.returncode)}") 215 raise Exception(output.stderr) 216 else: 217 _run(f"git push origin {dest_branch}", check=True, shell=True) 218 except Exception as e: 219 loggy.error(f"release.git_promot(): {str(e)}") 220 return False 221 222 if 'master' in dest_branch: 223 version = get_new_build_release() if tag is None else tag 224 _run(f"git tag -f -a {version} -m \"Rev to version {version}\"", check=True, shell=True) 225 _run(f"git push origin {version}", check=True, shell=True) 226 227 return True
31class Release(Enum): 32 DEV = "develop" 33 DEVELOP = "develop" 34 QA = "qa" 35 STAGE = "staging" 36 STAGING = "staging" 37 DEMO = "demo" 38 PROD = "master" 39 HOTFIX = "hotfix"
An enumeration.
Inherited Members
- enum.Enum
- name
- value
42def get_semver(): 43 """ 44 get_semver() 45 46 Generates a new build version string using Semantic Versioning using environment variables. 47 48 Uses the following Environment variables to generate the version string: 49 50 * VERSION_MAJOR 51 * VERSION_MINOR 52 * GO_PIPELINE_COUNTER 53 54 Returns: None if environment variables are not set 55 """ 56 if os.environ.get('VERSION_MAJOR') and os.environ.get('VERSION_MINOR') and os.environ.get('GO_PIPELINE_COUNTER'): 57 return f"{os.environ.get('VERSION_MAJOR')}.{os.environ.get('VERSION_MINOR')}.{os.environ.get('GO_PIPELINE_COUNTER')}" 58 59 return None
get_semver()
Generates a new build version string using Semantic Versioning using environment variables.
Uses the following Environment variables to generate the version string:
- VERSION_MAJOR
- VERSION_MINOR
- GO_PIPELINE_COUNTER
Returns: None if environment variables are not set
62def get_version(): 63 """ 64 get_version() 65 66 Generates a new build version string using `get_new_build_release()` 67 68 NOTE: This is the `default` version format for ALL of our backend versioning. 69 70 """ 71 return get_new_build_release()
get_version()
Generates a new build version string using get_new_build_release()
NOTE: This is the default
version format for ALL of our backend versioning.
74def get_new_build_release(): 75 """ 76 get_new_build_release() 77 78 Generates a new build version string in the format: YY.MM.DD.GO_PIPELINE_COUNTER 79 80 returns: String 81 """ 82 now = datetime.datetime.now().strftime("%y.%m.%d.") 83 version = f"{now}{os.environ.get('GO_PIPELINE_COUNTER', '0')}" 84 loggy.info(f"release.get_new_build_release(): Generated build: {version}") 85 return version
get_new_build_release()
Generates a new build version string in the format: YY.MM.DD.GO_PIPELINE_COUNTER
returns: String
106def get_commit_short_hash(): 107 """ 108 get_commit_short_hash() 109 110 Grabs the git commit hash from ENV vars in a GoCD pipeline and returns the first 8 chars. 111 112 returns: String 113 """ 114 _check_for_multiple_materials() 115 return next(val for key, val in os.environ.items() if key.startswith('GO_REVISION_'))[0:8]
get_commit_short_hash()
Grabs the git commit hash from ENV vars in a GoCD pipeline and returns the first 8 chars.
returns: String
118def get_source_branch(): 119 """ 120 get_source_branch() 121 122 Grabs the git branch from ENV vars in a GoCD pipeline 123 124 returns: String 125 """ 126 _check_for_multiple_materials() 127 try: 128 return next(val for key, val in os.environ.items() if key.startswith('GO_MATERIAL_BRANCH')) 129 except StopIteration as e: 130 loggy.error(f"release.get_source_branch(): No branches found. GO_MATERIAIL_BRANCH_x env vars not found. Is this a GoCD pipeline? {str(e)}") 131 return None
get_source_branch()
Grabs the git branch from ENV vars in a GoCD pipeline
returns: String
134def get_last_tag(): 135 """ 136 get_last_tag() 137 138 Grabs the latest tag in a git repo. 139 140 returns: String or None 141 """ 142 _run("git fetch --all --tags", check=False, shell=True) 143 latest_tag = _run("git for-each-ref refs/tags --sort=-taggerdate --count=1 --format=\"%(refname)\"", check=False, shell=True) 144 145 resolved = latest_tag.stdout.strip().split('/')[-1] if latest_tag else None 146 147 loggy.info(f"release.get_last_tag(): Returning latest tag as: {resolved}") 148 return resolved
get_last_tag()
Grabs the latest tag in a git repo.
returns: String or None
151def git_promote(version=None, source=None, dest=None, tag=None) -> bool: 152 """ 153 git_promote() 154 155 Git promote a release from one branch to another. 156 157 version: Defaults to `get_new_build_release()`, which generates a version 158 string in the following format: YYYY.MM.BUILD_NUMBER 159 160 source: Defaults to `develop` 161 162 dest: Defaults to `qa` 163 164 tag: Default is None. If set, we will tag the release version. 165 """ 166 source_branch = 'develop' if source is None else resolve_gocd_pipeline_variable(source) 167 dest_branch = 'qa' if dest is None else resolve_gocd_pipeline_variable(dest) 168 version = get_new_build_release() if version is None else resolve_gocd_pipeline_variable(version) 169 promote_branch = f"promote/{version}" 170 171 _run("git status", check=False, shell=True) 172 _run("git config -l", check=False, shell=True) 173 _skip_merge = False 174 175 try: 176 _run(f"git checkout {dest_branch}", check=True, shell=True) 177 _run(f"git pull origin {dest_branch}", check=True, shell=True) 178 except Exception as e: 179 # dest branch doesn't exist. creating it 180 loggy.error("release.git_promote(): Exception: " + str(e)) 181 _run(f"git checkout -b {dest_branch}", check=True, shell=True) 182 _run(f"git push origin {dest_branch}", check=True, shell=True) 183 184 _skip_merge = True 185 pass 186 187 if not _skip_merge: 188 _run(f"git checkout {source_branch}", check=True, shell=True) 189 _run(f"git pull origin {source_branch}", check=True, shell=True) 190 191 try: 192 loggy.info(f"release.git_promote(): Creating temp release branch {promote_branch}") 193 _run(f"git checkout -b {promote_branch}", check=True, shell=True) 194 except Exception as e: 195 # branch probably already exists... 196 loggy.error("release.git_promote(): Exception: " + str(e)) 197 _run(f"git branch -D {promote_branch}", check=True, shell=True) 198 _run(f"git checkout -b {promote_branch}", check=True, shell=True) 199 pass 200 201 _run(f"git merge --strategy=ours --no-edit {dest_branch}", check=True, shell=True) 202 _run(f"git checkout {dest_branch}", check=True, shell=True) 203 _run(f"git merge --squash {promote_branch}", check=True, shell=True) 204 205 try: 206 output = _run(f"git commit -m \"GoCD merge {source_branch} to {dest_branch} for promotion {version}\"", check=False, shell=True) 207 208 if output.returncode != 0: 209 if 'nothing to commit' in output.stderr or 'nothing to commit' in output.stdout: 210 loggy.info("release.git_commit(): Nothing to commit. Skipping push and proceeding as success.") 211 else: 212 loggy.error("release.git_commit(): Git commit failure. Exiting. now") 213 loggy.error(f"release.git_promote(): {output.stdout}") 214 loggy.error(f"release.git_promote(): {output.stderr}") 215 loggy.error(f"release.git_promote(): {str(output.returncode)}") 216 raise Exception(output.stderr) 217 else: 218 _run(f"git push origin {dest_branch}", check=True, shell=True) 219 except Exception as e: 220 loggy.error(f"release.git_promot(): {str(e)}") 221 return False 222 223 if 'master' in dest_branch: 224 version = get_new_build_release() if tag is None else tag 225 _run(f"git tag -f -a {version} -m \"Rev to version {version}\"", check=True, shell=True) 226 _run(f"git push origin {version}", check=True, shell=True) 227 228 return True
git_promote()
Git promote a release from one branch to another.
version: Defaults to get_new_build_release()
, which generates a version
string in the following format: YYYY.MM.BUILD_NUMBER
source: Defaults to develop
dest: Defaults to qa
tag: Default is None. If set, we will tag the release version.