docs: def add_lines(self, *lines, **kwargs):
          """
          Add lines to the beginning or end of the build.
          :param lines: one or more lines to add to the content, by default at the end.
          :param all_stages: bool for whether to add in all stages for a multistage build
                             or (by default) only the last.
          :param at_start: adds at the beginning (after FROM) of the stage(s) instead of the end.
          :param skip_scratch: skip stages which use "FROM scratch"
          """
          assert len(lines) > 0
          lines = [_endline(line) for line in lines]
          all_stages = kwargs.pop('all_stages', False)
          at_start = kwargs.pop('at_start', False)
          skip_scratch = kwargs.pop('skip_scratch', False)
          assert not kwargs, "Unknown keyword argument(s): {0}".format(kwargs.keys())
  
          froms = [
              instr for instr in self.structure
              if instr['instruction'] == 'FROM'
          ] or [{'endline': -1}]  # no FROM? fake one before the beginning
          if not all_stages:  # only modify the last
              froms = [froms[-1]]
  
          df_lines = self.lines
          # make sure last line has a newline if lines are to be appended
          if df_lines and not at_start:
              df_lines[-1] = _endline(df_lines[-1])
  
          # iterate through the stages in reverse order
          # so adding lines doesn't invalidate line numbers from structure dicts.
          # first add a bogus instruction to represent EOF in our iteration.
          froms.append({'startline': len(df_lines) + 1})
          for stage in range(len(froms)-2, -1, -1):  # e.g. 0 for single or 2, 1, 0 for 3 stages
              start, finish = froms[stage], froms[stage+1]
              linenum = start['endline'] + 1 if at_start else finish['startline']
              if skip_scratch and froms[stage]['value'] == 'scratch':
                  continue
              df_lines[linenum:linenum] = lines
  
          self.lines = df_lines