Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 84 additions & 4 deletions scripts/release/homebrew/docker/formula_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import requests
import jinja2
from poet.poet import make_graph, RESOURCE_TEMPLATE
from collections import OrderedDict
import bisect
import argparse

TEMPLATE_FILE_NAME='formula_template.txt'
CLI_VERSION=os.environ['CLI_VERSION']
Expand All @@ -21,6 +24,24 @@
def main():
print('Generate formular for Azure CLI homebrew release.')

parser = argparse.ArgumentParser(prog='formula_generator.py')
parser.set_defaults(func=generate_formula)
parser.add_argument('-b', dest='build_method', choices=['update_existing', 'use_template'], help='The build method, default is update_existing, the other option is use_template.')
args = parser.parse_args()
args.func(**vars(args))

def generate_formula(build_method: str, **_):
content = ''
if build_method is None or build_method == 'update_existing':
content = update_formula()
elif build_method == 'use_template':
content = generate_formula_with_template()
with open('azure-cli.rb', mode='w') as fq:
fq.write(content)


def generate_formula_with_template() -> str:
"""Generate a brew formula by using a template"""
template_path = os.path.join(os.path.dirname(__file__), TEMPLATE_FILE_NAME)
with open(template_path, mode='r') as fq:
template_content = fq.read()
Expand All @@ -35,17 +56,16 @@ def main():
)
if not content.endswith('\n'):
content += '\n'
return content

with open('azure-cli.rb', mode='w') as fq:
fq.write(content)

def compute_sha256(resource_url: str) -> str:
import hashlib
sha256 = hashlib.sha256()
resp = requests.get(resource_url)
resp.raise_for_status()
sha256.update(resp.content)

return sha256.hexdigest()


Expand All @@ -60,6 +80,12 @@ def collect_resources() -> str:
return '\n\n'.join(nodes_render)


def collect_resources_dict() -> dict:
nodes = make_graph('azure-cli')
filtered_nodes = {nodes[node_name]['name']: nodes[node_name] for node_name in sorted(nodes) if resource_filter(node_name)}
return filtered_nodes


def resource_filter(name: str) -> bool:
# TODO remove need for any filters and delete this method.
return not name.startswith('azure-cli') and name not in ('futures', 'jeepney', 'entrypoints')
Expand All @@ -83,9 +109,63 @@ def last_bottle_hash():
if 'bottle do' in content:
start = idx
look_for_end = True

return '\n'.join(lines[start: end + 1])


def update_formula() -> str:
"""Generate a brew formula by updating the existing one"""
nodes = collect_resources_dict()

resp = requests.get(HOMEBREW_FORMULAR_LATEST)
resp.raise_for_status()
text = resp.text

# update url, version and sha256 of azure-cli
text = re.sub('url ".*"', 'url "{}"'.format(HOMEBREW_UPSTREAM_URL), text, 1)
text = re.sub('version ".*"', 'version "{}"'.format(CLI_VERSION), text, 1)
upstream_sha = compute_sha256(HOMEBREW_UPSTREAM_URL)
text = re.sub('sha256 ".*"', 'sha256 "{}"'.format(upstream_sha), text, 1)
text = re.sub('.*revision.*\n', '', text, 1) # remove revision for previous version if exists
pack = None
packs_to_remove = set()
lines = text.split('\n')
node_index_dict = OrderedDict()
for idx, line in enumerate(lines):
if line.strip().startswith("resource"):
m = re.search(r'resource "(.*)" do', line)
if m is not None:
pack = m.group(1)
node_index_dict[pack] = idx
elif pack is not None:
if line.strip().startswith("url"):
#update the url of package
if pack in nodes.keys():
lines[idx] = re.sub('url ".*"', 'url "{}"'.format(nodes[pack]['url']), line, 1)
else:
packs_to_remove.add(pack)
elif line.strip().startswith("sha256"):
#update the sha256 of package
if pack in nodes.keys():
lines[idx] = re.sub('sha256 ".*"', 'sha256 "{}"'.format(nodes[pack]['checksum']), line, 1)
del nodes[pack]
pack = None
elif line.strip().startswith('def install'):
if nodes:
# add new dependency packages
for node_name, node in nodes.items():
# find the right place to insert the new resource per alphabetic order
i = bisect.bisect_left(list(node_index_dict.keys()), node_name)
line_idx = list(node_index_dict.items())[i][1]
resource = RESOURCE_TEMPLATE.render(resource=node)
lines[line_idx] = resource + '\n\n' + lines[line_idx]
new_text = "\n".join(lines)

# remove dependency packages that are no longer needed
for pack in packs_to_remove:
new_text = re.sub(r'resource "{}" do.*?\n end\n\s+'.format(pack), '', new_text, flags=re.DOTALL)
return new_text


if __name__ == '__main__':
main()