diff options
author | rillig <rillig@pkgsrc.org> | 2020-02-05 04:09:00 +0000 |
---|---|---|
committer | rillig <rillig@pkgsrc.org> | 2020-02-05 04:09:00 +0000 |
commit | 2a53f3e9bb9e73740d98a8fd0e5910624e2f2d45 (patch) | |
tree | 7994008730ab0bece8a57d520fac2cace121e45e /pkgtools/pkglint | |
parent | 14782f35cb8e5f5261335e4bbae9e497c16a848e (diff) | |
download | pkgsrc-2a53f3e9bb9e73740d98a8fd0e5910624e2f2d45.tar.gz |
pkgtools/pkglint: update to 19.4.7
Changes since 19.4.6:
HOMEPAGE definitions that use http no longer get a warning that they
should migrate to https. Those that could be migrated have been migrated,
and the remaining homepages are not yet ready, so there's no benefit in
having this warning by default. Only in --network mode and when the https
site is indeed reachable, the warning is shown.
In long continued lines, the continuation backslash should be preceded by
a single space. Pkglint had wrongly removed that space in a few cases
before.
Diffstat (limited to 'pkgtools/pkglint')
-rw-r--r-- | pkgtools/pkglint/Makefile | 5 | ||||
-rw-r--r-- | pkgtools/pkglint/files/homepage.go | 89 | ||||
-rw-r--r-- | pkgtools/pkglint/files/homepage_test.go | 135 | ||||
-rw-r--r-- | pkgtools/pkglint/files/line.go | 3 | ||||
-rw-r--r-- | pkgtools/pkglint/files/logging.go | 3 | ||||
-rw-r--r-- | pkgtools/pkglint/files/pkgver/vercmp_test.go | 16 | ||||
-rw-r--r-- | pkgtools/pkglint/files/redundantscope_test.go | 5 | ||||
-rw-r--r-- | pkgtools/pkglint/files/varalignblock.go | 6 | ||||
-rw-r--r-- | pkgtools/pkglint/files/varalignblock_test.go | 25 | ||||
-rw-r--r-- | pkgtools/pkglint/files/vartypecheck_test.go | 1 |
10 files changed, 203 insertions, 85 deletions
diff --git a/pkgtools/pkglint/Makefile b/pkgtools/pkglint/Makefile index 921f3b864b7..81414053c11 100644 --- a/pkgtools/pkglint/Makefile +++ b/pkgtools/pkglint/Makefile @@ -1,7 +1,6 @@ -# $NetBSD: Makefile,v 1.629 2020/02/02 14:19:09 bsiegert Exp $ +# $NetBSD: Makefile,v 1.630 2020/02/05 04:09:00 rillig Exp $ -PKGNAME= pkglint-19.4.6 -PKGREVISION= 1 +PKGNAME= pkglint-19.4.7 CATEGORIES= pkgtools DISTNAME= tools MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} diff --git a/pkgtools/pkglint/files/homepage.go b/pkgtools/pkglint/files/homepage.go index b07fd2f2aa1..044a7a7339b 100644 --- a/pkgtools/pkglint/files/homepage.go +++ b/pkgtools/pkglint/files/homepage.go @@ -40,10 +40,15 @@ type HomepageChecker struct { ValueNoVar string MkLine *MkLine MkLines *MkLines + + // Can be mocked for the tests. + isReachable func(url string) YesNoUnknown } func NewHomepageChecker(value string, valueNoVar string, mkline *MkLine, mklines *MkLines) *HomepageChecker { - return &HomepageChecker{value, valueNoVar, mkline, mklines} + ck := HomepageChecker{value, valueNoVar, mkline, mklines, nil} + ck.isReachable = ck.isReachableOnline + return &ck } func (ck *HomepageChecker) Check() { @@ -119,41 +124,41 @@ func (ck *HomepageChecker) checkHttp() { return } - shouldAutofix, from, to := ck.toHttps(ck.Value) - if from == "" { + migrate, from, to := ck.migrate(ck.Value) + if !migrate { return } fix := ck.MkLine.Autofix() fix.Warnf("HOMEPAGE should migrate from %s to %s.", from, to) - if shouldAutofix { - fix.Replace(from, to) + fix.Replace(from, to) + if from == "http" && to == "https" { + fix.Explain( + "To provide secure communication by default,", + "the HOMEPAGE URL should use the https protocol if available.", + "", + "If the HOMEPAGE really does not support https,", + "add a comment near the HOMEPAGE variable stating this clearly.") } - fix.Explain( - "To provide secure communication by default,", - "the HOMEPAGE URL should use the https protocol if available.", - "", - "If the HOMEPAGE really does not support https,", - "add a comment near the HOMEPAGE variable stating this clearly.") fix.Apply() } -// toHttps checks whether the homepage should be migrated from http to https +// migrate checks whether the homepage should be migrated from http to https // and which part of the homepage URL needs to be modified for that. // // If for some reason the https URL should not be reachable but the // corresponding http URL is, the homepage is changed back to http. -func (ck *HomepageChecker) toHttps(url string) (bool, string, string) { - m, scheme, host, port := match3(url, `(https?)://([A-Za-z0-9-.]+)(:[0-9]+)?`) - if !m { +func (ck *HomepageChecker) migrate(url string) (bool, string, string) { + m, scheme, host := match2(url, `(https?)://([A-Za-z0-9-.]+)(?:/|$)?`) + if !m || containsVarRefLong(url) { return false, "", "" } - if ck.hasAnySuffix(host, - "www.gnustep.org", // 2020-01-18 - "aspell.net", // 2020-01-18 + if scheme == "http" && ck.hasAnySuffix(host, + "www.gnustep.org", // no https as of 2020-01-18 + "aspell.net", // no https as of 2020-01-18 "downloads.sourceforge.net", // gets another warning already - ".dl.sourceforge.net", // gets another warning already + "dl.sourceforge.net", // gets another warning already ) { return false, "", "" } @@ -174,39 +179,36 @@ func (ck *HomepageChecker) toHttps(url string) (bool, string, string) { "nongnu.org", "tryton.org", "tug.org") { - return port == "", "http", "https" + return true, "http", "https" } - if scheme == "http" && host == "sf.net" { - return port == "", "http://sf.net", "https://sourceforge.net" + if host == "sf.net" { + // sf.net redirects to sourceforge.net + return true, scheme + "://sf.net", "https://sourceforge.net" } from := scheme to := "https" - toReachable := unknown // SourceForge projects use either http://project.sourceforge.net or // https://project.sourceforge.io (not net). - if m, project := match1(host, `^([\w-]+)\.(?:sf|sourceforge)\.net$`); m { + if m, project, domain := match2(host, `^([\w-]+)\.((?:sf|sourceforge)\.net)$`); m { + if scheme == "http" { - from = scheme + "://" + host // See https://sourceforge.net/p/forge/documentation/Custom%20VHOSTs + from = "http://" + host to = "https://" + project + ".sourceforge.io" } else { - from = "sourceforge.net" + from = domain to = "sourceforge.io" // Roll back wrong https SourceForge homepages generated by: // https://mail-index.netbsd.org/pkgsrc-changes/2020/01/18/msg205146.html - if port == "" && G.Opts.Network { - _, migrated := replaceOnce(url, from, to) - if ck.isReachable(migrated) == no { - ok, httpOnly := replaceOnce(url, "https://", "http://") - if ok && ck.isReachable(httpOnly) == yes && ck.isReachable(url) == no { - from = "https" - to = "http" - toReachable = yes - } + _, migrated := replaceOnce(url, from, to) + if ck.isReachable(migrated) == no { + _, httpOnly := replaceOnce(url, "https://", "http://") + if ck.isReachable(httpOnly) == yes && ck.isReachable(url) == no { + return true, "https", "http" } } } @@ -216,16 +218,13 @@ func (ck *HomepageChecker) toHttps(url string) (bool, string, string) { return false, "", "" } - shouldAutofix := toReachable == yes - if port == "" && G.Opts.Network && toReachable == unknown { - _, migrated := replaceOnce(url, from, to) - if ck.isReachable(migrated) == yes { - shouldAutofix = true - } else { - return false, "", "" - } + _, migrated := replaceOnce(url, from, to) + migrate := ck.isReachable(migrated) + if migrate == yes { + return true, from, to } - return shouldAutofix, from, to + + return false, "", "" } func (ck *HomepageChecker) checkBadUrls() { @@ -292,7 +291,7 @@ func (ck *HomepageChecker) checkReachable() { } } -func (*HomepageChecker) isReachable(url string) YesNoUnknown { +func (*HomepageChecker) isReachableOnline(url string) YesNoUnknown { switch { case !G.Opts.Network, containsVarRefLong(url), diff --git a/pkgtools/pkglint/files/homepage_test.go b/pkgtools/pkglint/files/homepage_test.go index 98509ffc389..6d1e7b596e4 100644 --- a/pkgtools/pkglint/files/homepage_test.go +++ b/pkgtools/pkglint/files/homepage_test.go @@ -132,14 +132,8 @@ func (s *Suite) Test_HomepageChecker_checkHttp(c *check.C) { "http://asf.net/") vt.Output( - "WARN: filename.mk:2: HOMEPAGE should migrate from http to https.", - "WARN: filename.mk:3: HOMEPAGE should migrate "+ - "from http://project.sourceforge.net "+ - "to https://project.sourceforge.io.", - "WARN: filename.mk:4: HOMEPAGE should migrate "+ - "from http://sf.net to https://sourceforge.net.", - "WARN: filename.mk:5: HOMEPAGE should migrate from http to https.", - "WARN: filename.mk:8: HOMEPAGE should migrate from http to https.") + "WARN: filename.mk:4: HOMEPAGE should migrate " + + "from http://sf.net to https://sourceforge.net.") t.SetUpCommandLine("--autofix") vt.Values( @@ -162,38 +156,119 @@ func (s *Suite) Test_HomepageChecker_checkHttp(c *check.C) { "AUTOFIX: filename.mk:18: Replacing \"http\" with \"https\".") } -func (s *Suite) Test_HomepageChecker_toHttps(c *check.C) { +func (s *Suite) Test_HomepageChecker_migrate(c *check.C) { t := s.Init(c) - test := func(url string, shouldAutofix bool, from, to string) { - toHttps := (*HomepageChecker).toHttps - actualShouldAutofix, actualFrom, actualTo := toHttps(nil, url) + reachable := map[string]YesNoUnknown{} + used := map[string]bool{} + + isReachable := func(url string) YesNoUnknown { + if r, ok := reachable[url]; ok { + used[url] = true + return r + } + panic(url) + } + + test := func(url string, migrate bool, from, to string) { + ck := NewHomepageChecker(url, url, nil, nil) + ck.isReachable = isReachable + + actualMigrate, actualFrom, actualTo := ck.migrate(url) + t.CheckDeepEquals( - []interface{}{actualShouldAutofix, actualFrom, actualTo}, - []interface{}{shouldAutofix, from, to}) + []interface{}{actualMigrate, actualFrom, actualTo}, + []interface{}{migrate, from, to}) + + for key, _ := range reachable { + assertf(used[key], "Reachability of %q was not used.", key) + delete(reachable, key) + } } - test("http://localhost/", false, "http", "https") + reachable["https://localhost/"] = unknown + test( + "http://localhost/", + false, + "", + "") + reachable["https://localhost/"] = yes + test( + "http://localhost/", + true, + "http", + "https") + + reachable["https://project.sourceforge.io/"] = unknown test( "http://project.sourceforge.net/", false, + "", + "") + + reachable["https://project.sourceforge.io/"] = yes + test( + "http://project.sourceforge.net/", + true, "http://project.sourceforge.net", "https://project.sourceforge.io") // To clean up the wrong autofix from 2020-01-18: // https://mail-index.netbsd.org/pkgsrc-changes/2020/01/18/msg205146.html + reachable["https://project.sourceforge.io/"] = yes test( "https://project.sourceforge.net/", - false, + true, "sourceforge.net", "sourceforge.io") + // To clean up the wrong autofix from 2020-01-18: + // https://mail-index.netbsd.org/pkgsrc-changes/2020/01/18/msg205146.html + // + // If neither of the https URLs is reachable, the homepage + // is rolled back to the http URL. + reachable["https://project.sourceforge.io/"] = no + reachable["http://project.sourceforge.net/"] = yes + reachable["https://project.sourceforge.net/"] = no + test( + "https://project.sourceforge.net/", + true, + "https", + "http") + + // To clean up the wrong autofix from 2020-01-18: + // https://mail-index.netbsd.org/pkgsrc-changes/2020/01/18/msg205146.html + // + // If the https URL of sourceforge.net is reachable, + // it is preferred over the http URL, + // even though it is not mentioned in the SourceForge documentation. + reachable["https://project.sourceforge.io/"] = no + reachable["http://project.sourceforge.net/"] = yes + reachable["https://project.sourceforge.net/"] = yes + test( + "https://project.sourceforge.net/", + false, + "", + "") + + // To clean up the wrong autofix from 2020-01-18: + // https://mail-index.netbsd.org/pkgsrc-changes/2020/01/18/msg205146.html + reachable["https://project.sourceforge.io/"] = no + reachable["http://project.sourceforge.net/"] = no + test( + "https://project.sourceforge.net/", + false, + "", + "") + + // Since the URL contains a variable, it cannot be resolved. + // Therefore it is skipped without any HTTP request being sent. test( "http://godoc.org/${GO_SRCPATH}", false, - "http", - "https") + "", + "") } func (s *Suite) Test_HomepageChecker_checkBadUrls(c *check.C) { @@ -259,23 +334,17 @@ func (s *Suite) Test_HomepageChecker_checkReachable(c *check.C) { "http://localhost:28780/status/500") vt.Output( - "WARN: filename.mk:1: HOMEPAGE should migrate from http to https.", - "WARN: filename.mk:2: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:2: Homepage "+ "\"http://localhost:28780/status/301?location=/redirect301\" "+ "redirects to \"http://localhost:28780/redirect301\".", - "WARN: filename.mk:3: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:3: Homepage "+ "\"http://localhost:28780/status/302?location=/redirect302\" "+ "redirects to \"http://localhost:28780/redirect302\".", - "WARN: filename.mk:4: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:4: Homepage "+ "\"http://localhost:28780/status/307?location=/redirect307\" "+ "redirects to \"http://localhost:28780/redirect307\".", - "WARN: filename.mk:5: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:5: Homepage \"http://localhost:28780/status/404\" "+ "returns HTTP status \"404 Not Found\".", - "WARN: filename.mk:6: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:6: Homepage \"http://localhost:28780/status/500\" "+ "returns HTTP status \"500 Internal Server Error\".") @@ -283,15 +352,13 @@ func (s *Suite) Test_HomepageChecker_checkReachable(c *check.C) { "http://localhost:28780/timeout") vt.Output( - "WARN: filename.mk:11: HOMEPAGE should migrate from http to https.", - "WARN: filename.mk:11: Homepage \"http://localhost:28780/timeout\" "+ + "WARN: filename.mk:11: Homepage \"http://localhost:28780/timeout\" " + "cannot be checked: timeout") vt.Values( "http://localhost:28780/%invalid") vt.Output( - "WARN: filename.mk:21: HOMEPAGE should migrate from http to https.", "ERROR: filename.mk:21: Invalid URL \"http://localhost:28780/%invalid\".") vt.Values( @@ -299,8 +366,7 @@ func (s *Suite) Test_HomepageChecker_checkReachable(c *check.C) { // The "unknown network error" is for compatibility with Go < 1.13. t.CheckOutputMatches( - "WARN: filename.mk:31: HOMEPAGE should migrate from http to https.", - `^WARN: filename\.mk:31: Homepage "http://localhost:28781/" `+ + `^WARN: filename\.mk:31: Homepage "http://localhost:28781/" ` + `cannot be checked: (connection refused|unknown network error:.*)$`) vt.Values( @@ -310,9 +376,15 @@ func (s *Suite) Test_HomepageChecker_checkReachable(c *check.C) { t.CheckOutputMatches( `^WARN: filename\.mk:41: Homepage "https://no-such-name.example.org/" ` + `cannot be checked: (name not found|unknown network error:.*)$`) + + vt.Values( + "https://!!!invalid/") + + t.CheckOutputLines( + "WARN: filename.mk:51: \"https://!!!invalid/\" is not a valid URL.") } -func (s *Suite) Test_HomepageChecker_isReachable(c *check.C) { +func (s *Suite) Test_HomepageChecker_isReachableOnline(c *check.C) { t := s.Init(c) t.SetUpCommandLine("--network") @@ -352,7 +424,8 @@ func (s *Suite) Test_HomepageChecker_isReachable(c *check.C) { }() test := func(url string, reachable YesNoUnknown) { - actual := (*HomepageChecker).isReachable(nil, url) + ck := NewHomepageChecker(url, url, nil, nil) + actual := ck.isReachableOnline(url) t.CheckEquals(actual, reachable) } diff --git a/pkgtools/pkglint/files/line.go b/pkgtools/pkglint/files/line.go index af4a4c49677..dd6f3708105 100644 --- a/pkgtools/pkglint/files/line.go +++ b/pkgtools/pkglint/files/line.go @@ -144,6 +144,9 @@ func (line *Line) IsCvsID(prefixRe regex.Pattern) (found bool, expanded bool) { return m, exp != "" } +// FIXME: By definition there cannot be fatal diagnostics. +// Having these was a misconception from the beginning, +// and they must be re-classified as fatal technical errors. func (line *Line) Fatalf(format string, args ...interface{}) { if trace.Tracing { trace.Stepf("Fatalf: %q, %v", format, args) diff --git a/pkgtools/pkglint/files/logging.go b/pkgtools/pkglint/files/logging.go index 818be49d52f..918235f9259 100644 --- a/pkgtools/pkglint/files/logging.go +++ b/pkgtools/pkglint/files/logging.go @@ -58,6 +58,9 @@ type LogLevel struct { } var ( + // FIXME: By definition there cannot be fatal diagnostics. + // Having these was a misconception from the beginning, + // and they must be re-classified as fatal technical errors. Fatal = &LogLevel{"FATAL", "fatal"} Error = &LogLevel{"ERROR", "error"} Warn = &LogLevel{"WARN", "warning"} diff --git a/pkgtools/pkglint/files/pkgver/vercmp_test.go b/pkgtools/pkglint/files/pkgver/vercmp_test.go index a1b725a764a..a3ecd8066c4 100644 --- a/pkgtools/pkglint/files/pkgver/vercmp_test.go +++ b/pkgtools/pkglint/files/pkgver/vercmp_test.go @@ -85,12 +85,28 @@ func (s *Suite) Test_newVersion(c *check.C) { &version{nil, 1}) c.Check(newVersion("1.0.1a"), check.DeepEquals, &version{[]int{1, 0, 0, 0, 1, 1}, 0}) + c.Check(newVersion("1.1.1dnb2"), check.DeepEquals, + &version{[]int{1, 0, 1, 0, 1, 4}, 2}) c.Check(newVersion("1.0.1z"), check.DeepEquals, &version{[]int{1, 0, 0, 0, 1, 26}, 0}) c.Check(newVersion("0pre20160620"), check.DeepEquals, &version{[]int{0, -1, 20160620}, 0}) c.Check(newVersion("3.5.DEV1710"), check.DeepEquals, &version{[]int{3, 0, 5, 0, 4, 5, 22, 1710}, 0}) + + // In the following edge case, the "nb" and "beta" overlap. + // All the digits after the "nb" (which in this case are none at all) + // end up in the nb version part, and parsing continues with the next + // letter. + // + // Luckily this will not happen in practice since most version numbers + // are completely numeric, and those that aren't might have suffixes + // like "alpha", "beta", "public beta", "GA" (general availability), + // "final", "snapshot". The word "nonbeta" is purely hypothetical, and + // I didn't find any other word that would contain "nbeta". Commit + // hashes are also safe since their hex encoding cannot contain "n". + c.Check(newVersion("1.0nonbeta"), check.DeepEquals, + &version{[]int{1, 0, 0, 14, 15, 5, 20, 1}, 0}) } func (s *Suite) Test__qa(c *check.C) { diff --git a/pkgtools/pkglint/files/redundantscope_test.go b/pkgtools/pkglint/files/redundantscope_test.go index bdf7e929114..cc907a71b2c 100644 --- a/pkgtools/pkglint/files/redundantscope_test.go +++ b/pkgtools/pkglint/files/redundantscope_test.go @@ -1445,10 +1445,7 @@ func (s *Suite) Test_RedundantScope__is_relevant_for_infrastructure(c *check.C) scope := NewRedundantScope() scope.IsRelevant = func(mkline *MkLine) bool { // See checkfilePackageMakefile. - if !G.Infrastructure && !G.Opts.CheckGlobal { - return !G.Pkgsrc.IsInfra(mkline.Filename) - } - return true + return G.Opts.CheckGlobal || !G.Pkgsrc.IsInfra(mkline.Filename) } scope.Check(mklines) diff --git a/pkgtools/pkglint/files/varalignblock.go b/pkgtools/pkglint/files/varalignblock.go index c5fe8969879..ed624692358 100644 --- a/pkgtools/pkglint/files/varalignblock.go +++ b/pkgtools/pkglint/files/varalignblock.go @@ -655,6 +655,9 @@ func (info *varalignLine) alignContinuation(valueColumn, rightMarginColumn int) fix.Notef("The continuation backslash should be preceded by a single space.") } else { newSpace = alignmentToWidths(info.uptoValueWidth(), rightMarginColumn) + if newSpace == "" { + newSpace = " " + } fix.Notef( "The continuation backslash should be in column %d, not %d.", rightMarginColumn+1, column+1) @@ -852,6 +855,9 @@ func (p *varalignParts) isCanonicalFollow() bool { } func (p *varalignParts) isTooLongFor(valueColumn int) bool { + // FIXME: As of 2020-01-27, this method is called from + // Test_VaralignBlock__right_margin with valueColumn == 0, + // which doesn't make sense. It should at least be 8. column := tabWidthAppend(valueColumn, p.value) if p.isContinuation() { column += 2 diff --git a/pkgtools/pkglint/files/varalignblock_test.go b/pkgtools/pkglint/files/varalignblock_test.go index b679cb34a81..4a38595b7e6 100644 --- a/pkgtools/pkglint/files/varalignblock_test.go +++ b/pkgtools/pkglint/files/varalignblock_test.go @@ -218,7 +218,7 @@ func (vt *VaralignTester) checkTestName() { } vt.tester.CheckDeepEquals(descriptorsString(actual), descriptorsString(expected)) - vt.tester.CheckDeepEquals(actual, expected) + vt.tester.CheckDeepEquals(expected, actual) } func (s *Suite) Test_VaralignBlock__var_none_value(c *check.C) { @@ -2063,6 +2063,29 @@ func (s *Suite) Test_VaralignBlock__var_tab_value63_space_cont_tab8_value71_spac vt.Run() } +// Up to 2020-01-27, pkglint removed all spaces before the backslash, +// which was against the rule of having at least one space. +func (s *Suite) Test_VaralignBlock__right_margin(c *check.C) { + vt := NewVaralignTester(s, c) + vt.Input( + "CONFIGURE_ARGS+=\t\\", + "\t.....................................................................79\t\\", + "\t............................................................70 \t\t\\", + "\t........................................................66") + vt.Diagnostics( + "NOTE: Makefile:2: The continuation backslash should be in column 73, not 81.", + "NOTE: Makefile:3: The continuation backslash should be in column 73, not 81.") + vt.Autofixes( + "AUTOFIX: Makefile:2: Replacing \"\\t\" with \" \".", + "AUTOFIX: Makefile:3: Replacing \" \\t\\t\" with \"\\t\".") + vt.Fixed( + "CONFIGURE_ARGS+= \\", + " .....................................................................79 \\", + " ............................................................70 \\", + " ........................................................66") + vt.Run() +} + // Up to 2018-01-27, it could happen that some source code was logged // without a corresponding diagnostic. This was unintended and confusing. func (s *Suite) Test_VaralignBlock__fix_without_diagnostic(c *check.C) { diff --git a/pkgtools/pkglint/files/vartypecheck_test.go b/pkgtools/pkglint/files/vartypecheck_test.go index 7c78e1b6057..9ffe8c036ae 100644 --- a/pkgtools/pkglint/files/vartypecheck_test.go +++ b/pkgtools/pkglint/files/vartypecheck_test.go @@ -917,7 +917,6 @@ func (s *Suite) Test_VartypeCheck_Homepage(c *check.C) { "${MASTER_SITES}") vt.Output( - "WARN: filename.mk:1: HOMEPAGE should migrate from http to https.", "WARN: filename.mk:3: HOMEPAGE should not be defined in terms of MASTER_SITEs.") // For more tests, see HomepageChecker. |