55import os
66import re
77import logging
8- import docker
8+ import string
99import sys
10+ import hashlib
11+ import escapism
1012import xml .etree .ElementTree as ET
1113
1214from traitlets import Dict
@@ -534,6 +536,17 @@ def render(self):
534536 "RUN {}" .format (textwrap .dedent (script .strip ("\n " )))
535537 )
536538
539+ # Based on a physical location of a build script on the host,
540+ # create a mapping between:
541+ # 1. Location of a build script in a Docker build context
542+ # ('assemble_files/<escaped-file-path-truncated>-<6-chars-of-its-hash>')
543+ # 2. Location of the aforemention script in the Docker image
544+ # Base template basically does: COPY <1.> <2.>
545+ build_script_files = {
546+ self .generate_build_context_filename (k )[0 ]: v
547+ for k , v in self .get_build_script_files ().items ()
548+ }
549+
537550 return t .render (
538551 packages = sorted (self .get_packages ()),
539552 path = self .get_path (),
@@ -544,13 +557,42 @@ def render(self):
544557 preassemble_script_files = self .get_preassemble_script_files (),
545558 preassemble_script_directives = preassemble_script_directives ,
546559 assemble_script_directives = assemble_script_directives ,
547- build_script_files = self . get_build_script_files () ,
560+ build_script_files = build_script_files ,
548561 base_packages = sorted (self .get_base_packages ()),
549562 post_build_scripts = self .get_post_build_scripts (),
550563 start_script = self .get_start_script (),
551564 appendix = self .appendix ,
552565 )
553566
567+ @staticmethod
568+ def generate_build_context_filename (src_path , hash_length = 6 ):
569+ """
570+ Generate a filename for a file injected into the Docker build context.
571+
572+ In case the src_path is relative, it's assumed it's relative to directory of
573+ this __file__. Returns the resulting filename and an absolute path to the source
574+ file on host.
575+ """
576+ if not os .path .isabs (src_path ):
577+ src_parts = src_path .split ("/" )
578+ src_path = os .path .join (os .path .dirname (__file__ ), * src_parts )
579+
580+ src_path_hash = hashlib .sha256 (src_path .encode ("utf-8" )).hexdigest ()
581+ safe_chars = set (string .ascii_letters + string .digits )
582+
583+ def escape (s ):
584+ return escapism .escape (s , safe = safe_chars , escape_char = "-" )
585+
586+ src_path_slug = escape (src_path )
587+ filename = "build_script_files/{name}-{hash}"
588+ return (
589+ filename .format (
590+ name = src_path_slug [: 255 - hash_length - 20 ],
591+ hash = src_path_hash [:hash_length ],
592+ ).lower (),
593+ src_path ,
594+ )
595+
554596 def build (
555597 self ,
556598 client ,
@@ -580,9 +622,8 @@ def _filter_tar(tar):
580622 return tar
581623
582624 for src in sorted (self .get_build_script_files ()):
583- src_parts = src .split ("/" )
584- src_path = os .path .join (os .path .dirname (__file__ ), * src_parts )
585- tar .add (src_path , src , filter = _filter_tar )
625+ dest_path , src_path = self .generate_build_context_filename (src )
626+ tar .add (src_path , dest_path , filter = _filter_tar )
586627
587628 tar .add (ENTRYPOINT_FILE , "repo2docker-entrypoint" , filter = _filter_tar )
588629
0 commit comments