1
0
Fork 0

Adding upstream version 3.10.8.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-18 09:37:23 +02:00
parent 37e9b6d587
commit 03bfe4079e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
356 changed files with 28857 additions and 0 deletions

84
forges/gitlab/common.go Normal file
View file

@ -0,0 +1,84 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/kind"
options_http "code.forgejo.org/f3/gof3/v3/options/http"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"github.com/hashicorp/go-version"
"gitlab.com/gitlab-org/api/client-go"
)
type common struct {
generic.NullDriver
}
func (o *common) GetHelper() any {
panic("not implemented")
}
func (o *common) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
return generic.NewChildrenSlice(0)
}
func (o *common) getTree() generic.TreeInterface {
return o.GetNode().GetTree()
}
func (o *common) getPageSize() int {
return o.getTreeDriver().GetPageSize()
}
func (o *common) getF3Tree() f3_tree.TreeInterface {
return o.getTree().(f3_tree.TreeInterface)
}
func (o *common) getKind() kind.Kind {
return o.GetNode().GetKind()
}
func (o *common) getChildDriver(kind kind.Kind) generic.NodeDriverInterface {
return o.GetNode().GetChild(id.NewNodeID(kind)).GetDriver()
}
func (o *common) isContainer() bool {
return o.getF3Tree().IsContainer(o.getKind())
}
func (o *common) getURL() string {
return o.getTreeDriver().options.GetURL()
}
func (o *common) getPushURL() string {
return o.getTreeDriver().options.GetPushURL()
}
func (o *common) getNewMigrationHTTPClient() options_http.NewMigrationHTTPClientFun {
return o.getTreeDriver().options.GetNewMigrationHTTPClient()
}
func (o *common) getTreeDriver() *treeDriver {
return o.GetTreeDriver().(*treeDriver)
}
func (o *common) getIsAdmin() bool {
return o.getTreeDriver().GetIsAdmin()
}
func (o *common) getClient() *gitlab.Client {
return o.getTreeDriver().GetClient()
}
func (o *common) getVersion() *version.Version {
return o.getTreeDriver().GetVersion()
}
func (o *common) IsNull() bool { return false }

View file

@ -0,0 +1,43 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
)
type container struct {
common
}
func (o *container) NewFormat() f3.Interface {
node := o.GetNode()
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
}
func (o *container) ToFormat() f3.Interface {
return o.NewFormat()
}
func (o *container) FromFormat(content f3.Interface) {
}
func (o *container) Get(context.Context) bool { return true }
func (o *container) Put(ctx context.Context) id.NodeID {
return o.upsert(ctx)
}
func (o *container) Patch(ctx context.Context) {
o.upsert(ctx)
}
func (o *container) upsert(context.Context) id.NodeID {
return id.NewNodeID(o.getKind())
}

46
forges/gitlab/forge.go Normal file
View file

@ -0,0 +1,46 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
type forge struct {
common
ownersKind map[string]kind.Kind
}
func newForge() generic.NodeDriverInterface {
return &forge{
ownersKind: make(map[string]kind.Kind),
}
}
func (o *forge) Equals(context.Context, generic.NodeInterface) bool { return true }
func (o *forge) Get(context.Context) bool { return true }
func (o *forge) Put(context.Context) id.NodeID { return id.NewNodeID("forge") }
func (o *forge) Patch(context.Context) {}
func (o *forge) Delete(context.Context) {}
func (o *forge) NewFormat() f3.Interface { return &f3.Forge{} }
func (o *forge) FromFormat(f3.Interface) {}
func (o *forge) ToFormat() f3.Interface {
return &f3.Forge{
Common: f3.NewCommon("forge"),
URL: o.String(),
}
}
func (o *forge) String() string {
options := o.GetTreeDriver().(*treeDriver).options
return options.ForgeAuth.GetURL()
}

16
forges/gitlab/main.go Normal file
View file

@ -0,0 +1,16 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options"
"code.forgejo.org/f3/gof3/v3/options"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
)
func init() {
f3_tree.RegisterForgeFactory(gitlab_options.Name, newTreeDriver)
options.RegisterFactory(gitlab_options.Name, newOptions)
}

16
forges/gitlab/options.go Normal file
View file

