Search code examples
gitlabgitlab-api

How to import a specific file to a newly created Gitlab project without using import_url?


While creating new projects, I used to import a specific file from a parent repository that contained just that single file, so it could've been imported as a whole.

The complete action contained of a single POST request to https://gitlab.example.com/api/v4/projects/, sending payload like:

{
    "name": "The New Great Project III",
    "namespace_id": 12,
    "description": "A Project",
    "import_url": "https://user:users_token@gitlab.example.com/great-projects/0-0_parent_project.git"
}

The action used to create a new project with a single file imported from the parent project and users could start working right away.

However, it became unreliable: when entered newly created projects, users started seeing hanging import jobs, that never finished. The first suspects were insufficient permissions of users importing the parent project, but even parent project owners reported inability to import the parent project to new projects.

So here's my question: are there any other ways to clone file(s) from a template project to new ones? Any hooks or cronjobs run independently on the Gitlab servers?


Solution

  • I managed to split the whole process into 3 steps:

    1. Create new project.
    2. Fetch desired file(s) from existing project repository.
    3. Upload the file(s) to the new project repository.

    Since the question is technology agnostic, I'll just post some JavaScript/Vue.js flavored pseudocode to give a general overview to readers.

    Here's a function that is responsible for calling Gitlab API:

    async callGitLabProjects(url, method, payload, format) {
          const token = this.$store.getters.token
          
          let headers
          if (format === "text") {
              headers = {
                'Private-Token': token
              }
          }
          else {
              headers = {
                'Content-Type': 'application/json',
                'Private-Token': token
              }
          }
    
          const response = await fetch(url, {
            method: method,
            headers: headers,
            body: JSON.stringify(payload)
          })
          return response.json()
        }
    

    New project can be created by calling Gitlab API with the following payload and instructions:

    const payload = {
            "name": "newProjectName",
            "namespace_id": 10,
            "description": "Description"
          }
    
    let newRepoUrl = ""
    
    await this.callGitLabProjects("https://gitlab.expl.com/api/v4/projects/", "POST", payload, "json")
            .then(data => {
               newRepoUrl += data._links.self
              }
            .catch((e) => {
               console.error(e)
             })
    

    As you noticed, there's the new repository URL saved for future use.

    In step 2, we fetch the raw file to be imported (remember about URLencoding the file name):

    let rawFile = await this.callGitLabProjects("https://gitlab.expl.com/api/v4/projects/10705/repository/files/my%2Dfile%2Emd/raw?ref=master", "GET", "", "text")
    

    And after it's fetched, you can simply upload it to the new project's repository:

    const filePayload = {
              "branch": "master",
              "content": rawFile,
              "commit_message": "Initial file uploaded"
            }
    
    this.callGitLabProjects(newRepoUrl + "/repository/files/my%2Dfile%2Emd", "POST", filePayload, "json")
            .catch((e) => {
              console.log(e)
            })