#!/usr/bin/env python3 import argparse import logging import re def update_gitlab_ci_include(filename, include_project, new_revision): """Update the include statement in a gitlab-ci yml to a given revision Parameters: filename( string): The path to the file to change. include_project( string): The path used to reference the project the include points to. new_revision (string): The hex sha to set the include to. Returns: True if the file was changed. """ # Set the possible include in the local .gitlab.yml file # to the new revision. The include needs to have the revision # specified directly in the file, as it is parsed before the # submodule checkout is done # Use custom read write method as I didn't got ruamel yaml # to keep all costum formating (linebreaks ...) # This assumes following format of the include block # include: # - project: 'SECO-Northern-Europe/yocto/infrastructure/gitlab-ci' # ref: c5a3793e783fcb364c7f3bda73e8cd7c08a08804 # file: 'manifest-childs.yml' # Verify hash format: if re.match(r"\A[0-9a-fA-F]{40}\Z", new_revision) is None: raise TypeError("Format of specified revision is not correct") parsestate = 0 changed = False # Remove the SECO-Northern-Europe part from the priject filter # as it is normally specified by $CI_PROJECT_ROOT_NAMESPACE include_project = include_project.split("/", 1)[1] logging.debug("Include project: %s", include_project) logging.debug("New revision: %s", new_revision) with open(filename, "r+", encoding="UTF-8") as fp: while True: linestart = fp.tell() line = fp.readline() parts = line.partition(":") logging.debug("Splitted input line: %s", parts) if len(line) == 0: break # End of file if parsestate == 0: if parts[0] == "include": # Found include block parsestate = 1 logging.debug("Found 'include' block at %d", linestart) elif parsestate == 1: if parts[0] == "\n": break # End of include block if ( parts[0].endswith(" - project") and parts[2].find(include_project) >= 0 ): # Found the correct project parsestate = 2 logging.debug("Found 'project' entry at %d", linestart) elif parsestate == 2: if parts[0].endswith(" ref"): # Found the ref: entry, compare the revision logging.debug("Found 'ref' entry at %d", linestart) parsestate = 1 if parts[2].find(new_revision) >= 0: print( "Revision in {} is already set to {}".format( filename, new_revision ) ) else: print( "Changed revision in {} to {}".format( filename, new_revision ) ) fp.seek(linestart) fp.write("{}: {}".format(parts[0], new_revision)) fp.flush() changed = True elif parts[0].find("- ") >= 0 or not parts[0].startswith(" "): # Format of the line is not 'name: value' assume end of block # Block was not found break return changed def main(): parser = argparse.ArgumentParser() parser.add_argument( "--filename", help="""File to change""", required=True, ) parser.add_argument( "--include-project", help="""The path to the included project as used in 'filename'""", required=True, ) parser.add_argument( "--revision", help="""new revision for submodule""", required=True, ) parser.add_argument( "-v", "--verbose", action="store_true", help="""Increase verbosity.""", ) args, _ = parser.parse_known_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) update_gitlab_ci_include(args.filename, args.include_project, args.revision) if __name__ == "__main__": main()