@@ -24,8 +24,12 @@ import (
2424 "github.com/go-git/go-billy/v5/memfs"
2525 "github.com/go-git/go-git/v5"
2626 "github.com/go-git/go-git/v5/config"
27+
2728 //"github.com/go-git/go-git/v5/plumbing"
2829 "github.com/go-git/go-git/v5/plumbing/object"
30+ "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
31+ "github.com/go-git/go-git/v5/plumbing/transport"
32+ gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http"
2933 "github.com/go-git/go-git/v5/storage/memory"
3034
3135 uuid "github.com/satori/go.uuid"
@@ -1112,7 +1116,12 @@ func SetGitWorkflow(ctx context.Context, workflow Workflow, org *Org) error {
11121116 }
11131117
11141118 if len (org .Defaults .WorkflowUploadBranch ) == 0 {
1115- org .Defaults .WorkflowUploadBranch = "master"
1119+ // Default to 'main' for Azure DevOps, 'master' for others
1120+ if strings .Contains (org .Defaults .WorkflowUploadRepo , "dev.azure.com" ) {
1121+ org .Defaults .WorkflowUploadBranch = "main"
1122+ } else {
1123+ org .Defaults .WorkflowUploadBranch = "master"
1124+ }
11161125 }
11171126
11181127 if org .Defaults .WorkflowUploadRepo == "" || org .Defaults .WorkflowUploadToken == "" {
@@ -1122,18 +1131,8 @@ func SetGitWorkflow(ctx context.Context, workflow Workflow, org *Org) error {
11221131 }
11231132
11241133 org .Defaults .WorkflowUploadRepo = strings .TrimSpace (org .Defaults .WorkflowUploadRepo )
1125- if strings .HasPrefix (org .Defaults .WorkflowUploadRepo , "https://" ) {
1126- org .Defaults .WorkflowUploadRepo = strings .Replace (org .Defaults .WorkflowUploadRepo , "https://" , "" , 1 )
1127- org .Defaults .WorkflowUploadRepo = strings .Replace (org .Defaults .WorkflowUploadRepo , "http://" , "" , 1 )
1128- }
1129-
1130- if strings .HasSuffix (org .Defaults .WorkflowUploadRepo , ".git" ) {
1131- org .Defaults .WorkflowUploadRepo = strings .TrimSuffix (org .Defaults .WorkflowUploadRepo , ".git" )
1132- }
1133-
1134- //log.Printf("[DEBUG] Uploading workflow %s to repo %s for org %s (%s)", workflow.ID, org.Defaults.WorkflowUploadRepo, org.Name, org.Id)
11351134
1136- // Remove images
1135+ // Remove images from workflow before backup
11371136 workflow .Image = ""
11381137 for actionIndex , _ := range workflow .Actions {
11391138 workflow .Actions [actionIndex ].LargeImage = ""
@@ -1155,12 +1154,39 @@ func SetGitWorkflow(ctx context.Context, workflow Workflow, org *Org) error {
11551154 return err
11561155 }
11571156
1158- commitMessage := fmt .Sprintf ("User '%s' updated workflow '%s' with status '%s'" , workflow .UpdatedBy , workflow .Name , workflow .Status )
1159- urlEncodedPassword := url .QueryEscape (org .Defaults .WorkflowUploadToken )
1160- location := fmt .Sprintf ("https://%s:%s@%s.git" , org .Defaults .WorkflowUploadUsername , urlEncodedPassword , org .Defaults .WorkflowUploadRepo )
1157+ commitMessage := fmt .Sprintf ("User '%s' updated workflow '%s' with status '%s' at %s" , workflow .UpdatedBy , workflow .Name , workflow .Status , time .Now ().Format ("2006-01-02 15:04:05" ))
1158+ repoURL := org .Defaults .WorkflowUploadRepo
1159+ repoURL = strings .TrimPrefix (repoURL , "https://" )
1160+ repoURL = strings .TrimPrefix (repoURL , "http://" )
1161+
1162+ var location string
1163+ var isAzureDevOps bool
1164+
1165+ if strings .Contains (repoURL , "dev.azure.com" ) {
1166+ isAzureDevOps = true
1167+ location = fmt .Sprintf ("https://%s" , repoURL )
1168+ log .Printf ("[DEBUG] Detected Azure DevOps repository" )
1169+ } else {
1170+ isAzureDevOps = false
1171+ // Only append .git if the URL contains github.com
1172+ if strings .Contains (repoURL , "github.com" ) && ! strings .HasSuffix (repoURL , ".git" ) {
1173+ repoURL += ".git"
1174+ }
1175+
1176+ urlEncodedPassword := url .QueryEscape (org .Defaults .WorkflowUploadToken )
1177+ location = fmt .Sprintf ("https://%s:%s@%s" , org .Defaults .WorkflowUploadUsername , urlEncodedPassword , repoURL )
1178+
1179+ }
1180+
1181+ maskedRepo := location
1182+ if ! isAzureDevOps {
1183+ maskedRepo = strings .ReplaceAll (location , org .Defaults .WorkflowUploadToken , "****" )
1184+ if urlEncodedPassword := url .QueryEscape (org .Defaults .WorkflowUploadToken ); urlEncodedPassword != org .Defaults .WorkflowUploadToken {
1185+ maskedRepo = strings .ReplaceAll (maskedRepo , urlEncodedPassword , "****" )
1186+ }
1187+ }
11611188
1162- newRepoName := strings .Replace (strings .Replace (location , org .Defaults .WorkflowUploadToken , "****" , - 1 ), urlEncodedPassword , "****" , - 1 )
1163- log .Printf ("[DEBUG] Uploading workflow %s to repo: %s" , workflow .ID , newRepoName )
1189+ log .Printf ("[DEBUG] Uploading workflow %s to repo: %s" , workflow .ID , maskedRepo )
11641190
11651191 fs := memfs .New ()
11661192 if len (workflow .Status ) == 0 {
@@ -1170,65 +1196,94 @@ func SetGitWorkflow(ctx context.Context, workflow Workflow, org *Org) error {
11701196 //filePath := fmt.Sprintf("/%s/%s.json", workflow.Status, workflow.ID)
11711197 filePath := fmt .Sprintf ("%s/%s/%s_%s.json" , workflow .ExecutingOrg .Id , workflow .Status , strings .ReplaceAll (workflow .Name , " " , "-" ), workflow .ID )
11721198
1173- // Specify the file path within the repository
1174- repo , err := git .Clone (memory .NewStorage (), fs , & git.CloneOptions {
1199+ cloneOptions := & git.CloneOptions {
11751200 URL : location ,
1176- })
1177- if err != nil {
1178- newErr := strings .ReplaceAll (err .Error (), org .Defaults .WorkflowUploadToken , "****" )
1179- newErr = strings .ReplaceAll (newErr , urlEncodedPassword , "****" )
1180-
1181- log .Printf ("[ERROR] Error cloning repo '%s' (workflow backup): %s" , newRepoName , newErr )
1182- return err
11831201 }
11841202
1185- // Initialize a new Git repository in memory
1186- w := & git.Worktree {}
1203+ // For Azure DevOps, add additional auth configuration and capability handling
1204+ if isAzureDevOps {
1205+ transport .UnsupportedCapabilities = []capability.Capability {
1206+ capability .ThinPack ,
1207+ }
1208+
1209+ cloneOptions .Auth = & gitHttp.BasicAuth {
1210+ Username : org .Defaults .WorkflowUploadUsername , // Use the username for Azure DevOps
1211+ Password : org .Defaults .WorkflowUploadToken ,
1212+ }
1213+ }
11871214
1188- // Create a new commit with the in-memory file
1189- w , err = repo .Worktree ()
1215+ repo , err := git .Clone (memory .NewStorage (), fs , cloneOptions )
11901216 if err != nil {
1191- newErr := strings .ReplaceAll (err .Error (), org .Defaults .WorkflowUploadToken , "****" )
1192- newErr = strings .ReplaceAll (newErr , urlEncodedPassword , "****" )
1217+ errMsg := err .Error ()
1218+ if isAzureDevOps {
1219+ log .Printf ("[ERROR] Azure DevOps clone failed. Check: 1) PAT has Code(R/W) permissions, 2) Branch '%s' exists, 3) Repo URL is correct" , org .Defaults .WorkflowUploadBranch )
1220+ }
1221+ log .Printf ("[ERROR] Error cloning repo '%s': %s" , maskedRepo , strings .ReplaceAll (errMsg , org .Defaults .WorkflowUploadToken , "****" ))
1222+ return err
1223+ }
11931224
1194- log .Printf ("[ERROR] Error getting worktree for repo '%s' (2): %s" , newRepoName , newErr )
1225+ w , err := repo .Worktree ()
1226+ if err != nil {
1227+ log .Printf ("[ERROR] Error getting worktree for repo '%s': %s" , maskedRepo , err )
11951228 return err
11961229 }
11971230
11981231 // Write the byte blob to the in-memory file system
11991232 file , err := fs .Create (filePath )
12001233 if err != nil {
1201- newErr := strings .ReplaceAll (err .Error (), org .Defaults .WorkflowUploadToken , "****" )
1234+ log .Printf ("[ERROR] Creating file in repo '%s': %v" , maskedRepo , err )
1235+ return err
1236+ }
1237+ defer file .Close ()
12021238
1203- log .Printf ("[ERROR] Creating git file: %v" , newErr )
1239+ if _ , err = io .Copy (file , bytes .NewReader (workflowData )); err != nil {
1240+ log .Printf ("[ERROR] Writing data to file: %v" , err )
12041241 return err
12051242 }
12061243
1207- defer file .Close ()
1208- //_, err = io.Copy(file, bytes.NewReader(workflowData))
1209- _ , err = io .Copy (file , bytes .NewReader (workflowData ))
1210- if err != nil {
1211- log .Printf ("[ERROR] Writing data to git file: %v" , err )
1244+ if _ , err = w .Add (filePath ); err != nil {
1245+ log .Printf ("[ERROR] Adding file to staging area: %s" , err )
12121246 return err
12131247 }
12141248
1215- // Add the file to the staging area
1216- _ , err = w .Add ( filePath )
1249+ // Check if there are any changes to commit
1250+ status , err : = w .Status ( )
12171251 if err != nil {
1218- log .Printf ("[ERROR] Error adding file to git staging area (2) : %s " , err )
1252+ log .Printf ("[ERROR] Getting working tree status : %v " , err )
12191253 return err
12201254 }
12211255
1222- // Commit the changes
1256+ hasChanges := false
1257+ for _ , fileStatus := range status {
1258+ if fileStatus .Staging != git .Unmodified {
1259+ hasChanges = true
1260+ break
1261+ }
1262+ }
1263+
1264+ if ! hasChanges {
1265+ log .Printf ("[INFO] No changes detected for workflow %s (%s). File content is identical to existing version." , workflow .Name , workflow .ID )
1266+ return nil
1267+ }
1268+
1269+ authorName := org .Defaults .WorkflowUploadUsername
1270+ if authorName == "" {
1271+ if isAzureDevOps {
1272+ authorName = "Workflow Automation"
1273+ } else {
1274+ authorName = "Shuffle User"
1275+ }
1276+ }
1277+
12231278 _ , err = w .Commit (commitMessage , & git.CommitOptions {
12241279 Author : & object.Signature {
1225- Name : org . Defaults . WorkflowUploadUsername ,
1280+ Name : authorName ,
12261281 Email : "" ,
12271282 When : time .Now (),
12281283 },
12291284 })
12301285 if err != nil {
1231- log .Printf ("[ERROR] Committing git changes: %v (2) " , err )
1286+ log .Printf ("[ERROR] Committing changes: %v" , err )
12321287 return err
12331288 }
12341289
@@ -1237,18 +1292,25 @@ func SetGitWorkflow(ctx context.Context, workflow Workflow, org *Org) error {
12371292 // Push the changes to a remote repository (replace URL with your repository URL)
12381293 // fmt.Sprintf("refs/heads/%s:refs/heads/%s", org.Defaults.WorkflowUploadBranch, org.Defaults.WorkflowUploadBranch)},
12391294 ref := fmt .Sprintf ("refs/heads/%s:refs/heads/%s" , org .Defaults .WorkflowUploadBranch , org .Defaults .WorkflowUploadBranch )
1240- err = repo .Push (& git.PushOptions {
1295+
1296+ pushOptions := & git.PushOptions {
12411297 RemoteName : "origin" ,
12421298 RefSpecs : []config.RefSpec {config .RefSpec (ref )},
1243- RemoteURL : location ,
1244- })
1299+ }
1300+
1301+ // Set authentication for push operations for both Azure DevOps and GitHub
1302+ pushOptions .Auth = & gitHttp.BasicAuth {
1303+ Username : org .Defaults .WorkflowUploadUsername ,
1304+ Password : org .Defaults .WorkflowUploadToken ,
1305+ }
1306+
1307+ err = repo .Push (pushOptions )
12451308 if err != nil {
1246- log .Printf ("[ERROR] Change git Push issue : %v (2)" , err )
1309+ log .Printf ("[ERROR] Git push failed for repo '%s' : %v" , maskedRepo , err )
12471310 return err
12481311 }
12491312
1250- log .Printf ("[DEBUG] File uploaded successfully to '%s'!" , newRepoName )
1251-
1313+ log .Printf ("[DEBUG] Workflow successfully uploaded to '%s'!" , maskedRepo )
12521314 return nil
12531315}
12541316
0 commit comments