Search code examples
gitlabgit-forkgitlab-api

Fork project into group for a new contributor (API)


For the possibility that there is a better approach that I'm thinking about I would like to explain my requirement first (the question starts after the divider line).

Given I have the following GitLab group structure:

- main-group
  - sub-group-project-templates
    - project-template-1
  - sub-group-projects

In main-group/sub-group-project-templates there are multiple projects that function as starter project for a task. If there is someone who wants to do such a task I want to have a fork of the corresponding project template (e.g. project-template-1) in the group sub-group-projects with the name of the person as project name.

Given the name of the person is John Doe who wants to start the task of project-template-1 there should be a new project main-group/sub-group-projects/john_doe_project-template-1.

John Doe should only be able to see (read / write) the repository john_doe_project-template-1 and NOT the rest of the project (other forks, ...).


My solution would be to fork the template project to the sub group and then add a new contributor.

But I already fail at the first step (forking the project into the sub group with a new name).

My first shot at it was looking at:

POST http://gitlab.com/api/v4/projects/project_id/fork

But I don't know how to define the target directory and set the name of the new fork. The following isn't working:

POST http://gitlab.com/api/v4/projects/project_id/fork?namespace=main-group%2Fsub-group-projects%2Fjohn_doe_project-template-1

"message": "404 Target Namespace Not Found"

Is something like this even possible or how can I achieve this?


Solution

  • Supposing you have the following configuration with project project-template-1 in subgroup sub-group-project-templates :

    enter image description here

    It can be done in multiple API requests but there are some work-in-progress for the following features:

    But these 2 issues have workarounds eg just perform an additionnal request to get the namespace id (1) and perform an edit project request (2) after forking

    The steps are the following :

    • get namespace id for the subgroup sub-group-projects
    • fork project main-group/sub-group-project-templates/project-template-1 to namespace with id $namespace_id & get the project id of the created project
    • rename project with id $project_id from project-template-1 to : johndoe_project-template-1
    • get user id for user johndoe
    • add project member for project johndoe_project-template-1 : add user with id $user_id

    Note that to add a member, you need the user id which I assume you want to search in the 4th step but you may not need this step if you already have it

    Here is a bash script performing all these step, it uses & :

    #!/bin/bash
    set -e
    
    gitlab_host=gitlab.your.host
    access_token=YOUR_ACCESS_TOKEN
    
    username=johndoe
    project=project-template-1
    user_access=30
    
    group=main-group
    namespace_src=sub-group-project-templates
    namespace_dest=sub-group-projects
    new_project_name=${username}_${project}
    
    project_src=$group/$namespace_src/$project
    
    encoded_project=$(echo $project_src | sed 's/\//%2F/g')
    
    echo "1 - get namespace id for $namespace_dest"
    #https://docs.gitlab.com/ce/api/namespaces.html
    namespace_id=$(curl -s "https://$gitlab_host/api/v4/namespaces?search=$namespace_dest" \
        -H "Private-Token: $access_token" | jq -r '.[0].id')
    
    
    echo "2 - fork project $project_src to namespace with id $namespace_id"
    #https://docs.gitlab.com/ce/api/projects.html#fork-project
    project_id=$(curl -s -X POST "https://$gitlab_host/api/v4/projects/$encoded_project/fork?namespace=$namespace_id" \
        -H "Private-Token: $access_token" | jq '.id')
    
    if [ -z $project_id ]; then
        echo "fork failed"
        exit 1
    fi
    
    echo "3 - rename project with id $project_id from $project to : $new_project_name"
    #https://docs.gitlab.com/ce/api/projects.html#edit-project
    curl -s -X PUT "https://$gitlab_host/api/v4/projects/$project_id" \
        -H "Content-Type: application/json" \
        -d "{\"path\": \"$new_project_name\",\"name\": \"$new_project_name\" }" \
        -H "Private-Token: $access_token" > /dev/null
    
    echo "4 - get user id for : $username"
    #https://docs.gitlab.com/ce/api/users.html
    user_id=$(curl -s "https://$gitlab_host/api/v4/users?username=johndoe" \
        -H "Private-Token: $access_token" | \
        jq -r '.[] | select(.name == "johndoe") | .id')
    
    echo "5 - edit project member : add user with id $user_id"
    #https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
    curl -s "https://$gitlab_host/api/v4/projects/$project_id/members" \
        -H "Private-Token: $access_token" \
        -d "user_id=$user_id&access_level=$user_access" > /dev/null
    

    In the 2nd step (fork project), project source namespace is URL encoded so you may want to use a proper manner to encode it (in this case we only have to replace / with %2F)