backstage: πŸ› Bug Report: Multiple Merge Request creation to same Gitlab Repo fail with 400 Bad request

πŸ“œ Description

Hi, We are using the action: publish:gitlab:merge-request in our Merge Request templates and generating the MR to an existing repo in Gitlab. First MR will be successful, however any subsequent MR with same branch name or different branch name fails with 400 Bad request error.

Please let me know how can this be resolved?

πŸ‘ Expected behavior

The template should generate subsequent MRs successfully as long as the branch name is new.

πŸ‘Ž Actual Behavior with Screenshots

The publish action fails with below error.

Commit to branch failed

image

πŸ‘Ÿ Reproduction steps

  1. Create a software template with publish step using - action: publish:gitlab:merge-request
  2. Try to publish the MR to an existing Gitlab repo. Once successful, merge the MR and close it.
  3. Try to create another MR to the same repo, it fails with above error.

πŸ“ƒ Provide the context for the Bug.

No response

πŸ–₯️ Your Environment

No response

πŸ‘€ Have you spent some time to check if this bug has been raised before?

  • I checked and didn’t find similar issue

🏒 Have you read the Code of Conduct?

Are you willing to submit PR?

None

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 38 (28 by maintainers)

Most upvoted comments

GitLab API supports different actions per file, so the scaffolder action could be modified to make it per file/path, for example:


    - id: gitlab_merge_request
      name: publish:gitlab:merge-request
      action: publish:gitlab:merge-request
      input:
        repoUrl: gitlab.com?repo=backstage-playground&owner=agavlyukovskiy
        title: "Test MR"
        branchName: my-mr-branch
        commitActions:
          - path: CODEOWNERS
            action: 'update'
          - path: my-new-dir
            action: 'create'
          - path: dont-like-dir
            action: 'delete'

instead of current approach where we use the same commitAction for all files

@sharadpattanshetti Is there any update about this? Thanks!

Is there any news?

the issue is up for grabs @ichasco-heytrade!

@Rugvip and everyone sorry for the late reply. Had to check the policies before posting. Hope this helps…

  1. Read integration config and other required details in the handler function
  2. Clone the template repo - this might have happened using cookiecutter action in the first step in template.yaml
  3. Clone the component repo using below command, execSync is from built in nodejs lib :
 require("child_process").execSync(`git clone https://oauth2:${token}@${cloneUrl} ${templateDir} --depth 1`)
  1. Serialize the template directory({{cookiecutter_componentId}}) content as in the publish:gitlab-merge-request action
let fileRoot: string;
            if (sourcePath) {
                fileRoot = resolveSafeChildPath(ctx.workspacePath, sourcePath);
            } else if (targetPath) {
                // for backward compatibility
                fileRoot = resolveSafeChildPath(ctx.workspacePath, targetPath);
            } else {
                fileRoot = ctx.workspacePath;
            }
            const fileContents = await serializeDirectoryContents(fileRoot, {
                gitignore: true,
            });
            const fileContentsIntermediate = await serializeDirectoryContents(intermediateDir, {
                gitignore: true,
            });
  1. Now the fileContents dir has all the serialized template dir content and fileContentsIntermediate is having all the serialized component content(where the merge request is going to be created)
  2. Create local git branch using @isomorphic-git lib
  3. Read complete folder tree from the Repo before doing sync
  const getAllDirectoryNames = fs.readdirSync(ctx.workspacePath, { withFileTypes: true })
                .filter((item) => item.isDirectory())
                .map((item) => item.name);
            const getAllFileNames = fs.readdirSync(ctx.workspacePath, { withFileTypes: true })
                .filter((item) => item.isFile())
                .map((item) => item.name);
            getAllFileNames.map(async (file: any) => {
                fs.copySync(`${ctx.workspacePath}/${file}`, `${templateDir}/${file}`);
            })
  1. Call @alumna/reflect to sync each folder of template repo to the component repo
await Promise.all(
                getAllDirectoryNames.map(async (directory_test) => {
                    await getDirectory(templateDir, ctx.workspacePath, directory_test);
                })
            )
  1. Get modified files using the status using the @isomorphic-git lib
// GET ADDED/MODIFIED FILES  
            await Promise.all(
                fileContents.map(async (file: any) => {
                    let status = await git.status({ fs, dir: `${templateDir}`, filepath: `${file.path}` })
                    if (status === '*added' || status === '*modified' || status === 'absent') {
                        ctx.logger.debug(`my-gitlab-merge-request : Step 2 - FILES ADD/UPDATE: ${file.path}:${status}`)
                        await git.add({ fs, dir: `${templateDir}`, filepath: `${file.path}` })
                    }
                })
            )
  1. Get Deleted file @isomorphic-git lib
//GET DELETED FILES  
           await Promise.all(
               fileContentsIntermediate.map(async (deletedFile: any) => {
                   let status = await git.status({ fs, dir: `${templateDir}`, filepath: `${deletedFile.path}` })
                   if (status === '*deleted') {
                       if (fs.existsSync(`${templateDir}/${deletedFile.path}`)) {
                           await git.remove({ fs, dir: `${templateDir}`, filepath: `${deletedFile.path}` })
                       }
                       else {
                           await fs.copy(`${intermediateDir}/${deletedFile.path}`, `${templateDir}/${deletedFile.path}`);
                           await git.remove({ fs, dir: `${templateDir}`, filepath: `${deletedFile.path}` })
                           ctx.logger.debug(`my-gitlab-merge-request : Step 2 - FILES DELETED: ${deletedFile.path}`)
                       }

                   }
               })
           )

  1. Do a commit and push using @isomorphic-git lib
  2. Create MR using gitbreaker lib
  3. Additionally if you want to create target branch/verify in between these steps etc then use gitbreaker lib

sure @tudi2d . Occupied a bit with urgent deliverables now, will try in few days definitely. Before that I wanted your and @Rugvip opinion if this is the best way to achieve that functionality?

Thanks @sharadpattanshetti - sounds great. Would you be up for contributing a fix to this bug? πŸ˜ƒ