@ -0,0 +1,16 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options"
"code.forgejo.org/f3/gof3/v3/options"
)
func newOptions() options.Interface {
o := &gitlab_options.Options{}
o.SetName(gitlab_options.Name)
return o
}

View file

@ -0,0 +1,9 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package options
const (
Name = "gitlab"
)

View file

@ -0,0 +1,33 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package options
import (
"context"
"code.forgejo.org/f3/gof3/v3/forges/helpers/auth"
"code.forgejo.org/f3/gof3/v3/options"
options_http "code.forgejo.org/f3/gof3/v3/options/http"
"code.forgejo.org/f3/gof3/v3/options/logger"
"github.com/urfave/cli/v3"
)
type Options struct {
options.Options
logger.OptionsLogger
auth.ForgeAuth
options_http.Implementation
Version string
}
func (o *Options) FromFlags(ctx context.Context, c *cli.Command, prefix string) {
o.ForgeAuth.FromFlags(ctx, c, prefix)
}
func (o *Options) GetFlags(prefix, category string) []cli.Flag {
return o.ForgeAuth.GetFlags(prefix, category)
}

View file

@ -0,0 +1,121 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"fmt"
"net/http"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/util"
"gitlab.com/gitlab-org/api/client-go"
)
type organization struct {
common
forgejoOrganization *gitlab.Group
}
var _ f3_tree.ForgeDriverInterface = &organization{}
func newOrganization() generic.NodeDriverInterface {
return &organization{}
}
func (o *organization) SetNative(organization any) {
o.forgejoOrganization = organization.(*gitlab.Group)
}
func (o *organization) GetNativeID() string {
return fmt.Sprintf("%d", o.forgejoOrganization.ID)
}
func (o *organization) NewFormat() f3.Interface {
node := o.GetNode()
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
}
func (o *organization) ToFormat() f3.Interface {
if o.forgejoOrganization == nil {
return o.NewFormat()
}
return &f3.Organization{
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoOrganization.ID)),
Name: o.forgejoOrganization.Name,
FullName: o.forgejoOrganization.Description,
}
}
func (o *organization) FromFormat(content f3.Interface) {
organization := content.(*f3.Organization)
o.forgejoOrganization = &gitlab.Group{
ID: int(util.ParseInt(organization.GetID())),
Name: organization.Name,
Path: organization.Name,
Description: organization.FullName,
}
}
func (o *organization) Get(ctx context.Context) bool {
node := o.GetNode()
o.Trace("%s", node.GetID())
organization, resp, err := o.getClient().Groups.GetGroup(node.GetID().Int(), &gitlab.GetGroupOptions{})
if resp.StatusCode == http.StatusNotFound {
return false
}
if err != nil {
panic(fmt.Errorf("organization %v %w", o, err))
}
o.forgejoOrganization = organization
return true
}
func (o *organization) Patch(context.Context) {
node := o.GetNode()
o.Trace("%s", node.GetID())
_, _, err := o.getClient().Groups.UpdateGroup(o.forgejoOrganization.Name, &gitlab.UpdateGroupOptions{
Description: &o.forgejoOrganization.Description,
})
if err != nil {
panic(fmt.Errorf("UpdateGroup %v %w", o, err))
}
}
func (o *organization) Put(context.Context) id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
organization, _, err := o.getClient().Groups.CreateGroup(&gitlab.CreateGroupOptions{
Name: &o.forgejoOrganization.Name,
Path: &o.forgejoOrganization.Name,
Description: &o.forgejoOrganization.Description,
})
if err != nil {
panic(fmt.Errorf("CreateGroup %v %w", o, err))
}
o.forgejoOrganization = organization
o.Trace("%s", organization)
return id.NewNodeID(o.GetNativeID())
}
func (o *organization) Delete(ctx context.Context) {
node := o.GetNode()
o.Trace("%s", node.GetID())
permanentlyRemove := true
_, err := o.getClient().Groups.DeleteGroup(o.forgejoOrganization.Name, &gitlab.DeleteGroupOptions{
PermanentlyRemove: &permanentlyRemove,
})
if err != nil {
panic(fmt.Errorf("DeleteGroup %v %w", o, err))
}
}

View file

