Loading

Paste #pwn4vt8bg

  1. #!/usr/bin/env python3
  2. """
  3. # Make program executable
  4. chmod u+x code.py
  5.  
  6. # Make it known under various names
  7. ln -s code.py get-version
  8. ln -s code.py get-tag
  9. ln -s code.py new-release-major
  10. ln -s code.py new-release-minor
  11. ln -s code.py new-release-build
  12. ln -s code.py new-dev
  13.  
  14. get-version:       Get the newest version at or below the current revision.
  15. get-number:        Get the number related to a version.
  16. new-release-major: Invent a new major release version
  17. new-release-minor: Invent a new minor release version
  18. new-release-build: Invent a new build release version
  19. new-dev:           Invent a new development version
  20. """
  21. import sys, os, subprocess, re
  22.  
  23. def exe_cmd(cmd, env=None):
  24.     """
  25.    Execute a shell command.
  26.  
  27.    @param cmd: List of command-line arguments.
  28.    @param env: Environment variables to set (dict of key/value pairs)
  29.    @return: Lines of output.
  30.    @note: If command fails, program is aborted.
  31.    """
  32.     if dump_commands:
  33.         print("CMD: {}".format(" ".join(escape_name(arg) for arg in cmd)))
  34.  
  35.     if env is not None:
  36.         new_env = os.environ.copy()
  37.         new_env.update(env)
  38.     else:
  39.         new_env = None
  40.  
  41.  
  42.     lines = output.splitlines()
  43.     return lines
  44.  
  45. def get_git_tag(match=None):
  46.     """
  47.    Query git for the current-label, return prefix + label if possible.
  48.    """
  49.     cmd = ['git', 'describe']
  50.     if match is not None:
  51.         cmd.append('--match=' + match)
  52.  
  53.     if sys.version_info.minor < 5:
  54.         # Pre 3.5 has no subprocess.run
  55.         output = subprocess.check_output(cmd, universal_newlines=True)
  56.     else:
  57.         output = subprocess.run(cmd, check=True, stdout=subprocess.PIPE,
  58.                                 universal_newlines=True).stdout
  59.  
  60.     lines = [line for line in output.splitlines() if line]
  61.     if len(lines) != 1:
  62.         return None
  63.     line = lines[0]
  64.     # Either the current commit is a tag, or it is a commit above a tag.
  65.     parts = line.split('-')
  66.     label = parse_tag(parts[-1])
  67.     if label:
  68.         return "-".join(parts[:-1]), label
  69.  
  70.     if len(parts) > 1:
  71.         label = parse_tag(parts[-2])
  72.         if label:
  73.             return "-".join(parts[:-2]), label
  74.  
  75.     if len(parts) > 2:
  76.         label = parse_tag(parts[-3])
  77.         if label:
  78.             return "-".join(parts[:-3]), label
  79.  
  80.     return None, None
  81.  
  82. def get_git_label(match=None):
  83.     prefix, label = get_git_tag(match)
  84.     return label
  85.  
  86.  
  87. NUMBITS_MAJOR = 4
  88. NUMBITS_MINOR = 8
  89. NUMBITS_RELEASE = 1
  90. NUMBITS_BUILD = 31 - NUMBITS_RELEASE - NUMBITS_MINOR - NUMBITS_MAJOR
  91.  
  92. MAX_MAJOR = 2 ** NUMBITS_MAJOR - 1
  93. MAX_MINOR = 2 ** NUMBITS_MINOR - 1
  94. MAX_RELEASE = 2 ** NUMBITS_RELEASE - 1
  95. MAX_BUILD = 2 ** NUMBITS_BUILD - 1
  96.  
  97. assert NUMBITS_BUILD > 0
  98.  
  99. def good_version(major, minor, release, build):
  100.     """
  101.    Verify whether the 4 provided numbers are within boundaries of a version number.
  102.    """
  103.     if major < 0 or major > MAX_MAJOR: return False
  104.     if minor < 0 or minor > MAX_MINOR: return False
  105.     if release < 0 or release > MAX_RELEASE: return False
  106.     if build < 0 or build > MAX_BUILD: return False
  107.     return True
  108.  
  109. class VersionLabel:
  110.     """
  111.    Object holding a version. Constructor crashes if an incorret number
  112.    is supplied, check against 'good_version' first.
  113.    """
  114.     def __init__(self, major, minor, release, build):
  115.         assert good_version(major, minor, release, build)
  116.         self.major = major
  117.         self.minor = minor
  118.         self.release = release
  119.         self.build = build
  120.  
  121.     def to_tag(self, all_numbers=False):
  122.         """
  123.        Convert version label instance to tag text.
  124.        """
  125.         parts = []
  126.         if self.release == 0:
  127.             parts.append('dev')
  128.         else:
  129.             parts.append('rel')
  130.         parts.append(str(self.major))
  131.  
  132.         if not all_numbers and self.minor == 0 and self.build == 0:
  133.             return "".join(parts)
  134.  
  135.         parts.extend(['.', str(self.minor)])
  136.         if not all_numbers and self.build == 0:
  137.             return "".join(parts)
  138.  
  139.         parts.extend(['.', str(self.build)])
  140.         return "".join(parts)
  141.  
  142.     def to_id(self):
  143.         """
  144.        Convert version label instance to numeric id.
  145.        """
  146.         value = self.build
  147.         shift = NUMBITS_BUILD
  148.  
  149.         value = value + (self.release << shift)
  150.         shift = shift + NUMBITS_RELEASE
  151.  
  152.         value = value + (self.minor << shift)
  153.         shift = shift + NUMBITS_MINOR
  154.  
  155.         value = value + (self.major << shift)
  156.         # 'value' is at most 31 bits, so always fits in a DWORD.
  157.         return value
  158.  
  159.     def next_major(self):
  160.         assert self.release == 1
  161.         if self.major >= MAX_MAJOR:
  162.             return None
  163.         return VersionLabel(self.major + 1, 0, 1, 0)
  164.  
  165.     def next_minor(self):
  166.         assert self.release == 1
  167.         if self.minor >= MAX_MINOR:
  168.             return None
  169.         return VersionLabel(self.major, self.minor + 1, 1, 0)
  170.  
  171.     def next_build(self):
  172.         if self.build >= MAX_BUILD:
  173.             return None
  174.         return VersionLabel(self.major, self.minor, self.release, self.build + 1)
  175.  
  176.     def next_dev(self):
  177.         if self.release == 0:
  178.             return self.next_build()
  179.         else:
  180.             label = self.next_minor()
  181.             label.release = 0
  182.             return label
  183.  
  184. TAG_PATTERN = re.compile("(rel|dev)([0-9]+)(?:\\.([0-9]+)(?:\\.([0-9]+))?)?$")
  185.  
  186. def parse_tag(text):
  187.     """
  188.    Convert a tag text like "rel1.3.25" to a VersionLabel if allowed.
  189.    """
  190.     m = TAG_PATTERN.search(text)
  191.     if m:
  192.         if m.group(1) == 'rel':
  193.             release = 1
  194.         else:
  195.             release = 0
  196.  
  197.         major = int(m.group(2))
  198.  
  199.         if m.group(3) is not None:
  200.             minor = int(m.group(3))
  201.         else:
  202.             minor = 0
  203.  
  204.         if m.group(4) is not None:
  205.             build = int(m.group(4))
  206.         else:
  207.             build = 0
  208.  
  209.         if good_version(major, minor, release, build):
  210.             return VersionLabel(major, minor, release, build)
  211.     return None
  212.  
  213. def parse_id(value):
  214.     """
  215.    Convert a numeric id to a VersionLabel, if allowed.
  216.    """
  217.     build = value & MAX_BUILD
  218.     value = value >> NUMBITS_BUILD
  219.  
  220.     release = value & MAX_RELEASE
  221.     value = value >> NUMBITS_RELEASE
  222.  
  223.     minor = value & MAX_MINOR
  224.     value = value >> NUMBITS_MINOR
  225.  
  226.     major = value & MAX_MAJOR
  227.     if good_version(major, minor, release, build):
  228.         return VersionLabel(major, minor, release, build)
  229.     return None
  230.  
  231. #
  232. # get-version: Get the newest version at or below the current revision.
  233. # get-tag: Get the current tag (so it can be checked out)
  234. # get-number: Get the number related to a version.
  235. # new-release-major: Invent a new major release version
  236. # new-release-minor: Invent a new minor release version
  237. # new-release-build: Invent a new build release version
  238. # new-dev: Invent a new development version
  239.  
  240. # Dirty hacky way to get several 'binaries' in the same program
  241. if sys.argv[0].endswith('get-version'):
  242.     label = get_git_label()
  243.     if label is None:
  244.         print("Cannot find a git tag with a proper name")
  245.         sys.exit(1)
  246.     print(label.to_id())
  247.     sys.exit(0)
  248.  
  249. elif sys.argv[0].endswith('get-tag'):
  250.     prefix, label = get_git_tag()
  251.     if prefix is None:
  252.         print("Cannot find a git tag with a proper name")
  253.         sys.exit(1)
  254.     print(prefix + "-" + label.to_tag())
  255.     sys.exit(0)
  256.  
  257. elif sys.argv[0].endswith('get-label'):
  258.     label = get_git_label()
  259.     if label is None:
  260.         print("Cannot find a git tag with a proper name")
  261.         sys.exit(1)
  262.     print(label.to_tag())
  263.     sys.exit(0)
  264.  
  265. elif sys.argv[0].endswith('new-release-major'):
  266.     prefix, label = get_git_tag("*-rel[0-9]*")
  267.     if label is None:
  268.         print("Cannot find a git tag with a proper name")
  269.         sys.exit(1)
  270.     label = label.next_major()
  271.     if label is None:
  272.         print("Cannot create a new major release version, maximum major number reached")
  273.         sys.exit(1)
  274.     print(prefix + "-" + label.to_tag())
  275.     sys.exit(0)
  276.  
  277. elif sys.argv[0].endswith('new-release-minor'):
  278.     prefix, label = get_git_tag("*-rel[0-9]*")
  279.     if label is None:
  280.         print("Cannot find a git tag with a proper name")
  281.         sys.exit(1)
  282.     label = label.next_minor()
  283.     if label is None:
  284.         print("Cannot create a new major release version, maximum minor number reached")
  285.         sys.exit(1)
  286.     print(prefix + "-" + label.to_tag())
  287.     sys.exit(0)
  288.  
  289. elif sys.argv[0].endswith('new-release-build'):
  290.     prefix, label = get_git_tag("*-rel[0-9]*")
  291.     if label is None:
  292.         print("Cannot find a git tag with a proper name")
  293.         sys.exit(1)
  294.     label = label.next_build()
  295.     if label is None:
  296.         print("Cannot create a new major release version, maximum build number reached")
  297.         sys.exit(1)
  298.     print(prefix + "-" + label.to_tag())
  299.     sys.exit(0)
  300.  
  301. elif sys.argv[0].endswith('new-dev'):
  302.     prefix, label = get_git_tag()
  303.     if label is None:
  304.         print("Cannot find a git tag with a proper name")
  305.         sys.exit(1)
  306.     label = label.next_dev()
  307.     if label is None:
  308.         print("Cannot create a new development version, maximum minor number reached")
  309.         sys.exit(1)
  310.     print(prefix + "-" + label.to_tag())
  311.     sys.exit(0)
  312.  
  313. elif sys.argv[0].endswith('get-info'):
  314.     print("Version: \"[rel|dev][major].[minor].[build]\"")
  315.     print("Min/max value of major: 0..{}".format(MAX_MAJOR))
  316.     print("Min/max value of minor: 0..{}".format(MAX_MINOR))
  317.     print("Min/max value of build: 0..{}".format(MAX_BUILD))
  318.     sys.exit(0)
  319.  
  320. else:
  321.     print("Unknown executable name, bye!")
  322.     sys.exit(1)

Comments