• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / deployments / 920043239

pending completion
920043239

Pull #872

gitlab-ci

alfrunes
chore: Restrict tag character set

Changelog: None
Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #872: MEN-6348: Add tags to releases

220 of 229 new or added lines in 7 files covered. (96.07%)

223 existing lines in 7 files now uncovered.

7560 of 9480 relevant lines covered (79.75%)

34.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.86
/model/release.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14

15
package model
16

17
import (
18
        "encoding/json"
19
        "errors"
20
        "fmt"
21
        "sort"
22
        "strconv"
23
        "strings"
24
        "time"
25
)
26

27
const (
28
        TagsMaxPerRelease = 20  // Maximum number of tags per release.
29
        TagsMaxUnique     = 100 // Maximum number of unique tags.
30
)
31

32
var (
33
        ErrTooManyTags = errors.New(
34
                "the total number of tags per exceeded maximum of " +
35
                        strconv.Itoa(TagsMaxPerRelease),
36
        )
37
        ErrTooManyUniqueTags = errors.New(
38
                "the total number of unique tags (" +
39
                        strconv.Itoa(TagsMaxUnique) +
40
                        ") has been exceeded",
41
        )
42
)
43

44
type Tags []Tag
45

46
func (tags Tags) Validate() (err error) {
2✔
47
        if len(tags) > TagsMaxPerRelease {
3✔
48
                return ErrTooManyTags
1✔
49
        }
1✔
50
        for _, tag := range tags {
3✔
51
                if err = tag.Validate(); err != nil {
3✔
52
                        return err
1✔
53
                }
1✔
54
        }
NEW
55
        return nil
×
56
}
57

58
func (tags Tags) MarshalJSON() ([]byte, error) {
2✔
59
        if len(tags) == 0 {
4✔
60
                return []byte{'[', ']'}, nil
2✔
61
        }
2✔
NEW
62
        return json.Marshal([]Tag(tags))
×
63
}
64

65
func (tags *Tags) Dedup() {
1✔
66
        // Deduplicate tags:
1✔
67
        s := *tags
1✔
68
        sort.Slice(s, func(i, j int) bool {
4✔
69
                return s[i] < s[j]
3✔
70
        })
3✔
71
        i, j := 0, 0
1✔
72
        for j < len(s) {
4✔
73
                if s[i] != s[j] {
4✔
74
                        i++
1✔
75
                        s[i] = s[j]
1✔
76
                }
1✔
77
                j++
3✔
78
        }
79
        *tags = s[:i+1]
1✔
80
}
81

82
func (tags *Tags) UnmarshalJSON(b []byte) error {
2✔
83
        err := json.Unmarshal(b, (*[]Tag)(tags))
2✔
84
        if err != nil {
3✔
85
                return err
1✔
86
        }
1✔
87
        tags.Dedup()
1✔
88
        return nil
1✔
89
}
90

91
type Tag string
92

93
const (
94
        TagMaxLength = 1024
95
)
96

97
var (
98
        ErrTagEmpty   = errors.New("tag cannot be empty")
99
        ErrTagTooLong = errors.New("tag must be less than " +
100
                strconv.Itoa(TagMaxLength) +
101
                " characters")
102
)
103

104
type InvalidCharacterError struct {
105
        Source string
106
        Char   rune
107
}
108

NEW
109
func (err *InvalidCharacterError) Error() string {
×
NEW
110
        return fmt.Sprintf(`invalid character '%c' in string "%s"`, err.Char, err.Source)
×
NEW
111
}
×
112

113
func (tag Tag) Validate() error {
4✔
114
        if len(tag) < 1 {
5✔
115
                return ErrTagEmpty
1✔
116
        } else if len(tag) > TagMaxLength {
5✔
117
                return ErrTagTooLong
1✔
118
        }
1✔
119
        for _, c := range tag { // [A-Za-z0-9-_.]
12✔
120
                if c >= 'A' && c <= 'Z' {
11✔
121
                        continue
1✔
122
                } else if c >= 'a' && c <= 'z' {
15✔
123
                        continue
6✔
124
                } else if c >= '0' && c <= '9' {
4✔
125
                        continue
1✔
126
                } else if c == '-' || c == '_' || c == '.' {
3✔
127
                        continue
1✔
128
                } else {
1✔
129
                        return &InvalidCharacterError{
1✔
130
                                Source: string(tag),
1✔
131
                                Char:   c,
1✔
132
                        }
1✔
133
                }
1✔
134
        }
135
        return nil
1✔
136
}
137

138
func (tag *Tag) UnmarshalJSON(b []byte) error {
3✔
139
        // Convert tag to lower case
3✔
140
        var s string
3✔
141
        err := json.Unmarshal(b, &s)
3✔
142
        if err != nil {
3✔
NEW
143
                return err
×
NEW
144
        }
×
145
        *tag = Tag(strings.ToLower(s))
3✔
146
        return nil
3✔
147
}
148

149
type Release struct {
150
        Name      string     `json:"Name" bson:"_id"`
151
        Modified  *time.Time `json:"Modified,omitempty" bson:"modified,omitempty"`
152
        Artifacts []Image    `json:"Artifacts" bson:"artifacts"`
153
        Tags      Tags       `json:"tags" bson:"tags,omitempty"`
154
}
155

156
type ReleaseOrImageFilter struct {
157
        Name        string `json:"name"`
158
        Description string `json:"description"`
159
        DeviceType  string `json:"device_type"`
160
        Page        int    `json:"page"`
161
        PerPage     int    `json:"per_page"`
162
        Sort        string `json:"sort"`
163
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc