Skip to content

Commit b07a1ae

Browse files
committed
wip
1 parent 53101de commit b07a1ae

File tree

6 files changed

+118
-55
lines changed

6 files changed

+118
-55
lines changed

fields.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,43 +48,30 @@ func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error
4848
}
4949

5050
if publiccodev0.Logo != "" {
51-
<<<<<<< HEAD
5251
if _, err := isRelativePathOrURL(publiccodev0.Logo, "logo"); err != nil {
5352
vr = append(vr, err)
5453
} else if validLogo, err := parser.validLogo(toCodeHostingURL(publiccodev0.Logo, parser.baseURL), network); !validLogo {
55-
=======
56-
if validLogo, err := parser.validLogo(toCodeHostingURL(publiccodev0.Logo, parser.baseURL, parser.localFilePath), network); !validLogo {
57-
>>>>>>> relative-file
5854
vr = append(vr, newValidationError("logo", err.Error()))
5955
}
6056
}
6157

6258
if publiccodev0.MonochromeLogo != "" {
6359
vr = append(vr, ValidationWarning{"monochromeLogo", "This key is DEPRECATED and will be removed in the future", 0, 0})
6460

65-
<<<<<<< HEAD
6661
if _, err := isRelativePathOrURL(publiccodev0.MonochromeLogo, "monochromeLogo"); err != nil {
6762
vr = append(vr, err)
6863
} else if validLogo, err := parser.validLogo(toCodeHostingURL(publiccodev0.MonochromeLogo, parser.baseURL), network); !validLogo {
69-
=======
70-
if validLogo, err := parser.validLogo(toCodeHostingURL(publiccodev0.MonochromeLogo, parser.baseURL, parser.localFilePath), network); !validLogo {
71-
>>>>>>> relative-file
7264
vr = append(vr, newValidationError("monochromeLogo", err.Error()))
7365
}
7466
}
7567

7668
if publiccodev0.Legal.AuthorsFile != nil {
7769
vr = append(vr, ValidationWarning{"legal.authorsFile", "This key is DEPRECATED and will be removed in the future", 0, 0})
7870

79-
<<<<<<< HEAD
8071
if _, err := isRelativePathOrURL(*publiccodev0.Legal.AuthorsFile, "legal.authorsFile"); err != nil {
8172
vr = append(vr, err)
8273
} else if exists, err := parser.fileExists(toCodeHostingURL(*publiccodev0.Legal.AuthorsFile, parser.baseURL), network); !exists {
8374
u := toCodeHostingURL(*publiccodev0.Legal.AuthorsFile, parser.baseURL)
84-
=======
85-
if !parser.fileExists(toCodeHostingURL(*publiccodev0.Legal.AuthorsFile, parser.baseURL, parser.localFilePath), network) {
86-
u := toCodeHostingURL(*publiccodev0.Legal.AuthorsFile, parser.baseURL, parser.localFilePath)
87-
>>>>>>> relative-file
8875

8976
vr = append(vr, newValidationError("legal.authorsFile", "'%s' does not exist: %s", urlutil.DisplayURL(&u), err.Error()))
9077
}
@@ -129,14 +116,10 @@ func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error
129116
}
130117

131118
for i, v := range desc.Screenshots {
132-
<<<<<<< HEAD
133119
keyName := fmt.Sprintf("description.%s.screenshots[%d]", lang, i)
134120
if _, err := isRelativePathOrURL(v, keyName); err != nil {
135121
vr = append(vr, err)
136122
} else if isImage, err := parser.isImageFile(toCodeHostingURL(v, parser.baseURL), network); !isImage {
137-
=======
138-
if isImage, err := parser.isImageFile(toCodeHostingURL(v, parser.baseURL, parser.localFilePath), network); !isImage {
139-
>>>>>>> relative-file
140123
vr = append(vr, newValidationError(
141124
keyName,
142125
"'%s' is not an image: %s", v, err.Error(),

parser.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"net/url"
99
"os"
10+
"path"
1011
"path/filepath"
1112
"regexp"
1213
"slices"
@@ -48,7 +49,7 @@ type Parser struct {
4849
domain Domain
4950
branch string
5051
baseURL *url.URL
51-
localFilePath string
52+
fileURL *url.URL
5253
}
5354

5455
// Domain is a single code hosting service.
@@ -69,6 +70,7 @@ func NewParser(config ParserConfig) (*Parser, error) {
6970
}
7071

7172
if config.BaseURL != "" {
73+
fmt.Printf("-- setting baseURL from config %s\n", config.BaseURL)
7274
var err error
7375
if parser.baseURL, err = toURL(config.BaseURL); err != nil {
7476
return nil, err
@@ -218,11 +220,22 @@ func (p *Parser) ParseStream(in io.Reader) (PublicCode, error) {
218220
}
219221
}
220222

221-
// baseURL was not set to a local path, let's autodetect it from the
222-
// publiccode.yml url key
223-
//
224-
// We need the baseURL to perform network checks.
225-
if p.baseURL == nil && !p.disableNetwork && publiccode.Url() != nil {
223+
fmt.Printf(":::::::p.baseURL: %+v\n", p.baseURL)
224+
var bURL *url.URL
225+
// baseURL was not set by the user (with ParserConfig{BaseURL: "..."})),
226+
// We need a base URL to perform external checks on relative files (eg. logo).
227+
if p.baseURL == nil {
228+
fmt.Printf(":::::::p.fileURL: %+v\n", p.fileURL)
229+
// If we parsed from an actual local or remote file, use its dir
230+
if p.fileURL != nil {
231+
bURL := *p.fileURL
232+
bURL.Path = path.Dir(u.Path)
233+
fmt.Printf("setting baseURL to %s\n", bURL.String())
234+
}
235+
}
236+
237+
// Still no base URL: we parsed from a stream, try to use the publiccode.yml's `url` field
238+
if bURL == nil && !p.disableNetwork && publiccode.Url() != nil {
226239
rawRoot, err := vcsurl.GetRawRoot((*url.URL)(publiccode.Url()), p.branch)
227240
if err != nil {
228241
line, column := getPositionInFile("url", node)
@@ -234,12 +247,24 @@ func (p *Parser) ParseStream(in io.Reader) (PublicCode, error) {
234247
Column: column,
235248
})
236249

237-
// Return early because proceeding with no baseURL would result in a lot
250+
// Return early because proceeding with no base URL would result in a lot
238251
// of duplicate errors stemming from its absence.
239252
return publiccode, ve
240253
}
241254

242-
p.baseURL = rawRoot
255+
bURL = rawRoot
256+
}
257+
258+
// Still no base URL: DisableNetwork is true, use the current working directory as a fallback
259+
if bURL == nil {
260+
cwd, err := os.Getwd()
261+
if err != nil {
262+
ve = append(ve, newValidationError("", fmt.Sprintf("no baseURL set and failed to get working directory: %s", err)))
263+
264+
return publiccode, ve
265+
}
266+
267+
bURL = &url.URL{Scheme: "file", Path: cwd}
243268
}
244269

245270
if err = validateFields(publiccode, *p, !p.disableNetwork); err != nil {
@@ -270,12 +295,14 @@ func (p *Parser) Parse(uri string) (PublicCode, error) {
270295
return nil, fmt.Errorf("invalid URL '%s': %w", uri, err)
271296
}
272297

298+
p.fileURL = url
299+
fmt.Printf("setting fileURL to %s\n", p.fileURL.String())
300+
273301
if url.Scheme == "file" {
274302
stream, err = os.Open(url.Path)
275303
if err != nil {
276304
return nil, fmt.Errorf("can't open file '%s': %w", url.Path, err)
277305
}
278-
p.localFilePath = url.Path
279306
} else {
280307
resp, err := http.Get(uri)
281308
if err != nil {
@@ -284,6 +311,7 @@ func (p *Parser) Parse(uri string) (PublicCode, error) {
284311

285312
defer func() {
286313
_ = resp.Body.Close()
314+
p.fileURL = nil
287315
}()
288316

289317
stream = resp.Body

parser_test.go

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -692,24 +692,28 @@ func TestDecodeValueErrorsRemote(t *testing.T) {
692692
// are checked and expanded correctly
693693
func TestRelativePathsOrURLs(t *testing.T) {
694694
testRemoteFiles := []testType{
695-
// Should look for the logo relative to this URL
695+
// Remote publiccode.yml and relative path in screenshots:
696+
// should look for the screenshot remotely relative to this URL
696697
{"https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/relative-paths/testdata/v0/invalid/description_en_screenshots_missing_file.yml", ValidationResults{
697-
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: HTTP GET failed for https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/testdata/v0/invalid/no_such_file.png: not found", 20, 5},
698+
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: HTTP GET failed for https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/relative-paths/testdata/v0/invalid/no_such_file.png: not found", 20, 5},
698699
}},
699700

700-
// Should look for the logo relative to this path
701+
// Local publiccode.yml and relative path in screenshot:
702+
// should look for the logo relative to this path in the filesystem
701703
{"testdata/v0/invalid/description_en_screenshots_missing_file.yml", ValidationResults{
702704
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: no_such_file.png: not found", 20, 5},
703705
}},
704706

705-
//
707+
// Remote publiccode.yml and URL in logo:
708+
// should look for the logo remotely
706709
{"https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/relative-paths/testdata/v0/invalid/logo_missing_url.yml", ValidationResults{
707-
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: HTTP GET failed for https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/testdata/v0/invalid/no_such_file.png: not found", 20, 5},
710+
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: HTTP GET failed for https://google.com/no_such_file.png: not found", 20, 5},
708711
}},
709712

710-
//
713+
// Local publiccode.yml and URL in logo:
714+
// should look for the logo remotely
711715
{"testdata/v0/invalid/logo_missing_url.yml", ValidationResults{
712-
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: no_such_file.png: not found", 20, 5},
716+
ValidationError{"description.en.screenshots[0]", "'no_such_file.png' is not an image: HTTP GET failed for https://google.com/no_such_file.png: not found", 20, 5},
713717
}},
714718
}
715719

@@ -729,6 +733,51 @@ func TestRelativePathsOrURLs(t *testing.T) {
729733
}
730734
}
731735

736+
// Test that files in fields with relative paths or URLs (ie. logo, screenshots, etc.)
737+
// are checked and expanded correctly when DisableNetwork is true
738+
func TestRelativePathsOrURLsNoNetwork(t *testing.T) {
739+
testRemoteFiles := []testType{
740+
// Remote publiccode.yml and relative path in screenshots:
741+
// should look for the screenshot remotely relative to this URL
742+
{"https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/relative-paths/testdata/v0/invalid/description_en_screenshots_missing_file.yml", ValidationResults{
743+
ValidationError{"description.en.screenshots[0]", "'n_such_file.png' is not an image: HTTP GET failed for https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/testdata/v0/invalid/no_such_file.png: not found", 20, 5},
744+
}},
745+
746+
// Local publiccode.yml and relative path in screenshot:
747+
// should look for the logo relative to this path in the filesystem
748+
{"testdata/v0/invalid/description_en_screenshots_missing_file.yml", ValidationResults{
749+
ValidationError{"description.en.screenshots[0]", "'n_such_file.png' is not an image: no_such_file.png: not found", 20, 5},
750+
}},
751+
752+
// Remote publiccode.yml and URL in logo:
753+
// should look for the logo remotely
754+
{"https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/relative-paths/testdata/v0/invalid/logo_missing_url.yml", ValidationResults{
755+
ValidationError{"description.en.screenshots[0]", "'no_sch_file.png' is not an image: HTTP GET failed for https://google.com/no_such_file.png: not found", 20, 5},
756+
}},
757+
758+
// Local publiccode.yml and URL in logo:
759+
// should look for the logo remotely
760+
{"testdata/v0/invalid/logo_missing_url.yml", ValidationResults{
761+
ValidationError{"description.en.screenshots[0]", "'no_sch_file.png' is not an image: HTTP GET failed for https://google.com/no_such_file.png: not found", 20, 5},
762+
}},
763+
}
764+
765+
parser, err := NewParser(ParserConfig{DisableNetwork: true})
766+
if err != nil {
767+
t.Errorf("Can't create parser: %v", err)
768+
}
769+
770+
for _, test := range testRemoteFiles {
771+
t.Run(fmt.Sprintf("%v", test.err), func(t *testing.T) {
772+
var err error
773+
774+
_, err = parser.Parse(test.file)
775+
776+
checkParseErrors(t, err, test)
777+
})
778+
}
779+
}
780+
732781
func TestUrlMissingWithoutPath(t *testing.T) {
733782
expected := map[string]error{
734783
"url_missing.yml": ValidationResults{

testdata/v0/valid/logo_with_url.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ publiccodeYmlVersion: "0.4"
33
name: Medusa
44
url: "https://github.com/italia/developers.italia.it.git"
55

6-
# Logo with an URL
6+
# Logo with a URL
77
logo: "https://raw.githubusercontent.com/italia/publiccode-parser-go/refs/heads/main/testdata/img/logo.png"
88

99
platforms:

testdata/v0/valid/valid.yml

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ applicationSuite: MegaProductivitySuite
88
url: "https://github.com/italia/developers.italia.it.git"
99
landingURL: "https://developers.italia.it"
1010

11+
logo: "foobar"
12+
1113
isBasedOn: "https://github.com/italia/developers.italia.it.git"
1214
softwareVersion: "1.0"
1315
releaseDate: 2017-04-15
@@ -146,31 +148,31 @@ description:
146148
localisedName: Medusa
147149
shortDescription: "A really interesting software."
148150
longDescription: >
149-
Very long description of this software, also split
150-
on multiple rows. You should note what the software
151-
is and why one should need it. This is 158 characters.
152-
Very long description of this software, also split
153-
on multiple rows. You should note what the software
154-
is and why one should need it. This is 316 characters.
155-
Very long description of this software, also split
156-
on multiple rows. You should note what the software
157-
is and why one should need it. This is 474 characters.
158-
Very long description of this software, also split
159-
on multiple rows. You should note what the software
160-
is and why one should need it. This is 632 characters.
151+
Very long description of this software, also split
152+
on multiple rows. You should note what the software
153+
is and why one should need it. This is 158 characters.
154+
Very long description of this software, also split
155+
on multiple rows. You should note what the software
156+
is and why one should need it. This is 316 characters.
157+
Very long description of this software, also split
158+
on multiple rows. You should note what the software
159+
is and why one should need it. This is 474 characters.
160+
Very long description of this software, also split
161+
on multiple rows. You should note what the software
162+
is and why one should need it. This is 632 characters.
161163
162164
documentation: "https://docs.italia.it"
163165
apiDocumentation: "https://developers.italia.it/it/api"
164166

165167
features:
166-
- Very important feature
167-
- Will run without a problem
168-
- Has zero bugs
169-
- Solves all the problems of the world
168+
- Very important feature
169+
- Will run without a problem
170+
- Has zero bugs
171+
- Solves all the problems of the world
170172
videos:
171-
- https://www.youtube.com/watch?v=RaHmGbBOP84
173+
- https://www.youtube.com/watch?v=RaHmGbBOP84
172174
awards:
173-
- 1st Price Software of the year
175+
- 1st Price Software of the year
174176

175177
legal:
176178
license: AGPL-3.0-or-later

validations.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (p *Parser) isReachable(u url.URL, network bool) (bool, error) {
9696
//
9797
// It supports relative paths and turns them into remote URLs or file:// URLs
9898
// depending on the value of baseURL
99-
func toCodeHostingURL(file string, baseURL *url.URL, localFilePath string) url.URL {
99+
func toCodeHostingURL(file string, baseURL *url.URL) url.URL {
100100
// Check if file is an absolute URL
101101
if uri, err := url.ParseRequestURI(file); err == nil {
102102
if raw := vcsurl.GetRawFile(uri); raw != nil {
@@ -116,8 +116,9 @@ func toCodeHostingURL(file string, baseURL *url.URL, localFilePath string) url.U
116116
return u
117117
}
118118

119-
// It's a local file
120-
return url.URL{Scheme: "file", Path: path.Join(localFilePath, file)}
119+
// No baseURL, so DisableNetwork == true and all we can fallback to
120+
// is treating it as a local file from the current directory
121+
return url.URL{Scheme: "file", Path: path.Join(baseURL.Path, file)}
121122
}
122123

123124
// fileExists returns true if the file resource exists.

0 commit comments

Comments
 (0)