
    QO                     L   S r SSKrSSKrSSKrSSKrSSKrSSKJr  SrSr	Sr
SrSrS	rS
r " S S\5      r\R"                  " S5      r\R"                  " S5      rS rS rS r " S S\5      rS r\S4S jrS rS rS rS#S jr  S$S jrS r S r!S r"S r#S r$S  r%S! r&S" r'g)%z7The implementation of generating a source context file.    N)
six_subsetzremote\.(.*)\.urlzt^https://(?P<hostname>[^/]*)/(?P<id_type>p|id)/(?P<project_or_repo_id>[^/?#]+)(/r/(?P<repo_name>[^/?#]+))?([/#?].*)?zL^# *(Untracked files|Changes to be committed|Changes not staged for commit):captureremote_repozsource-context.jsonzsource-contexts.jsonc                   0    \ rS rSrSrSrSrSrSrSr	Sr
S	rg
)_ContextType2   a%  Ordered enumeration of context types.

The ordering is based on which context information will provide the best
user experience. Higher numbers are considered better than lower numbers.
Google repositories have the highest ranking because they do not require
additional authorization to view.
r                   N)__name__
__module____qualname____firstlineno____doc__OTHERGIT_UNKNOWNGIT_KNOWN_HOST_SSHGIT_KNOWN_HOST
CLOUD_REPOSOURCE_CAPTURE__static_attributes__r       2lib/googlecloudsdk/appengine/tools/context_util.pyr   r   2   s2     % +  . * .r   r   z^(?P<protocol>\w+):z+^\w+://([^/]*[.@])?(?P<domain>\w+\.\w+)[/:]c                    U (       d  [         R                  $ [        R                  U 5      (       d  SU -   n [        R                  U 5      n[        R                  U 5      R                  S5      nU(       aY  UR                  S5      nUS:X  a  [         R                  $ US:X  d  US:X  a&  US:X  a  [         R                  $ [         R                  $ [         R                  $ )z/Returns the context type for the input Git url.zssh://protocoldomainz
google.comz
github.comzbitbucket.orgssh)	r   r   _PROTOCOL_PATTERNmatch_DOMAIN_PATTERNgroupr   r   r   )urldomain_matchr   r   s       r   _GetGitContextTypeFromDomainr'   R   s     
###		 	 	%	%
S.C &&s+,$$S)//
;()F$$$	<	6_#<	U	...***		!	!!r   c                    UR                  S5      [        :X  a  [        R                  $ U R                  S5      nU(       a  [	        UR                  S5      5      $ SU ;   a  [        R
                  $ [        R                  $ )zReturns the _ContextType for the input extended source context.

Args:
  context: A source context dict.
  labels: A dict containing the labels associated with the context.
Returns:
  The context type.
categorygitr%   	cloudRepo)getCAPTURE_CATEGORYr   r   r'   r   r   )contextlabelsgit_contexts      r   _GetContextTyper1   h   sg     ZZ
//&&&E"+'(>??G"""			r   c                 B    U (       a  US:X  a  gU(       a  U S:X  a  gX:  $ )a  Indicates if a new remote is better than an old one, based on remote name.

Names are ranked as follows: If either name is "origin", it is considered
best, otherwise the name that comes last alphabetically is considered best.

The alphabetical ordering is arbitrary, but it was chosen because it is
stable. We prefer "origin" because it is the standard name for the origin
of cloned repos.

Args:
  new_name: The name to be evaluated.
  old_name: The name to compare against.
Returns:
  True iff new_name should replace old_name.
originFTr   )new_nameold_names     r   _IsRemoteBetterr6   {   s'      
X)	X)		r   c                       \ rS rSrSrSrg)GenerateSourceContextError   z<An error occurred while trying to create the source context.r   N)r   r   r   r   r   r   r   r   r   r8   r8      s    Dr   r8   c                 T    U R                  S0 5      R                  SS 5      [        :H  $ )Nr/   r)   )r,   r-   )r.   s    r   IsCaptureContextr;      s&    	Xr	"	&	&z4	8<L	LLr   c                 (    SU0nU(       a  X#S'   XS.$ )aE  Converts a source context dict to an ExtendedSourceContext dict.

Args:
  context: A SourceContext-compatible dict
  category:  string indicating the category of context (either
      CAPTURE_CATEGORY or REMOTE_REPO_CATEGORY)
  remote_name: The name of the remote in git.
Returns:
  An ExtendedSourceContext-compatible dict.
