go-semantic-release / semantic-release

📦🚀 semantic-release written in Go
https://go-semantic-release.xyz
MIT License
395 stars 43 forks source link

Gitlab CI_JOB_TOKEN #141

Closed cliedeman closed 10 months ago

cliedeman commented 2 years ago

Get go-semantic-release to work with a gitlab ci job token.

The token has much less permissions than a PAT but only has access to limited endpoints. The motivation is ease of maintance. The ci tokens are automatically created and deleted after builds so are ideal for publishing

The variables we currently source from the project api are available during the build

https://docs.gitlab.com/ee/ci/variables/predefined_variables.html

Its not clear to me if the token can even access the commits

Will investigate...

cliedeman commented 2 years ago

Created a little test project

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/xanzy/go-gitlab"
)

func main() {
    fmt.Printf("Default Branch: %s\n", os.Getenv("CI_DEFAULT_BRANCH"))
    fmt.Printf("Project Visibility: %s\n", os.Getenv("CI_PROJECT_VISIBILITY"))
    projectId := os.Getenv("CI_PROJECT_ID")
    fmt.Printf("Project Id: %s\n", projectId)

    token := os.Getenv("CI_JOB_TOKEN")
    err := run(token, projectId)
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
      }
}

func run(token string, projectId string) error {
    git, err := gitlab.NewJobClient(token)

    if err != nil {
        return err
    }

    project, _, err := git.Projects.GetProject(projectId, nil)
    if err != nil {
        log.Printf("Project Err: %s", err)
    } else {
        log.Printf("Project: %v", project)
    }

    commits, _, err := git.Commits.ListCommits(projectId, &gitlab.ListCommitsOptions{
        ListOptions: gitlab.ListOptions{
            Page:    1,
            PerPage: 10,
        },
    })
    if err != nil {
        log.Printf("Commits Err: %s", err)
    } else {
        log.Printf("Commits: %v", commits)
    }

    tags, _, err := git.Tags.ListTags(projectId, &gitlab.ListTagsOptions{
        ListOptions: gitlab.ListOptions{
            Page:    1,
            PerPage: 100,
        },
    })
    if err != nil {
        log.Printf("Tags Err: %s", err)
    } else {
        log.Printf("Tags: %v", tags)
    }

    _, _, err = git.Releases.CreateRelease(projectId, &gitlab.CreateReleaseOptions{
        // TagName: &tag,
        // Ref:     &release.SHA,
        Description: gitlab.String("Test"),
    })
    if err != nil {
        log.Printf("Create Release Err: %s", err)
    } else {
        log.Printf("Create Release: %v", tags)
    }

    return nil
}

switching the client type is key

git, err := gitlab.NewJobClient(token)

Output

Default Branch: main
Project Visibility: private
Project Id: ******
2022/05/31 10:25:55 Project Err: GET https://gitlab.com/api/v4/projects/******: 404 {message: 404 Project Not Found}
2022/05/31 10:25:56 Commits Err: GET https://gitlab.com/api/v4/projects/******/repository/commits: 404 {message: 404 Project Not Found}
2022/05/31 10:25:56 Tags Err: GET https://gitlab.com/api/v4/projects/******/repository/tags: 404 {message: 404 Project Not Found}
2022/05/31 10:25:56 Create Release Err: POST https://gitlab.com/api/v4/projects/******/releases: 422 {message: Ref is not specified}

None of the calls work except for create release.

There is away to clone a repo:

git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.example.com/<namespace>/<project>

So with only being able to create a release and clone a repo we could actually implement this but it would be a reasonable amount of effort. Also this may not be viable for large repos

secustor commented 2 years ago

A clone shouldn't be needed, as long GIT_STRATEGY is not overwritten by an user.

CI_JOB_TOKEN permission documentation for reference. https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html I would like to see this feature too, this would be the killer feature which would make me migrate.

If I have time, I will give it a shot.

cliedeman commented 2 years ago

Hi @secustor

We can probably detect GIT_STRATEGY and fail the build. The clone could be used in caes where the pipeline is not cloned by default but I dont need this use case either.

@christophwitzko Do you think this should be a new alpha provider or switches on the existing gitlab provider?

christophwitzko commented 2 years ago

Thank you @secustor and @cliedeman for looking into this. I think we can add this to the current provider, so it will work for everyone out of the box. If GIT_STRATEGY is clone we can use the existing git provider as a library to read the commits and releases (https://github.com/go-semantic-release/provider-git/blob/master/pkg/provider/git.go). And the release is than created with the CI_JOB_TOKEN.

simon-weimann commented 11 months ago

Hi @christophwitzko,

do you have any update on this issue? For me it seems like you have a plan, that could work. We are currently migrating from github to gitlab and stumbled upon this issue.

Thanks in advance

christophwitzko commented 10 months ago

Thanks to @superewald a new version of the GitLab provider was just released that supports now the CI_JOB_TOKEN.