#NEWLINE# def detached( name , ref , target = None , remote = 'origin' , user = None , force_clone = False , force_checkout = False , fetch_remote = True , hard_reset = False , submodules = False , identity = None , https_user = None , https_pass = None , onlyif = False , unless = False , **kwargs ) : #NEWLINE# #INDENT# '\n    .. versionadded:: 2016.3.0\n\n    Make sure a repository is cloned to the given target directory and is\n    a detached HEAD checkout of the commit ID resolved from ``ref``.\n\n    name\n        Address of the remote repository.\n\n    ref\n        The branch, tag, or commit ID to checkout after clone.\n        If a branch or tag is specified it will be resolved to a commit ID\n        and checked out.\n\n    target\n        Name of the target directory where repository is about to be cloned.\n\n    remote : origin\n        Git remote to use. If this state needs to clone the repo, it will clone\n        it using this value as the initial remote name. If the repository\n        already exists, and a remote by this name is not present, one will be\n        added.\n\n    user\n        User under which to run git commands. By default, commands are run by\n        the user under which the minion is running.\n\n    force_clone : False\n        If the ``target`` directory exists and is not a git repository, then\n        this state will fail. Set this argument to ``True`` to remove the\n        contents of the target directory and clone the repo into it.\n\n    force_checkout : False\n        When checking out the revision ID, the state will fail if there are\n        unwritten changes. Set this argument to ``True`` to discard unwritten\n        changes when checking out.\n\n    fetch_remote : True\n        If ``False`` a fetch will not be performed and only local refs\n        will be reachable.\n\n    hard_reset : False\n        If ``True`` a hard reset will be performed before the checkout and any\n        uncommitted modifications to the working directory will be discarded.\n        Untracked files will remain in place.\n\n        .. note::\n            Changes resulting from a hard reset will not trigger requisites.\n\n    submodules : False\n        Update submodules\n\n    identity\n        A path on the minion server to a private key to use over SSH\n        Key can be specified as a SaltStack file server URL\n        eg. salt://location/identity_file\n\n    https_user\n        HTTP Basic Auth username for HTTPS (only) clones\n\n    https_pass\n        HTTP Basic Auth password for HTTPS (only) clones\n\n    onlyif\n        A command to run as a check, run the named command only if the command\n        passed to the ``onlyif`` option returns true\n\n    unless\n        A command to run as a check, only run the named command if the command\n        passed to the ``unless`` option returns false\n\n    ' #NEWLINE# ret = { #NEWLINE# #INDENT# 'name' : name , #NEWLINE# 'result' : True , #NEWLINE# 'comment' : '' , #NEWLINE# 'changes' : { #NEWLINE# #INDENT# #NEWLINE# #UNINDENT# } , #NEWLINE# #UNINDENT# } #NEWLINE# kwargs = salt . utils . clean_kwargs ( ** kwargs ) #NEWLINE# if kwargs : #NEWLINE# #INDENT# return _fail ( ret , salt . utils . invalid_kwargs ( kwargs , raise_exc = False ) ) #NEWLINE# #UNINDENT# if ( not ref ) : #NEWLINE# #INDENT# return _fail ( ret , "'{0}' is not a valid value for the 'ref' argument" . format ( ref ) ) #NEWLINE# #UNINDENT# if ( not target ) : #NEWLINE# #INDENT# return _fail ( ret , "'{0}' is not a valid value for the 'target' argument" . format ( ref ) ) #NEWLINE# #UNINDENT# if ( not isinstance ( ref , six . string_types ) ) : #NEWLINE# #INDENT# ref = str ( ref ) #NEWLINE# #UNINDENT# if ( target is not None ) : #NEWLINE# #INDENT# if ( not isinstance ( target , six . string_types ) ) : #NEWLINE# #INDENT# target = str ( target ) #NEWLINE# #UNINDENT# if ( not os . path . isabs ( target ) ) : #NEWLINE# #INDENT# return _fail ( ret , "Target '{0}' is not an absolute path" . format ( target ) ) #NEWLINE# #UNINDENT# #UNINDENT# if ( ( user is not None ) and ( not isinstance ( user , six . string_types ) ) ) : #NEWLINE# #INDENT# user = str ( user ) #NEWLINE# #UNINDENT# if ( ( remote is not None ) and ( not isinstance ( remote , six . string_types ) ) ) : #NEWLINE# #INDENT# remote = str ( remote ) #NEWLINE# #UNINDENT# if ( identity is not None ) : #NEWLINE# #INDENT# if isinstance ( identity , six . string_types ) : #NEWLINE# #INDENT# identity = [ identity ] #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if ( not isinstance ( identity , list ) ) : #NEWLINE# #INDENT# return _fail ( ret , 'Identity must be either a list or a string' ) #NEWLINE# #UNINDENT# #UNINDENT# for ident_path in identity : #NEWLINE# #INDENT# if ( 'salt://' in ident_path ) : #NEWLINE# #INDENT# try : #NEWLINE# #INDENT# ident_path = __salt__ [ 'cp.cache_file' ] ( ident_path ) #NEWLINE# #UNINDENT# except IOError as exc : #NEWLINE# #INDENT# log . error ( 'Failed to cache {0}: {1}' . format ( ident_path , exc ) ) #NEWLINE# return _fail ( ret , "Identity '{0}' does not exist." . format ( ident_path ) ) #NEWLINE# #UNINDENT# #UNINDENT# if ( not os . path . isabs ( ident_path ) ) : #NEWLINE# #INDENT# return _fail ( ret , "Identity '{0}' is not an absolute path" . format ( ident_path ) ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# if ( ( https_user is not None ) and ( not isinstance ( https_user , six . string_types ) ) ) : #NEWLINE# #INDENT# https_user = str ( https_user ) #NEWLINE# #UNINDENT# if ( ( https_pass is not None ) and ( not isinstance ( https_pass , six . string_types ) ) ) : #NEWLINE# #INDENT# https_pass = str ( https_pass ) #NEWLINE# #UNINDENT# if os . path . isfile ( target ) : #NEWLINE# #INDENT# return _fail ( ret , "Target '{0}' exists and is a regular file, cannot proceed" . format ( target ) ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# desired_fetch_url = salt . utils . url . add_http_basic_auth ( name , https_user , https_pass , https_only = True ) #NEWLINE# #UNINDENT# except ValueError as exc : #NEWLINE# #INDENT# return _fail ( ret , exc . __str__ ( ) ) #NEWLINE# #UNINDENT# redacted_fetch_url = salt . utils . url . redact_http_basic_auth ( desired_fetch_url ) #NEWLINE# run_check_cmd_kwargs = { #NEWLINE# #INDENT# 'runas' : user , #NEWLINE# #UNINDENT# } #NEWLINE# if ( 'shell' in __grains__ ) : #NEWLINE# #INDENT# run_check_cmd_kwargs [ 'shell' ] = __grains__ [ 'shell' ] #NEWLINE# #UNINDENT# cret = mod_run_check ( run_check_cmd_kwargs , onlyif , unless ) #NEWLINE# if isinstance ( cret , dict ) : #NEWLINE# #INDENT# ret . update ( cret ) #NEWLINE# return ret #NEWLINE# #UNINDENT# remote_ref_type = 'ref' #NEWLINE# if ( ( len ( ref ) <= 40 ) and all ( ( ( x in string . hexdigits ) for x in ref ) ) ) : #NEWLINE# #INDENT# ref = ref . lower ( ) #NEWLINE# remote_ref_type = 'hash' #NEWLINE# #UNINDENT# comments = [ ] #NEWLINE# hash_exists_locally = False #NEWLINE# local_commit_id = None #NEWLINE# gitdir = os . path . join ( target , '.git' ) #NEWLINE# if ( os . path . isdir ( gitdir ) or __salt__ [ 'git.is_worktree' ] ( target ) ) : #NEWLINE# #INDENT# local_commit_id = _get_local_rev_and_branch ( target , user ) [ 0 ] #NEWLINE# if ( ( remote_ref_type is 'hash' ) and __salt__ [ 'git.describe' ] ( ref ) ) : #NEWLINE# #INDENT# hash_exists_locally = True #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# remotes = __salt__ [ 'git.remotes' ] ( target , user = user , redact_auth = False ) #NEWLINE# if ( ( remote in remotes ) and ( name in remotes [ remote ] [ 'fetch' ] ) ) : #NEWLINE# #INDENT# pass #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# current_fetch_url = None #NEWLINE# if ( remote in remotes ) : #NEWLINE# #INDENT# current_fetch_url = remotes [ remote ] [ 'fetch' ] #NEWLINE# #UNINDENT# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# return _neutral_test ( ret , 'Remote {0} would be set to {1}' . format ( remote , name ) ) #NEWLINE# #UNINDENT# __salt__ [ 'git.remote_set' ] ( target , url = name , remote = remote , user = user , https_user = https_user , https_pass = https_pass ) #NEWLINE# comments . append ( "Remote {0} updated from '{1}' to '{2}'" . format ( remote , str ( current_fetch_url ) , name ) ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# else : #NEWLINE# #INDENT# if os . path . isdir ( current_fetch_url ) : #NEWLINE# #INDENT# if force_clone : #NEWLINE# #INDENT# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# return _neutral_test ( ret , 'Target directory {0} exists. Since force_clone=True, the contents of {0} would be deleted, and {1} would be cloned into this directory.' . format ( target , name ) ) #NEWLINE# #UNINDENT# log . debug ( 'Removing contents of {0} to clone repository {1} in its place (force_clone=True set in git.detached state)' . format ( target , name ) ) #NEWLINE# try : #NEWLINE# #INDENT# if os . path . islink ( target ) : #NEWLINE# #INDENT# os . unlink ( target ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# salt . utils . rm_rf ( target ) #NEWLINE# #UNINDENT# #UNINDENT# except OSError as exc : #NEWLINE# #INDENT# return _fail ( ret , 'Unable to remove {0}: {1}' . format ( target , exc ) , comments ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# ret [ 'changes' ] [ 'forced clone' ] = True #NEWLINE# #UNINDENT# #UNINDENT# else : #NEWLINE# #INDENT# if os . listdir ( target ) : #NEWLINE# #INDENT# return _fail ( ret , "Target '{0}' exists, is non-empty and is not a git repository. Set the 'force_clone' option to True to remove this directory's contents and proceed with cloning the remote repository" . format ( target ) ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# log . debug ( "Target {0} is not found, 'git clone' is required" . format ( target ) ) #NEWLINE# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# return _neutral_test ( ret , 'Repository {0} would be cloned to {1}' . format ( name , target ) ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# clone_opts = [ '--no-checkout' ] #NEWLINE# if ( remote != 'origin' ) : #NEWLINE# #INDENT# clone_opts . extend ( [ '--origin' , remote ] ) #NEWLINE# #UNINDENT# __salt__ [ 'git.clone' ] ( target , name , user = user , opts = clone_opts , identity = identity , https_user = https_user , https_pass = https_pass ) #NEWLINE# comments . append ( '{0} cloned to {1}' . format ( name , target ) ) #NEWLINE# #UNINDENT# except Exception as exc : #NEWLINE# #INDENT# log . error ( 'Unexpected exception in git.detached state' , exc_info = True ) #NEWLINE# if isinstance ( exc , CommandExecutionError ) : #NEWLINE# #INDENT# msg = _strip_exc ( exc ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# msg = str ( exc ) #NEWLINE# #UNINDENT# return _fail ( ret , msg , comments ) #NEWLINE# #UNINDENT# #UNINDENT# refspecs = [ 'refs/heads/*:refs/remotes/{0}/*' . format ( remote ) , '+refs/tags/*:refs/tags/*' ] #NEWLINE# if ( hash_exists_locally or ( fetch_remote is False ) ) : #NEWLINE# #INDENT# pass #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# return _neutral_test ( ret , 'Repository remote {0} would be fetched' . format ( remote ) ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# fetch_changes = __salt__ [ 'git.fetch' ] ( target , remote = remote , force = True , refspecs = refspecs , user = user , identity = identity ) #NEWLINE# #UNINDENT# except CommandExecutionError as exc : #NEWLINE# #INDENT# msg = 'Fetch failed' #NEWLINE# msg += ( ':\n\n' + str ( exc ) ) #NEWLINE# return _fail ( ret , msg , comments ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if fetch_changes : #NEWLINE# #INDENT# comments . append ( 'Remote {0} was fetched, resulting in updated refs' . format ( remote ) ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# checkout_commit_id = '' #NEWLINE# if ( remote_ref_type is 'hash' ) : #NEWLINE# #INDENT# if __salt__ [ 'git.describe' ] ( ref ) : #NEWLINE# #INDENT# checkout_commit_id = ref #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# return _fail ( ret , 'Ref does not exist: {0}' . format ( ref ) ) #NEWLINE# #UNINDENT# #UNINDENT# else : #NEWLINE# #INDENT# try : #NEWLINE# #INDENT# all_remote_refs = __salt__ [ 'git.remote_refs' ] ( target , user = user , identity = identity , https_user = https_user , https_pass = https_pass , ignore_retcode = False ) #NEWLINE# if ( ( ( ( 'refs/remotes/' + remote ) + '/' ) + ref ) in all_remote_refs ) : #NEWLINE# #INDENT# checkout_commit_id = all_remote_refs [ ( ( ( 'refs/remotes/' + remote ) + '/' ) + ref ) ] #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if ( ( 'refs/tags/' + ref ) in all_remote_refs ) : #NEWLINE# #INDENT# checkout_commit_id = all_remote_refs [ ( 'refs/tags/' + ref ) ] #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# return _fail ( ret , 'Ref {0} does not exist' . format ( ref ) ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# except CommandExecutionError as exc : #NEWLINE# #INDENT# return _fail ( ret , 'Failed to list refs for {0}: {1}' . format ( remote , _strip_exc ( exc ) ) ) #NEWLINE# #UNINDENT# #UNINDENT# if hard_reset : #NEWLINE# #INDENT# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# return _neutral_test ( ret , 'Hard reset to HEAD would be performed on {0}' . format ( target ) ) #NEWLINE# #UNINDENT# __salt__ [ 'git.reset' ] ( target , opts = [ '--hard' , 'HEAD' ] , user = user ) #NEWLINE# comments . append ( 'Repository was reset to HEAD before checking out ref' ) #NEWLINE# #UNINDENT# if ( checkout_commit_id == local_commit_id ) : #NEWLINE# #INDENT# new_rev = None #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if __opts__ [ 'test' ] : #NEWLINE# #INDENT# ret [ 'changes' ] [ 'HEAD' ] = { #NEWLINE# #INDENT# 'old' : local_commit_id , #NEWLINE# 'new' : checkout_commit_id , #NEWLINE# #UNINDENT# } #NEWLINE# return _neutral_test ( ret , 'Commit ID {0} would be checked out at {1}' . format ( checkout_commit_id , target ) ) #NEWLINE# #UNINDENT# __salt__ [ 'git.checkout' ] ( target , checkout_commit_id , force = force_checkout , user = user ) #NEWLINE# comments . append ( 'Commit ID {0} was checked out at {1}' . format ( checkout_commit_id , target ) ) #NEWLINE# try : #NEWLINE# #INDENT# new_rev = __salt__ [ 'git.revision' ] ( cwd = target , user = user , ignore_retcode = True ) #NEWLINE# #UNINDENT# except CommandExecutionError : #NEWLINE# #INDENT# new_rev = None #NEWLINE# #UNINDENT# #UNINDENT# if submodules : #NEWLINE# #INDENT# __salt__ [ 'git.submodule' ] ( target , 'update' , opts = [ '--init' , '--recursive' ] , user = user , identity = identity ) #NEWLINE# comments . append ( 'Submodules were updated' ) #NEWLINE# #UNINDENT# if ( new_rev is not None ) : #NEWLINE# #INDENT# ret [ 'changes' ] [ 'HEAD' ] = { #NEWLINE# #INDENT# 'old' : local_commit_id , #NEWLINE# 'new' : new_rev , #NEWLINE# #UNINDENT# } #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# comments . append ( 'Already checked out at correct revision' ) #NEWLINE# #UNINDENT# msg = _format_comments ( comments ) #NEWLINE# log . info ( msg ) #NEWLINE# ret [ 'comment' ] = msg #NEWLINE# return ret