r)   remote_name)r.   r/   r   )r.   r)   r=   r/   s       r   ExtendContextDictr>      s"     !&'=	//r   c                 j    [        U S5      n[        R                  " [        U[        R                  S9$ )zChecks if the git repo in a directory has any pending changes.

Args:
  source_directory: The path to directory containing the source code.
Returns:
  True if there are any uncommitted or untracked changes in the local repo
  for the given directory.
status)flags)_CallGitresearch_GIT_PENDING_CHANGE_PATTERN	MULTILINE)source_directoryr@   s     r   HasPendingChangesrH      s.     $h/&	.
' 'r   c                 @   [        U 5      nU(       d  [        SU -  5      e[        U 5      nU(       d  [        SU -  5      e/ nUR                  5        H2  u  pE[	        XEU5      nU(       d  M  Xc;  d  M!  UR                  U5        M4     U(       d  [        SU -  5      eU$ )a  Generate extended source contexts for a directory.

Scans the remotes and revision of the git repository at source_directory,
returning one or more ExtendedSourceContext-compatible dictionaries describing
the repositories.

Currently, this function will return only the Google-hosted repository
associated with the directory, if one exists.

Args:
  source_directory: The path to directory containing the source code.
Returns:
  One or more ExtendedSourceContext-compatible dictionaries describing
  the remote repository or repositories associated with the given directory.
Raises:
  GenerateSourceContextError: if source context could not be generated.
z4Could not list remote URLs from source directory: %sz:Could not find HEAD revision from the source directory: %szICould not find any repository in the remote URLs for source directory: %s)_GetGitRemoteUrlsr8   _GetGitHeadRevisionitems_ParseSourceContextappend)rG   remote_urlssource_revisionsource_contextsr=   
remote_urlsource_contexts          r   CalculateExtendedSourceContextsrT      s    ( ""23+	
$>	 
 ((89/	
$D	 
 /!,!2!2!4k(2N
 ~.?^, "5 

$	*	+, , 
r   c                     SnSnSnU  Hb  nUS   nUR                  S0 5      n[        XV5      nU(       a  Xr:  a  M3  UR                  S5      nXr:X  a  [        UU5      (       d  M\  UnUnUnMd     U$ )aV  Returns the "best" source context from a list of contexts.

"Best" is a heuristic that attempts to define the most useful context in
a Google Cloud Platform application. The most useful context is defined as:

1. The capture context, if there is one. (I.e., a context with category
   'capture')
2. The Cloud Repo context, if there is one.
3. A repo context from another known provider (i.e. github or bitbucket), if
   there is no Cloud Repo context.
4. The generic git repo context, if not of the above apply.

If there are two Cloud Repo contexts and one of them is a "capture" context,
that context is considered best.

If two Git contexts come from the same provider, they will be evaluated based
on remote name: "origin" is the best name, followed by the name that comes
last alphabetically.

If all of the above does not resolve a tie, the tied context that is
earliest in the source_contexts list wins.

Args:
  source_contexts: A list of extended source contexts.
Returns:
  A single source context, or None if source_contexts is empty.
Raises:
  KeyError if any extended source context is malformed.
Nr.   r/   r=   )r,   r1   r6   )	rQ   rS   	best_typebest_remote_nameext_ctx	candidater/   context_typer=   s	            r   BestSourceContextr[      s    < .) g	"I[[2&F"95L \-**]+K 9I*K *KN"I !  
r   c                 v   ^ U(       d  [        U=(       d    U 5      nU(       d  / mO[        X5      /mU4S jnU$ )a  Returns a function to create source context files in the given directory.

The returned creator function will produce one file: source-context.json

Args:
  output_dir: (String) The directory to create the files (usually the yaml
      directory).
  source_contexts: ([ExtendedSourceContext-compatible json dict])
      A list of json-serializable dicts containing source contexts. If None
      or empty, output_dir will be inspected to determine if it has an
      associated Git repo, and appropriate source contexts will be created
      for that directory.
  source_dir: (String) The location of the source files, for inferring source
      contexts when source_contexts is empty or None. If not specified,
      output_dir will be used instead.
Returns:
  callable() - A function that will create source-context.json file in the
  given directory. The creator function will return a cleanup function which
  can be used to delete any files the creator function creates.

  If there are no source_contexts associated with the directory, the creator
  function will not create any files (and the cleanup function it returns
  will also do nothing).
c                  N   >^ T V s/ s H	  o " 5       PM     sn mU4S jnU$ s  sn f )Nc                  &   > T H
  n U " 5         M     g Nr   )ccleanupss    r   Cleanup?GetSourceContextFilesCreator.<locals>.Generate.<locals>.CleanupD  s    !	 r   r   )grb   ra   creatorss     @r   Generate.GetSourceContextFilesCreator.<locals>.GenerateB  s*    %&XX&H N	 's   ")_GetSourceContexts_GetContextFileCreator)
output_dirrQ   
source_dirrf   re   s       @r   GetSourceContextFilesCreatorrl   "  s8    4 
()AzBO	H&zCDH 
/r   c                    U(       d  [        U=(       d    U 5      nU(       d  / $ / n[        [        U5      44 H  u  pV[        R                  R                  X5      n U(       d$  [        R                  R                  U5      (       d>  [        US5       n[        R                  " Xg5        SSS5        UR                  U5        M  M     U$ ! , (       d  f       N'= f! [         a"  n[        R                  " SXX5         SnAM  SnAff = f)ap  Creates source context file in the given directory if possible.

Currently, only source-context.json file will be produced.

Args:
  output_dir: (String) The directory to create the files (usually the yaml
      directory).
  source_contexts:  ([ExtendedSourceContext-compatible json dict])
      A list of json-serializable dicts containing source contexts. If None
      or empty, source context will be inferred from source_dir.
  overwrite: (boolean) If true, silently replace any existing file.
  source_dir: (String) The location of the source files, for inferring
      source contexts when source_contexts is empty or None. If not
      specified, output_dir will be used instead.
Returns:
  ([String]) A list containing the names of the files created. If there are
  no source contexts found, or if the contexts files could not be created, the
  result will be an empty.
wNCould not generate [%s]: %s)rh   CONTEXT_FILENAMEr[   ospathjoinexistsopenjsondumprN   IOErrorloggingwarn)	rj   rQ   	overwriterk   createdcontext_filenamecontext_objectfes	            r   CreateContextFilesr   K  s    * 
()AzBOi'*?;<+>&ww||JAG	"''..)9::"C(A
))N
& )'( ;	+> 
. )(  Gll02BFFGs0   7CC,C
C	C
D
(DD
c                     [         R                  " S/[        U5      -   U S9n[        R                  (       a  UR                  S5      nU$ ! [        [         R                  4 a!  n[        R                  " SX5         SnAgSnAff = f)zCalls git with the given args, in the given working directory.

Args:
  cwd: The working directory for the command.
  *args: Any arguments for the git command.
Returns:
  The raw output of the command, or None if the command failed.
r*   )cwdzutf-8z#Could not call git with args %s: %sN)

subprocesscheck_outputlistr   PY3decodeOSErrorCalledProcessErrorry   debug)r   argsoutputr   s       r   rB   rB   s  sk    $$eWtDz%9sCF~~}}W%fM
:00	1 MM7As   A	A B&BBc                 &    [        U SS[        5      $ )zCalls git to output every configured remote URL.

Args:
  source_directory: The path to directory containing the source code.
Returns:
  The raw output of the command, or None if the command failed.
configz--get-regexp)rB   _REMOTE_URL_PATTERN)rG   s    r   _GetGitRemoteUrlConfigsr     s     
.2E
G Gr   c                    [        U 5      nU(       d  0 $ 0 nUR                  S5      nU H  nU(       d  M  UR                  S5      n[        U5      S:w  a  [        R                  " SU5        ME  US   nUS   n[
        R                  " [        U5      nU(       d  [        R                  " SU5        M  UR                  S5      n	XrU	'   M     U$ )zFinds the list of git remotes for the given source directory.

Args:
  source_directory: The path to directory containing the source code.
Returns:
  A dictionary of remote name to remote URL, empty if no remotes are found.

 r
   z7Skipping unexpected config line, incorrect segments: %sr   r	   z;Skipping unexpected config line, could not match remote: %s)	r   splitlenry   r   rC   r"   r   r$   )
rG   remote_url_config_outputresultconfig_linesconfig_lineconfig_line_partsremote_url_config_namerR   remote_url_name_matchremote_url_names
             r   rJ   rJ     s     55EF	!I&)//5,!k $))#.
"mmM! /q1"1%JHH35 mm !"-/+11!4O(?- ". 
-r   c                 N    [        U SS5      nU(       a  UR                  5       $ S$ )zFinds the current HEAD revision for the given source directory.

Args:
  source_directory: The path to directory containing the source code.
Returns:
  The HEAD revision of the current branch, or None if the command failed.
z	rev-parseHEADN)rB   strip)rG   
raw_outputs     r   rK   rK     s)     (+v>*)			3t3r   c                    Sn[         R                  " [        U5      nU(       a  UR                  S5      nUS:X  a1  UR                  S5      nUR                  S5      (       d	  SSU0US.0nO=US	:X  a7  UR                  S5      nUR                  S5      =(       d    S
nSSUUS.0US.0nU(       d  SXS.0n[	        X0S9$ )a  Parses the URL into a source context blob, if the URL is a git or GCP repo.

Args:
  remote_name: The name of the remote.
  remote_url: The remote URL to parse.
  source_revision: The current revision of the source directory.
Returns:
  An ExtendedSourceContext suitable for JSON.
Nid_typeidproject_or_repo_id	repo_namer+   uid)repoId
revisionIdpdefaultprojectRepoId)	projectIdrepoNamer*   )r%   r   )r=   )rC   r"   _CLOUD_REPO_PATTERNr$   r>   )	r=   rR   rP   r.   r"   r   raw_repo_id
project_idr   s	            r   rM   rM     s     '  ((&

3%
kk)$G$KK 45k [[%%; .	/0 
C;;34j++k*7ii
!#-"+$-. ,-.g 
jHIG	7	<<r   c                    ^ ^^ [         R                  R                  T 5      (       a  [        R                  " ST 5        S $ U 4S jmUUU 4S jnU$ )a/  Creates a creator function for an extended source context file.

Args:
  name: (String) The name of the file to generate.
  json_object: Any object compatible with json.dump.
Returns:
  (callable()) A creator function that will create the file and return a
  cleanup function that will delete the file.
z*%s already exists. It will not be updated.c                      S $ )Nc                      g r_   r   r   r   r   <lambda>7_GetJsonFileCreator.<locals>.<lambda>.<locals>.<lambda>  s    Dr   r   r   r   r   r   %_GetJsonFileCreator.<locals>.<lambda>  s    Lr   c                  2   > [         R                  " T 5        g r_   )rq   remove)names   r   rb   $_GetJsonFileCreator.<locals>.Cleanup  s    IIdOr   c                     >  [        TS5       n [        R                  " TU 5        S S S 5        T$ ! , (       d  f       T$ = f! [         a#  n[        R
                  " STU5         S nAT$ S nAff = f)Nrn   ro   )ru   rv   rw   rx   ry   rz   )r   r   rb   json_objectr   s     r   rf   %_GetJsonFileCreator.<locals>.Generate  sf    ;c?a		+q!  N	 ? N  ;ll0$::N;s0   A 1A 
A A  A 
A0A++A0)rq   rr   rt   ry   rz   )r   r   rf   rb   s   `` @r   _GetJsonFileCreatorr     s>     WW^^DLL=tD!! 
/r   c                 t    [         R                  R                  U [        5      n[	        U[        U5      5      $ )ag  Creates a creator function for an old-style source context file.

Args:
  output_dir: (String) The name of the directory in which to generate the
      file. The file will be named source-context.json.
  contexts: ([dict]) A list of ExtendedSourceContext-compatible dicts for json
      serialization.
Returns:
  A creator function that will create the file.
)rq   rr   rs   rp   r   r[   )rj   contextsr   s      r   ri   ri     s,     
j"2	3$	T#4X#>	??r   c                 ~     [        U 5      nU(       d  [        R                  " SU 5        U$ ! [         a    / n N.f = f)aS  Gets the source contexts associated with a directory.

This function is mostly a wrapper around CalculateExtendedSourceContexts
which logs a message if the context could not be determined.
Args:
  source_dir: (String) The directory to inspect.
Returns:
  [ExtendedSourceContext-compatible json dict] A list of 0 or more source
  contexts.
zCould not find any remote repositories associated with [%s]. Cloud diagnostic tools may not be able to display the correct source code for this deployment.)rT   r8   ry   info)rk   rQ   s     r   rh   rh   +  sH    6zBO 
LL	+,68 
 
$ Os   - <<r_   )FN)(r   rv   ry   rq   rC   r   "googlecloudsdk.appengine._internalr   r   r   rE   r-   REMOTE_REPO_CATEGORYrp   EXT_CONTEXT_FILENAMEobjectr   compiler!   r#   r'   r1   r6   	Exceptionr8   r;   r>   rH   rT   r[   rl   r   rB   r   rJ   rK   rM   r   ri   rh   r   r   r   <module>r      s    >   	 	  9*  	   $ (  . 6 8 JJ56 **KL",&. 
M )=$ 0"'1h1h&R ?D"&%P&	G%P	4:=z2@r   