@ -0,0 +1,56 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"fmt"
"net/http"
"code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"gitlab.com/gitlab-org/api/client-go"
)
type organizations struct {
container
}
func (o *organizations) listOrganizationsPage(ctx context.Context, page int) []*gitlab.Group {
pageSize := o.getPageSize()
var organizationFounds []*gitlab.Group
var err error
organizationFounds, _, err = o.getClient().Groups.ListGroups(&gitlab.ListGroupsOptions{
ListOptions: gitlab.ListOptions{Page: page, PerPage: pageSize},
})
if err != nil {
panic(fmt.Errorf("error while listing organizations: %v", err))
}
return organizationFounds
}
func (o *organizations) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(o.listOrganizationsPage(ctx, page)...)...)
}
func (o *organizations) GetIDFromName(ctx context.Context, name string) id.NodeID {
organization, resp, err := o.getClient().Groups.GetGroup(name, &gitlab.GetGroupOptions{})
if resp.StatusCode == http.StatusNotFound {
return id.NilID
}
if err != nil {
panic(fmt.Errorf("organization %v %w", o, err))
}
return id.NewNodeID(organization.ID)
}
func newOrganizations() generic.NodeDriverInterface {
return &organizations{}
}

18
forges/gitlab/projects.go Normal file
View file

@ -0,0 +1,18 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT
package gitlab
import (
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
type projects struct {
container
}
func newProjects() generic.NodeDriverInterface {
return &projects{}
}

42
forges/gitlab/root.go Normal file
View file

@ -0,0 +1,42 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
type root struct {
generic.NullDriver
content f3.Interface
}
func newRoot(content f3.Interface) generic.NodeDriverInterface {
return &root{
content: content,
}
}
func (o *root) FromFormat(content f3.Interface) {
o.content = content
}
func (o *root) ToFormat() f3.Interface {
return o.content
}
func (o *root) Get(context.Context) bool { return true }
func (o *root) Put(context.Context) id.NodeID {
return id.NilID
}
func (o *root) Patch(context.Context) {
}

View file

@ -0,0 +1,16 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package tests
import (
gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options"
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
)
func init() {
tests_forge.RegisterFactory(gitlab_options.Name, func() tests_forge.Interface {
return newForgeTest(gitlab_options.Name)
})
}

View file

@ -0,0 +1,57 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package tests
import (
"testing"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/options"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
)
type forgeTest struct {
tests_forge.Base
}
func (o *forgeTest) NewOptions(t *testing.T) options.Interface {
return newTestOptions(t, o.Base.GetName())
}
func (o *forgeTest) GetNameExceptions() []string {
return []string{tests_forge.ComplianceNameForkedPullRequest}
}
func (o *forgeTest) GetKindExceptions() []kind.Kind {
return []kind.Kind{
f3_tree.KindAssets,
f3_tree.KindComments,
f3_tree.KindIssues,
f3_tree.KindLabels,
f3_tree.KindMilestones,
f3_tree.KindProjects,
f3_tree.KindPullRequests,
f3_tree.KindReactions,
f3_tree.KindReleases,
f3_tree.KindRepositories,
f3_tree.KindReviews,
f3_tree.KindReviewComments,
f3_tree.KindTopics,
}
}
func (o *forgeTest) GetNonTestUsers() []string {
return []string{
GetFixtureUsername(),
"ghost",
}
}
func newForgeTest(name string) tests_forge.Interface {
t := &forgeTest{}
t.SetName(name)
return t
}

View file

@ -0,0 +1,60 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package tests
import (
"os"
"strings"
"testing"
gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options"
"code.forgejo.org/f3/gof3/v3/forges/helpers/auth"
"code.forgejo.org/f3/gof3/v3/logger"
"code.forgejo.org/f3/gof3/v3/options"
)
func GetFixtureURL(name string) string {
upperName := strings.ToUpper(name)
hostPort := os.Getenv("GOF3_" + upperName + "_HOST_PORT")
if hostPort == "" {
return ""
}
return "http://" + hostPort
}
func GetFixtureUsername() string {
user := os.Getenv("GITLAB_TEST_USER")
if user == "" {
user = "root"
}
return user
}
func GetFixturePassword() string {
password := os.Getenv("GITLAB_TEST_PASSWORD")
if password == "" {
password = "Wrobyak4"
}
return password
}
func newTestOptions(t *testing.T, name string) options.Interface {
t.Helper()
url := GetFixtureURL(name)
if url == "" {
t.Skip("test server is not up")
}
forgeAuth := auth.NewForgeAuth()
forgeAuth.SetURL(url)
forgeAuth.SetUsername(GetFixtureUsername())
forgeAuth.SetPassword(GetFixturePassword())
o := options.GetFactory(gitlab_options.Name)().(*gitlab_options.Options)
o.ForgeAuth = forgeAuth
l := logger.NewLogger()
l.SetLevel(logger.Trace)
o.OptionsLogger.SetLogger(l)
return o
}

18
forges/gitlab/topics.go Normal file
View file

@ -0,0 +1,18 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT
package gitlab
import (
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
type topics struct {
container
}
func newTopics() generic.NodeDriverInterface {
return &topics{}
}

213
forges/gitlab/tree.go Normal file
View file

@ -0,0 +1,213 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options"
"code.forgejo.org/f3/gof3/v3/kind"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"github.com/hashicorp/go-version"
"gitlab.com/gitlab-org/api/client-go"
)
type treeDriver struct {
generic.NullTreeDriver
user *gitlab.User
client *gitlab.Client
options *gitlab_options.Options
version *version.Version
}
func (o *treeDriver) GetClient() *gitlab.Client {
return o.client
}
func (o *treeDriver) GetIsAdmin() bool {
return o.user.IsAdmin
}
func (o *treeDriver) SetIsAdmin() {
user, _, err := o.client.Users.CurrentUser()
if err != nil {
panic(fmt.Errorf("Failed to get information about the user for: %s. Error: %v", o.options.GetURL(), err))
}
o.user = user
o.options.SetUsername(user.Username)
}
var (
ForgejoVersion700 = version.Must(version.NewVersion("7.0.0")) // 1.22
ForgejoVersion600 = version.Must(version.NewVersion("6.0.0")) // 1.21
ForgejoVersion500 = version.Must(version.NewVersion("5.0.0")) // 1.20.1
ForgejoVersion501 = version.Must(version.NewVersion("5.0.1")) // 1.20.2
ForgejoVersion502 = version.Must(version.NewVersion("5.0.2")) // 1.20.3
ForgejoVersion503 = version.Must(version.NewVersion("5.0.3")) // 1.20.4
ForgejoVersion504 = version.Must(version.NewVersion("5.0.4")) // 1.20.5
ForgejoVersion4 = version.Must(version.NewVersion("4.0.0")) // 1.19
ForgejoVersionNotFound = version.Must(version.NewVersion("1.0.0"))
)
func (o *treeDriver) GetVersion() *version.Version {
o.SetVersion()
return o.version
}
func (o *treeDriver) SetVersion() {
if o.version != nil {
return
}
client := &http.Client{}
url := fmt.Sprintf("%s/api/forgejo/v1/version", o.options.GetURL())
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
resp, err := client.Do(req)
if err != nil {
panic(fmt.Errorf("while getting %s %w", url, err))
}
switch resp.StatusCode {
case http.StatusNotFound:
o.version = ForgejoVersionNotFound
case http.StatusOK:
v := struct{ Version string }{}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(fmt.Errorf("reading response body %+v %w", resp, err))
}
if err := json.Unmarshal(body, &v); err != nil {
panic(fmt.Errorf("decoding JSON response from %s %s %w", url, string(body), err))
}
o.version = version.Must(version.NewVersion(v.Version))
default:
panic(fmt.Errorf("unexpected status code fetching %s %d %v", url, resp.StatusCode, resp))
}
}
func (o *treeDriver) maybeSudo(uid interface{}) []gitlab.RequestOptionFunc {
if !o.GetIsAdmin() {
return []gitlab.RequestOptionFunc{}
}
if name, ok := uid.(string); ok && name == o.options.GetUsername() {
return []gitlab.RequestOptionFunc{}
}
return []gitlab.RequestOptionFunc{
gitlab.WithSudo(uid),
}
}
func (o *treeDriver) Init() {
o.NullTreeDriver.Init()
var err error
var client *gitlab.Client
if o.options.GetToken() != "" {
o.Debug("Connecting to %s with token", o.options.GetURL())
client, err = gitlab.NewClient(o.options.GetToken(), gitlab.WithBaseURL(o.options.GetURL()), gitlab.WithHTTPClient(o.options.GetNewMigrationHTTPClient()()))
} else {
o.Debug("Connecting to %s as user %s", o.options.GetURL(), o.options.GetUsername())
client, err = gitlab.NewBasicAuthClient(o.options.GetUsername(), o.options.GetPassword(), gitlab.WithBaseURL(o.options.GetURL()), gitlab.WithHTTPClient(o.options.GetNewMigrationHTTPClient()()))
}
if err != nil {
panic(fmt.Errorf("Failed to create Forgejo client for: %s. Error: %v", o.options.GetURL(), err))
}
o.client = client
o.SetIsAdmin()
if o.GetIsAdmin() {
o.Debug("Connected as admin")
} else {
o.Debug("Connected as regular user")
}
}
func newTreeDriver(tree generic.TreeInterface, anyOptions any) generic.TreeDriverInterface {
driver := &treeDriver{
options: anyOptions.(*gitlab_options.Options),
}
driver.SetTree(tree)
driver.Init()
return driver
}
func (o *treeDriver) Factory(ctx context.Context, k kind.Kind) generic.NodeDriverInterface {
switch k {
case f3_tree.KindForge:
return newForge()
case f3_tree.KindOrganizations:
return newOrganizations()
case f3_tree.KindOrganization:
return newOrganization()
case f3_tree.KindUsers:
return newUsers()
case f3_tree.KindUser:
return newUser()
case f3_tree.KindProjects:
return newProjects()
// case f3_tree.KindProject:
// return newProject()
// case f3_tree.KindIssues:
// return newIssues()
// case f3_tree.KindIssue:
// return newIssue()
// case f3_tree.KindComments:
// return newComments()
// case f3_tree.KindComment:
// return newComment()
// case f3_tree.KindAssets:
// return newAssets()
// case f3_tree.KindAsset:
// return newAsset()
// case f3_tree.KindLabels:
// return newLabels()
// case f3_tree.KindLabel:
// return newLabel()
// case f3_tree.KindReactions:
// return newReactions()
// case f3_tree.KindReaction:
// return newReaction()
// case f3_tree.KindReviews:
// return newReviews()
// case f3_tree.KindReview:
// return newReview()
// case f3_tree.KindReviewComments:
// return newReviewComments()
// case f3_tree.KindReviewComment:
// return newReviewComment()
// case f3_tree.KindMilestones:
// return newMilestones()
// case f3_tree.KindMilestone:
// return newMilestone()
// case f3_tree.KindPullRequests:
// return newPullRequests()
// case f3_tree.KindPullRequest:
// return newPullRequest()
// case f3_tree.KindReleases:
// return newReleases()
// case f3_tree.KindRelease:
// return newRelease()
case f3_tree.KindTopics:
return newTopics()
// case f3_tree.KindRepositories:
// return newRepositories()
// case f3_tree.KindRepository:
// return newRepository(ctx)
case kind.KindRoot:
return newRoot(o.GetTree().(f3_tree.TreeInterface).NewFormat(k))
default:
panic(fmt.Errorf("unexpected kind %s", k))
}
}

