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
@@ -509,6 +511,17 @@ def render(self):
509511 "RUN {}" .format (textwrap .dedent (script .strip ("\n " )))
510512 )
511513
514+ # Based on a physical location of a build script on the host,
515+ # create a mapping between:
516+ # 1. Location of a build script in a Docker build context
517+ # ('assemble_files/<escaped-file-path-truncated>-<6-chars-of-its-hash>')
518+ # 2. Location of the aforemention script in the Docker image
519+ # Base template basically does: COPY <1.> <2.>
520+ build_script_files = {
521+ self .generate_build_context_filename (k )[0 ]: v
522+ for k , v in self .get_build_script_files ().items ()
523+ }
524+
512525 return t .render (
513526 packages = sorted (self .get_packages ()),
514527 path = self .get_path (),
@@ -518,13 +531,42 @@ def render(self):
518531 build_script_directives = build_script_directives ,
519532 pre_assemble_script_directives = pre_assemble_script_directives ,
520533 assemble_script_directives = assemble_script_directives ,
521- build_script_files = self . get_build_script_files () ,
534+ build_script_files = build_script_files ,
522535 base_packages = sorted (self .get_base_packages ()),
523536 post_build_scripts = self .get_post_build_scripts (),
524537 start_script = self .get_start_script (),
525538 appendix = self .appendix ,
526539 )
527540
541+ @staticmethod
542+ def generate_build_context_filename (src_path , hash_length = 6 ):
543+ """
544+ Generate a filename for a file injected into the Docker build context.
545+
546+ In case the src_path is relative, it's assumed it's relative to directory of
547+ this __file__. Returns the resulting filename and an absolute path to the source
548+ file on host.
549+ """
550+ if not os .path .isabs (src_path ):
551+ src_parts = src_path .split ("/" )
552+ src_path = os .path .join (os .path .dirname (__file__ ), * src_parts )
553+
554+ src_path_hash = hashlib .sha256 (src_path .encode ("utf-8" )).hexdigest ()
555+ safe_chars = set (string .ascii_letters + string .digits )
556+
557+ def escape (s ):
558+ return escapism .escape (s , safe = safe_chars , escape_char = "-" )
559+
560+ src_path_slug = escape (src_path )
561+ filename = "build_script_files/{name}-{hash}"
562+ return (
563+ filename .format (
564+ name = src_path_slug [: 255 - hash_length - 20 ],
565+ hash = src_path_hash [:hash_length ],
566+ ).lower (),
567+ src_path ,
568+ )
569+
528570 def build (
529571 self ,
530572 client ,
@@ -554,9 +596,8 @@ def _filter_tar(tar):
554596 return tar
555597
556598 for src in sorted (self .get_build_script_files ()):
557- src_parts = src .split ("/" )
558- src_path = os .path .join (os .path .dirname (__file__ ), * src_parts )
559- tar .add (src_path , src , filter = _filter_tar )
599+ dest_path , src_path = self .generate_build_context_filename (src )
600+ tar .add (src_path , dest_path , filter = _filter_tar )
560601
561602 tar .add (ENTRYPOINT_FILE , "repo2docker-entrypoint" , filter = _filter_tar )
562603
0 commit comments