mirror of https://github.com/harness/drone.git
265 lines
7.3 KiB
Go
265 lines
7.3 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package migrate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/harness/gitness/app/store"
|
|
"github.com/harness/gitness/errors"
|
|
gitness_store "github.com/harness/gitness/store"
|
|
"github.com/harness/gitness/store/database/dbtx"
|
|
"github.com/harness/gitness/types"
|
|
"github.com/harness/gitness/types/enum"
|
|
|
|
"github.com/lucasb-eyer/go-colorful"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
const defaultLabelValueColor = enum.LabelColorGreen
|
|
|
|
// Label is label migrate.
|
|
type Label struct {
|
|
labelStore store.LabelStore
|
|
labelValueStore store.LabelValueStore
|
|
spaceStore store.SpaceStore
|
|
tx dbtx.Transactor
|
|
}
|
|
|
|
func NewLabel(
|
|
labelStore store.LabelStore,
|
|
labelValueStore store.LabelValueStore,
|
|
spaceStore store.SpaceStore,
|
|
tx dbtx.Transactor,
|
|
) *Label {
|
|
return &Label{
|
|
labelStore: labelStore,
|
|
labelValueStore: labelValueStore,
|
|
spaceStore: spaceStore,
|
|
tx: tx,
|
|
}
|
|
}
|
|
|
|
//nolint:gocognit
|
|
func (migrate Label) Import(
|
|
ctx context.Context,
|
|
migrator types.Principal,
|
|
space *types.Space,
|
|
extLabels []*ExternalLabel,
|
|
) ([]*types.Label, error) {
|
|
labels := make([]*types.Label, len(extLabels))
|
|
labelValues := make(map[string][]string)
|
|
|
|
spaceIDs, err := migrate.spaceStore.GetAncestorIDs(ctx, space.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get space ids hierarchy: %w", err)
|
|
}
|
|
scope := int64(len(spaceIDs))
|
|
for i, extLabel := range extLabels {
|
|
label, err := convertLabelWithSanitization(ctx, migrator, space.ID, scope, *extLabel)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to sanitize and convert external label input: %w", err)
|
|
}
|
|
labels[i] = label
|
|
|
|
if extLabel.Value != "" {
|
|
valueIn := &types.DefineValueInput{
|
|
Value: extLabel.Value,
|
|
Color: defaultLabelValueColor,
|
|
}
|
|
if err := valueIn.Sanitize(); err != nil {
|
|
return nil, fmt.Errorf("failed to sanitize external label value input: %w", err)
|
|
}
|
|
labelValues[label.Key] = append(labelValues[label.Key], valueIn.Value)
|
|
}
|
|
}
|
|
|
|
err = migrate.tx.WithTx(ctx, func(ctx context.Context) error {
|
|
for _, label := range labels {
|
|
err := migrate.defineLabelsAndValues(ctx, migrator.ID, space.ID, label, labelValues[label.Key])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to define labels and/or values: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to define external labels: %w", err)
|
|
}
|
|
|
|
return labels, nil
|
|
}
|
|
|
|
func (migrate Label) defineLabelsAndValues(
|
|
ctx context.Context,
|
|
migratorID int64,
|
|
spaceID int64,
|
|
labelIn *types.Label,
|
|
extValues []string) error {
|
|
var label *types.Label
|
|
var err error
|
|
// try to find the label first as it might have been defined already.
|
|
label, err = migrate.labelStore.Find(ctx, &spaceID, nil, labelIn.Key)
|
|
if errors.Is(err, gitness_store.ErrResourceNotFound) {
|
|
err := migrate.labelStore.Define(ctx, labelIn)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to define label: %w", err)
|
|
}
|
|
label = labelIn
|
|
} else if err != nil {
|
|
return fmt.Errorf("failed to find the label: %w", err)
|
|
}
|
|
|
|
values, err := migrate.labelValueStore.List(ctx, label.ID, &types.ListQueryFilter{})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to list label values: %w", err)
|
|
}
|
|
|
|
now := time.Now().UnixMilli()
|
|
existingValues := make(map[string]bool)
|
|
for _, val := range values {
|
|
existingValues[val.Value] = true
|
|
}
|
|
|
|
var newValuesCount int
|
|
for _, val := range extValues {
|
|
if existingValues[val] {
|
|
continue
|
|
}
|
|
// define new label values
|
|
if err := migrate.labelValueStore.Define(ctx, &types.LabelValue{
|
|
LabelID: label.ID,
|
|
Value: val,
|
|
Color: defaultLabelValueColor,
|
|
Created: now,
|
|
Updated: now,
|
|
CreatedBy: migratorID,
|
|
UpdatedBy: migratorID,
|
|
}); err != nil {
|
|
return fmt.Errorf("failed to create label value: %w", err)
|
|
}
|
|
newValuesCount++
|
|
}
|
|
|
|
_, err = migrate.labelStore.IncrementValueCount(ctx, label.ID, newValuesCount)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update label value count: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func convertLabelWithSanitization(
|
|
ctx context.Context,
|
|
migrator types.Principal,
|
|
spaceID int64,
|
|
scope int64,
|
|
extLabel ExternalLabel,
|
|
) (*types.Label, error) {
|
|
in := &types.DefineLabelInput{
|
|
Key: extLabel.Name,
|
|
Type: enum.LabelTypeStatic,
|
|
Description: extLabel.Description,
|
|
Color: findClosestColor(ctx, extLabel.Color),
|
|
}
|
|
|
|
if err := in.Sanitize(); err != nil {
|
|
return nil, fmt.Errorf("failed to sanitize external labels input: %w", err)
|
|
}
|
|
|
|
now := time.Now().UnixMilli()
|
|
label := &types.Label{
|
|
SpaceID: &spaceID,
|
|
RepoID: nil,
|
|
Scope: scope,
|
|
Key: in.Key,
|
|
Color: in.Color,
|
|
Description: in.Description,
|
|
Type: in.Type,
|
|
Created: now,
|
|
Updated: now,
|
|
CreatedBy: migrator.ID,
|
|
UpdatedBy: migrator.ID,
|
|
}
|
|
|
|
return label, nil
|
|
}
|
|
|
|
// findClosestColor finds the visually closest color to a provided value using go-colorful library.
|
|
func findClosestColor(ctx context.Context, extColor string) enum.LabelColor {
|
|
supportedColors, defColor := enum.GetAllLabelColors()
|
|
if len(extColor) > 1 && string(extColor[0]) != "#" {
|
|
extColor = "#" + extColor
|
|
}
|
|
targetColor, err := colorful.Hex(strings.ToUpper(extColor))
|
|
if err != nil {
|
|
log.Ctx(ctx).Warn().Err(err).Msg("failed to convert the color to hex. choosing default color instead.")
|
|
return defColor
|
|
}
|
|
closestColor := supportedColors[0]
|
|
minDistance := targetColor.DistanceLab(convertToColorful(supportedColors[0]))
|
|
|
|
for _, labelColor := range supportedColors[1:] {
|
|
distance := targetColor.DistanceLab(convertToColorful(labelColor))
|
|
if distance < minDistance {
|
|
closestColor = labelColor
|
|
minDistance = distance
|
|
}
|
|
}
|
|
|
|
return closestColor
|
|
}
|
|
|
|
// convertToColorful converts Gitness supported label colors to the hex (text value) using web/src/utils:ColorDetails.
|
|
func convertToColorful(color enum.LabelColor) colorful.Color {
|
|
var hexColor colorful.Color
|
|
switch color {
|
|
case enum.LabelColorRed:
|
|
hexColor, _ = colorful.Hex("#C7292F")
|
|
case enum.LabelColorGreen:
|
|
hexColor, _ = colorful.Hex("#16794C")
|
|
case enum.LabelColorYellow:
|
|
hexColor, _ = colorful.Hex("#92582D")
|
|
case enum.LabelColorBlue:
|
|
hexColor, _ = colorful.Hex("#236E93")
|
|
case enum.LabelColorPink:
|
|
hexColor, _ = colorful.Hex("#C41B87")
|
|
case enum.LabelColorPurple:
|
|
hexColor, _ = colorful.Hex("#9C2AAD")
|
|
case enum.LabelColorViolet:
|
|
hexColor, _ = colorful.Hex("#5645AF")
|
|
case enum.LabelColorIndigo:
|
|
hexColor, _ = colorful.Hex("#3250B2")
|
|
case enum.LabelColorCyan:
|
|
hexColor, _ = colorful.Hex("#0B7792")
|
|
case enum.LabelColorOrange:
|
|
hexColor, _ = colorful.Hex("#995137")
|
|
case enum.LabelColorBrown:
|
|
hexColor, _ = colorful.Hex("#805C43")
|
|
case enum.LabelColorMint:
|
|
hexColor, _ = colorful.Hex("#247469")
|
|
case enum.LabelColorLime:
|
|
hexColor, _ = colorful.Hex("#586729")
|
|
default:
|
|
// blue is the default color on Gitness
|
|
hexColor, _ = colorful.Hex("#236E93")
|
|
}
|
|
|
|
return hexColor
|
|
}
|