157
forges/gitlab/user.go Normal file
View file

@ -0,0 +1,157 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// Copyright twenty-panda <twenty-panda@posteo.com>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"fmt"
"net/http"
"time"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/util"
"gitlab.com/gitlab-org/api/client-go"
)
type user struct {
common
gitlabUser *gitlab.User
Password string
}
var _ f3_tree.ForgeDriverInterface = &user{}
func newUser() generic.NodeDriverInterface {
return &user{}
}
func (o *user) SetNative(user any) {
o.gitlabUser = user.(*gitlab.User)
}
func (o *user) GetNativeID() string {
return fmt.Sprintf("%d", o.gitlabUser.ID)
}
func (o *user) NewFormat() f3.Interface {
node := o.GetNode()
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
}
func (o *user) ToFormat() f3.Interface {
if o.gitlabUser == nil {
return o.NewFormat()
}
return &f3.User{
Common: f3.NewCommon(fmt.Sprintf("%d", o.gitlabUser.ID)),
UserName: o.gitlabUser.Username,
Name: o.gitlabUser.Name,
Email: o.gitlabUser.Email,
IsAdmin: o.gitlabUser.IsAdmin,
Password: o.Password,
}
}
func (o *user) FromFormat(content f3.Interface) {
user := content.(*f3.User)
o.gitlabUser = &gitlab.User{
ID: int(util.ParseInt(user.GetID())),
Username: user.UserName,
Name: user.Name,
Email: user.Email,
IsAdmin: user.IsAdmin,
}
o.Password = user.Password
}
func (o *user) Get(ctx context.Context) bool {
node := o.GetNode()
o.Trace("%s", node.GetID())
if node.GetID() == id.NilID {
panic("GetID() == 0")
}
user, resp, err := o.getClient().Users.GetUser(node.GetID().Int(), gitlab.GetUsersOptions{})
if resp.StatusCode == http.StatusNotFound {
return false
}
if err != nil {
panic(fmt.Errorf("user %v %w", o, err))
}
o.gitlabUser = user
return true
}
func (o *user) Patch(context.Context) {
}
func (o *user) Put(context.Context) id.NodeID {
skipConfirmation := true
if o.Password == "" {
o.Password = util.RandSeq(30)
}
u, _, err := o.getClient().Users.CreateUser(&gitlab.CreateUserOptions{
Username: &o.gitlabUser.Username,
Name: &o.gitlabUser.Name,
Email: &o.gitlabUser.Email,
Password: &o.Password,
Admin: &o.gitlabUser.IsAdmin,
SkipConfirmation: &skipConfirmation,
})
if err != nil {
panic(fmt.Errorf("%v: %w", o.gitlabUser, err))
}
o.gitlabUser = u
o.Trace("%s %d", o.gitlabUser.Username, o.gitlabUser.ID)
return id.NewNodeID(u.ID)
}
func (o *user) deleteUser(ctx context.Context) int {
u := fmt.Sprintf("users/%d", o.gitlabUser.ID)
type deleteUserOptions struct {
HardDelete *bool `url:"hard_delete,omitempty" json:"hard_delete,omitempty"`
}
hardDelete := true
req, err := o.getClient().NewRequest(http.MethodDelete, u, &deleteUserOptions{
HardDelete: &hardDelete,
}, nil)
if err != nil {
panic(fmt.Errorf("NewRequest: %v %v: %w", o.gitlabUser.ID, o.gitlabUser.Username, err))
}
resp, err := o.getClient().Do(req, nil)
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
panic(fmt.Errorf("unexpected status code: %v %v: %v %w", o.gitlabUser.ID, o.gitlabUser.Username, resp.StatusCode, err))
}
if resp.StatusCode != http.StatusNotFound && err != nil {
panic(fmt.Errorf("Do: %v %v: %w", o.gitlabUser.ID, o.gitlabUser.Username, err))
}
return resp.StatusCode
}
func (o *user) Delete(ctx context.Context) {
o.Trace("%s %d", o.gitlabUser.Username, o.gitlabUser.ID)
statusCode := o.deleteUser(ctx)
if statusCode != http.StatusNoContent {
panic(fmt.Errorf("expected user deletion to return 204 but got %d", statusCode))
}
loop := 100
for i := 0; i < loop; i++ {
if o.deleteUser(ctx) == http.StatusNotFound {
o.Trace("user deletion complete")
return
}
o.Trace("waiting for asynchronous user deletion (%d/%d)", i, loop)
time.Sleep(5 * time.Second)
}
o.Trace("user still present after %d attempts", loop)
}

50
forges/gitlab/users.go Normal file
View file

@ -0,0 +1,50 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package gitlab
import (
"context"
"fmt"
"code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"gitlab.com/gitlab-org/api/client-go"
)
type users struct {
container
}
func (o *users) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
pageSize := o.getPageSize()
userFounds, _, err := o.getClient().Users.ListUsers(&gitlab.ListUsersOptions{
ListOptions: gitlab.ListOptions{Page: page, PerPage: pageSize},
})
if err != nil {
panic(fmt.Errorf("error while listing users: %v", err))
}
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(userFounds...)...)
}
func (o *users) GetIDFromName(ctx context.Context, name string) id.NodeID {
users, _, err := o.getClient().Users.ListUsers(&gitlab.ListUsersOptions{Username: &name})
if err != nil {
panic(fmt.Errorf("user %v %w", o, err))
}
if len(users) == 0 {
return id.NilID
} else if len(users) > 1 {
panic(fmt.Errorf("user %v found multiple users %v", name, users))
}
return id.NewNodeID(users[0].ID)
}
func newUsers() generic.NodeDriverInterface {
return &users{}
}