Skip to content

Instantly share code, notes, and snippets.

@reinhrst
Created February 15, 2021 15:48
Show Gist options
  • Select an option

  • Save reinhrst/6bac4497f06efc3198fc0bb4d9e29a03 to your computer and use it in GitHub Desktop.

Select an option

Save reinhrst/6bac4497f06efc3198fc0bb4d9e29a03 to your computer and use it in GitHub Desktop.
GetStack helper file
# Run as "python getstack.py #filename#", it prints a filename on stdout that contains
# the stack in #filename# with some edits
# Useful in a commandline such as:
# aws cloudformation update-stack --stack-name MyStack --template-body file://$(python getstack.py stack/stack.yaml) --parameters ParameterKey=XXXX,ParameterValue=YYYY --capabilities CAPABILITY_IAM
# The following tags are supported
# Note that all filenames used are relative to the location of the file that contains
# the filename
# All files are expected to have utf-8 encoding
#
# ---- __INCLUDE__: #includefilename# ----
# Should appear on a line by itself
# the #includefilename# will be included in this script (recursively processed itself)
# and indented by the number of spaces before the __INCLUDE__:
# Note that a number may follow the __INCLUDE__ in order to make the tags unique
# This is not a requirement but some YAML editors will complain if you have two
# __INCLUDE__ keys at the same level, so you can use __INCLUDE__1, __INCLUDE__2, etc
#
# --- !!TIMESTAMP!! --
# This string is replaced by an identifierfriendly timestamp (e.g. 20210103T120354Z)
# There is a guarantee that the same timestamp is used everywhere in the file
from __future__ import annotations
import datetime
import logging
import pathlib
import re
import tempfile
logger = logging.getLogger()
IMPORT_RE = re.compile(
r"^(?P<indent> +)__INCLUDE__\d*: (?P<filename>.+)$", re.MULTILINE)
def importfile(indent: int, filename: pathlib.Path) -> str:
assert filename.is_absolute()
data = process_file(filename)
lines = [(" " * indent) + line if line else "" for line in data.split("\n")]
return "\n".join(lines)
def process_file(filename: pathlib.Path) -> str:
logger.info('Processing %s', filename)
assert filename.is_absolute()
filepath = filename.parent
data = filename.read_text(encoding="utf-8")
return IMPORT_RE.sub(
lambda match: importfile(
len(match.group("indent")),
(filepath / match.group("filename")).resolve()),
data)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("filename")
args = parser.parse_args()
absolute_filename = pathlib.Path(args.filename).resolve()
processed_stack = process_file(absolute_filename)
processed_stack = processed_stack.replace(
"!!TIMESTAMP!!", datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))
tmpfile = tempfile.NamedTemporaryFile(
mode="wt", encoding="utf-8", suffix="-" + absolute_filename.name, delete=False)
tmpfile.write(processed_stack)
logger.info("Wrote %s", tmpfile.name)
print(tmpfile.name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment