44
55use Ahc \Jwt \JWT ;
66use Exception ;
7+ use Utopia \Command ;
78use Utopia \Cache \Cache ;
89use Utopia \VCS \Adapter \Git ;
910use Utopia \VCS \Exception \FileNotFound ;
@@ -841,7 +842,7 @@ public function updateCommitStatus(string $repositoryName, string $commitHash, s
841842 /**
842843 * Generates a clone command using app access token
843844 */
844- public function generateCloneCommand (string $ owner , string $ repositoryName , string $ version , string $ versionType , string $ directory , string $ rootDirectory ): string
845+ public function generateCloneCommand (string $ owner , string $ repositoryName , string $ version , string $ versionType , string $ directory , string $ rootDirectory ): Command
845846 {
846847 if (empty ($ rootDirectory )) {
847848 $ rootDirectory = '* ' ;
@@ -854,42 +855,133 @@ public function generateCloneCommand(string $owner, string $repositoryName, stri
854855
855856 $ cloneUrl = "https:// {$ owner }{$ accessToken }@github.com/ {$ owner }/ {$ repositoryName }" ;
856857
857- $ directory = escapeshellarg ($ directory );
858- $ rootDirectory = escapeshellarg ($ rootDirectory );
859-
860858 $ commands = [
861- "mkdir -p {$ directory }" ,
862- "cd {$ directory }" ,
863- "git config --global init.defaultBranch main " ,
864- "git init " ,
865- "git remote add origin {$ cloneUrl }" ,
866- // Enable sparse checkout
867- "git config core.sparseCheckout true " ,
868- "echo {$ rootDirectory } >> .git/info/sparse-checkout " ,
869- // Disable fetching of refs we don't need
870- "git config --add remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*' " ,
871- // Disable fetching of tags
872- "git config remote.origin.tagopt --no-tags " ,
859+ (new Command ('mkdir ' ))
860+ ->flag ('-p ' )
861+ ->argument ($ directory ),
862+ (new Command ('git ' ))
863+ ->argument ('config ' )
864+ ->argument ('--global ' )
865+ ->argument ('init.defaultBranch ' )
866+ ->argument ('main ' ),
867+ (new Command ('git ' ))
868+ ->argument ('init ' )
869+ ->argument ($ directory ),
870+ (new Command ('git ' ))
871+ ->option ('-C ' , $ directory )
872+ ->argument ('remote ' )
873+ ->argument ('add ' )
874+ ->argument ('origin ' )
875+ ->argument ($ cloneUrl ),
876+ (new Command ('git ' ))
877+ ->option ('-C ' , $ directory )
878+ ->argument ('config ' )
879+ ->argument ('--add ' )
880+ ->argument ('remote.origin.fetch ' )
881+ ->argument ('+refs/heads/*:refs/remotes/origin/* ' ),
882+ (new Command ('git ' ))
883+ ->option ('-C ' , $ directory )
884+ ->argument ('config ' )
885+ ->argument ('remote.origin.tagopt ' )
886+ ->argument ('--no-tags ' ),
887+ (new Command ('git ' ))
888+ ->option ('-C ' , $ directory )
889+ ->argument ('sparse-checkout ' )
890+ ->argument ('set ' )
891+ ->argument ('--no-cone ' )
892+ ->argument ($ rootDirectory ),
873893 ];
874894
875895 switch ($ versionType ) {
876896 case self ::CLONE_TYPE_BRANCH :
877- $ branchName = escapeshellarg ($ version );
878- $ commands [] = "if git ls-remote --exit-code --heads origin {$ branchName }; then git pull --depth=1 origin {$ branchName } && git checkout {$ branchName }; else git checkout -b {$ branchName }; fi " ;
897+ $ commands [] = Command::or (
898+ Command::and (
899+ (new Command ('git ' ))
900+ ->option ('-C ' , $ directory )
901+ ->argument ('ls-remote ' )
902+ ->argument ('--exit-code ' )
903+ ->argument ('--heads ' )
904+ ->argument ('origin ' )
905+ ->argument ($ version ),
906+ (new Command ('git ' ))
907+ ->option ('-C ' , $ directory )
908+ ->argument ('pull ' )
909+ ->argument ('--depth=1 ' )
910+ ->argument ('origin ' )
911+ ->argument ($ version ),
912+ (new Command ('git ' ))
913+ ->option ('-C ' , $ directory )
914+ ->argument ('checkout ' )
915+ ->argument ($ version )
916+ ),
917+ (new Command ('git ' ))
918+ ->option ('-C ' , $ directory )
919+ ->argument ('checkout ' )
920+ ->argument ('-b ' )
921+ ->argument ($ version )
922+ );
879923 break ;
880924 case self ::CLONE_TYPE_COMMIT :
881- $ commitHash = escapeshellarg ($ version );
882- $ commands [] = "git fetch --depth=1 origin {$ commitHash } && git checkout {$ commitHash }" ;
925+ $ commands [] = (new Command ('git ' ))
926+ ->option ('-C ' , $ directory )
927+ ->argument ('fetch ' )
928+ ->argument ('--depth=1 ' )
929+ ->argument ('origin ' )
930+ ->argument ($ version );
931+ $ commands [] = (new Command ('git ' ))
932+ ->option ('-C ' , $ directory )
933+ ->argument ('checkout ' )
934+ ->argument ($ version );
883935 break ;
884936 case self ::CLONE_TYPE_TAG :
885- $ tagName = escapeshellarg ($ version );
886- $ commands [] = "git fetch --depth=1 origin refs/tags/$(git ls-remote --tags origin {$ tagName } | tail -n 1 | awk -F '/' '{print $3}') && git checkout FETCH_HEAD " ;
937+ $ resolvedTag = $ this ->resolveTagReference ($ owner , $ repositoryName , $ version );
938+ $ commands [] = (new Command ('git ' ))
939+ ->option ('-C ' , $ directory )
940+ ->argument ('fetch ' )
941+ ->argument ('--depth=1 ' )
942+ ->argument ('origin ' )
943+ ->argument ('refs/tags/ ' . $ resolvedTag );
944+ $ commands [] = (new Command ('git ' ))
945+ ->option ('-C ' , $ directory )
946+ ->argument ('checkout ' )
947+ ->argument ('FETCH_HEAD ' );
887948 break ;
888949 }
889950
890- $ fullCommand = implode (" && " , $ commands );
951+ return Command::and (...$ commands );
952+ }
953+
954+ private function resolveTagReference (string $ owner , string $ repositoryName , string $ version ): string
955+ {
956+ if (!str_contains ($ version , '* ' )) {
957+ return $ version ;
958+ }
959+
960+ $ prefix = rtrim (strstr ($ version , '* ' , true ) ?: '' , '. ' );
961+ $ refPrefix = 'tags ' . (!empty ($ prefix ) ? '/ ' . $ prefix : '' );
962+ $ response = $ this ->call (
963+ self ::METHOD_GET ,
964+ "/repos/ {$ owner }/ {$ repositoryName }/git/matching-refs/ {$ refPrefix }" ,
965+ ['Authorization ' => "Bearer $ this ->accessToken " ]
966+ );
967+
968+ $ refs = $ response ['body ' ] ?? [];
969+ $ matches = [];
970+
971+ foreach ($ refs as $ ref ) {
972+ $ tag = str_replace ('refs/tags/ ' , '' , $ ref ['ref ' ] ?? '' );
973+ if ($ tag !== '' && fnmatch ($ version , $ tag )) {
974+ $ matches [] = $ tag ;
975+ }
976+ }
977+
978+ if (empty ($ matches )) {
979+ throw new Exception ("Tag not found for pattern: {$ version }" );
980+ }
981+
982+ usort ($ matches , static fn (string $ left , string $ right ): int => version_compare ($ left , $ right ));
891983
892- return $ fullCommand ;
984+ return $ matches [ array_key_last ( $ matches )] ;
893985 }
894986
895987 /**
0 commit comments