all: replace `import parent` with an interpreter line hack
We'd like to run our executable python scripts using our new `pywrap`
feature as a better way to get their PYTHONPATH setup properly. This
script lives in our source tree, at a fixed relative location to each
script that we're interested in, but has no absolute system path.
The #!interpreter syntax does supports relative paths, but they are
resolved relative to the current working directory, not relative to the
file in which they appear, which makes this feature pretty useless.
There are some tricks we can do to get our designed 'relative'
behaviour, though.
Something like this works:
#!/usr/bin/env -S sh -c '"${0%/*}/pywrap" "$0"'
but it causes various file auto-detection schemes (including our own
test/static-code) to improperly conclude that this is a shell script,
rather than a Python script. (n)vim also gets it wrong for highlighting
and linting purposes.
Fortunately there's a somewhat longer, but still acceptable version of
this we can do in Python:
#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/pywrap", sys.argv)
This fits well under our 118 column limit.
Note that unlike `sh`, the `-c` option in Python doesn't require the
script to be passed as a separate argument, which means we don't need to
use the non-POSIX splitting tricks of `/usr/bin/env -S`.
Note also: it appears as though our hardcoding of /usr/bin/python3 here
might be venv-unfriendly, but it's not: /usr/bin/python3 (which surely
exists) is used only for running this one tiny fragment of code. Our
pywrap script itself finds python3 in the path.
Let's move all of our previous users of `import parent` over to the new
approach.
We can remove a couple of `parent.py` symlinks we had lying
around, as well.
We also remove `parent.py` itself, since otherwise vulture complains
about it being unused. Subprojects will be forced to adjust on upgrade.
Remove a CodeQL exemption that was necessitated by our `import parent`
shenanigans.
2023-05-26 00:57:53 -08:00
|
|
|
#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/../test/common/pywrap", sys.argv)
|
2020-01-29 01:34:49 -09:00
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Copyright (C) 2020 Red Hat, Inc.
|
2026-02-01 03:22:50 -09:00
|
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
|
|
|
|
|
import argparse
|
2022-02-10 03:18:30 -09:00
|
|
|
import fnmatch
|
all: replace `import parent` with an interpreter line hack
We'd like to run our executable python scripts using our new `pywrap`
feature as a better way to get their PYTHONPATH setup properly. This
script lives in our source tree, at a fixed relative location to each
script that we're interested in, but has no absolute system path.
The #!interpreter syntax does supports relative paths, but they are
resolved relative to the current working directory, not relative to the
file in which they appear, which makes this feature pretty useless.
There are some tricks we can do to get our designed 'relative'
behaviour, though.
Something like this works:
#!/usr/bin/env -S sh -c '"${0%/*}/pywrap" "$0"'
but it causes various file auto-detection schemes (including our own
test/static-code) to improperly conclude that this is a shell script,
rather than a Python script. (n)vim also gets it wrong for highlighting
and linting purposes.
Fortunately there's a somewhat longer, but still acceptable version of
this we can do in Python:
#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/pywrap", sys.argv)
This fits well under our 118 column limit.
Note that unlike `sh`, the `-c` option in Python doesn't require the
script to be passed as a separate argument, which means we don't need to
use the non-POSIX splitting tricks of `/usr/bin/env -S`.
Note also: it appears as though our hardcoding of /usr/bin/python3 here
might be venv-unfriendly, but it's not: /usr/bin/python3 (which surely
exists) is used only for running this one tiny fragment of code. Our
pywrap script itself finds python3 in the path.
Let's move all of our previous users of `import parent` over to the new
approach.
We can remove a couple of `parent.py` symlinks we had lying
around, as well.
We also remove `parent.py` itself, since otherwise vulture complains
about it being unused. Subprojects will be forced to adjust on upgrade.
Remove a CodeQL exemption that was necessitated by our `import parent`
shenanigans.
2023-05-26 00:57:53 -08:00
|
|
|
import os
|
2020-01-29 01:34:49 -09:00
|
|
|
import subprocess
|
2022-02-10 03:18:30 -09:00
|
|
|
import sys
|
|
|
|
|
import time
|
2020-01-29 01:34:49 -09:00
|
|
|
import urllib
|
2023-05-25 03:17:16 -08:00
|
|
|
from urllib.request import Request, urlopen
|
2022-02-10 03:18:30 -09:00
|
|
|
|
2026-06-02 00:03:47 -08:00
|
|
|
from lib.github import GitHub
|
2020-01-29 01:34:49 -09:00
|
|
|
|
all: replace `import parent` with an interpreter line hack
We'd like to run our executable python scripts using our new `pywrap`
feature as a better way to get their PYTHONPATH setup properly. This
script lives in our source tree, at a fixed relative location to each
script that we're interested in, but has no absolute system path.
The #!interpreter syntax does supports relative paths, but they are
resolved relative to the current working directory, not relative to the
file in which they appear, which makes this feature pretty useless.
There are some tricks we can do to get our designed 'relative'
behaviour, though.
Something like this works:
#!/usr/bin/env -S sh -c '"${0%/*}/pywrap" "$0"'
but it causes various file auto-detection schemes (including our own
test/static-code) to improperly conclude that this is a shell script,
rather than a Python script. (n)vim also gets it wrong for highlighting
and linting purposes.
Fortunately there's a somewhat longer, but still acceptable version of
this we can do in Python:
#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/pywrap", sys.argv)
This fits well under our 118 column limit.
Note that unlike `sh`, the `-c` option in Python doesn't require the
script to be passed as a separate argument, which means we don't need to
use the non-POSIX splitting tricks of `/usr/bin/env -S`.
Note also: it appears as though our hardcoding of /usr/bin/python3 here
might be venv-unfriendly, but it's not: /usr/bin/python3 (which surely
exists) is used only for running this one tiny fragment of code. Our
pywrap script itself finds python3 in the path.
Let's move all of our previous users of `import parent` over to the new
approach.
We can remove a couple of `parent.py` symlinks we had lying
around, as well.
We also remove `parent.py` itself, since otherwise vulture complains
about it being unused. Subprojects will be forced to adjust on upgrade.
Remove a CodeQL exemption that was necessitated by our `import parent`
shenanigans.
2023-05-26 00:57:53 -08:00
|
|
|
BASE_DIR = os.path.realpath(f'{__file__}/../..')
|
|
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
DAYS = 7
|
|
|
|
|
TASK_NAME = "Validate all URLs"
|
|
|
|
|
|
2026-06-02 00:03:47 -08:00
|
|
|
github = GitHub()
|
|
|
|
|
|
2022-02-10 03:18:30 -09:00
|
|
|
USER_AGENT = "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0"
|
|
|
|
|
|
2024-11-27 22:50:33 -09:00
|
|
|
IGNORE = [
|
|
|
|
|
"https://www.gnome.org", # keeps causing 403 from GitHub; DNS or user-agent ban?
|
|
|
|
|
"*example.com*", # some tests use demo urls
|
|
|
|
|
]
|
|
|
|
|
|
2022-02-10 03:18:30 -09:00
|
|
|
KNOWN_REDIRECTS = [
|
|
|
|
|
# fnmatch-like
|
|
|
|
|
"https://access.redhat.com/security/updates/classification/#",
|
|
|
|
|
"https://firefox.com/",
|
|
|
|
|
"https://www.microsoft.com/",
|
2024-08-18 19:41:48 -08:00
|
|
|
"https://github.com/patternfly/*",
|
|
|
|
|
# SVG/XML processing in *.jsx
|
|
|
|
|
"http://www.w3.org/2000/svg",
|
|
|
|
|
"http://www.w3.org/XML/1998/namespace",
|
2022-02-10 03:18:30 -09:00
|
|
|
]
|
2021-02-15 05:40:57 -09:00
|
|
|
|
2021-04-04 11:33:13 -08:00
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
def main():
|
|
|
|
|
parser = argparse.ArgumentParser(description=TASK_NAME)
|
|
|
|
|
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
2024-02-14 01:19:19 -09:00
|
|
|
parser.add_argument('-n', '--dry-run', action="store_true",
|
|
|
|
|
help="Only show urls")
|
2020-01-29 01:34:49 -09:00
|
|
|
opts = parser.parse_args()
|
|
|
|
|
|
2024-02-14 01:19:19 -09:00
|
|
|
if opts.dry_run:
|
|
|
|
|
(success, err) = check_urls(opts.verbose)
|
2024-08-18 18:46:08 -08:00
|
|
|
print(err)
|
|
|
|
|
print(success)
|
2024-02-14 01:19:19 -09:00
|
|
|
sys.exit(1 if err else 0)
|
|
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
since = time.time() - (DAYS * 86400)
|
|
|
|
|
|
|
|
|
|
# When there is an open issue, don't do anything
|
2026-06-02 00:03:47 -08:00
|
|
|
issues = github.issues(state="open")
|
2020-01-29 01:34:49 -09:00
|
|
|
issues = [i for i in issues if i["title"] == TASK_NAME]
|
|
|
|
|
if issues:
|
|
|
|
|
return
|
|
|
|
|
|
2026-06-02 00:03:47 -08:00
|
|
|
issues = github.issues(state="closed", since=since)
|
2020-01-29 01:34:49 -09:00
|
|
|
issues = [i for i in issues if i["title"] == TASK_NAME]
|
|
|
|
|
|
|
|
|
|
# If related issue was not modified in last n-DAYS, then do your thing
|
|
|
|
|
if not issues:
|
|
|
|
|
(success, err) = check_urls(opts.verbose)
|
|
|
|
|
if err:
|
|
|
|
|
# Create a new issue
|
|
|
|
|
data = {
|
|
|
|
|
"title": TASK_NAME,
|
|
|
|
|
"body": err,
|
|
|
|
|
"labels": ["bot"]
|
|
|
|
|
}
|
2026-06-02 00:03:47 -08:00
|
|
|
github.post("issues", data)
|
2020-01-29 01:34:49 -09:00
|
|
|
else:
|
|
|
|
|
# Try to comment on the last issue (look in the last 4*DAYS)
|
|
|
|
|
# If there is not issue to be found, then just open a new one and close it
|
|
|
|
|
success = success or "Task hasn't produced any output"
|
|
|
|
|
since = time.time() - (DAYS * 4 * 86400)
|
2026-06-02 00:03:47 -08:00
|
|
|
issues = github.issues(state="closed", since=since)
|
2020-01-29 01:34:49 -09:00
|
|
|
issues = [i for i in issues if i["title"] == TASK_NAME]
|
|
|
|
|
if issues:
|
2026-06-02 00:03:47 -08:00
|
|
|
github.comment(issues[0]["number"], success)
|
2020-01-29 01:34:49 -09:00
|
|
|
else:
|
|
|
|
|
data = {
|
|
|
|
|
"title": TASK_NAME,
|
|
|
|
|
"body": success,
|
|
|
|
|
"labels": ["bot"]
|
|
|
|
|
}
|
2026-06-02 00:03:47 -08:00
|
|
|
new_issue = github.post("issues", data)
|
|
|
|
|
github.patch(f"issues/{new_issue['number']}", {"state": "closed"})
|
2020-01-29 01:34:49 -09:00
|
|
|
|
2021-04-04 11:33:13 -08:00
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
def check_urls(verbose):
|
2020-03-18 04:04:08 -08:00
|
|
|
command = r'git grep -IEho "(https?)://[-a-zA-Z0-9@:%_\+.~#?&=/]+" -- pkg ":!*.svg" | sort -u'
|
2021-04-06 00:48:04 -08:00
|
|
|
urls = subprocess.check_output(command, shell=True, universal_newlines=True, cwd=BASE_DIR).split("\n")
|
2021-02-15 05:40:57 -09:00
|
|
|
redirects = []
|
2020-01-29 01:34:49 -09:00
|
|
|
failed = []
|
|
|
|
|
for url in urls:
|
|
|
|
|
if not url:
|
|
|
|
|
continue
|
|
|
|
|
|
2024-11-27 22:50:33 -09:00
|
|
|
if any(fnmatch.fnmatch(url, pattern) for pattern in IGNORE):
|
|
|
|
|
if verbose:
|
|
|
|
|
print(f"Ignoring: {url}")
|
2020-01-29 01:34:49 -09:00
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if verbose:
|
2023-09-27 03:39:40 -08:00
|
|
|
print(f"Checking: {url}")
|
2020-01-29 01:34:49 -09:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Specify agent as some websites otherwise block requests
|
2022-02-10 03:18:30 -09:00
|
|
|
req = Request(url=url, headers={"User-Agent": USER_AGENT})
|
2021-04-04 11:33:13 -08:00
|
|
|
resp = urlopen(req)
|
2022-02-10 03:18:30 -09:00
|
|
|
if resp.geturl() != url and not any(fnmatch.fnmatch(url, pattern) for pattern in KNOWN_REDIRECTS):
|
2021-02-15 05:40:57 -09:00
|
|
|
redirects.append(url)
|
|
|
|
|
if resp.getcode() >= 400:
|
2024-03-13 06:49:00 -08:00
|
|
|
failed.append(f"{url} : {resp.getcode()} {resp.reason}")
|
|
|
|
|
except urllib.error.URLError as e:
|
2024-06-19 04:05:42 -08:00
|
|
|
failed.append(f"{url} : {e.reason}")
|
2020-01-29 01:34:49 -09:00
|
|
|
|
|
|
|
|
err = ""
|
|
|
|
|
success = ""
|
|
|
|
|
if failed:
|
2022-02-10 03:18:30 -09:00
|
|
|
err = f"Checked {len(urls)} URLs out of which {len(failed)} is/are invalid:\n"
|
|
|
|
|
err += ''.join(f' {url}\n' for url in failed)
|
2020-01-29 01:34:49 -09:00
|
|
|
else:
|
2022-02-10 03:18:30 -09:00
|
|
|
success = f"Checked {len(urls)} URLs and all are valid."
|
2024-08-18 18:46:08 -08:00
|
|
|
if redirects:
|
|
|
|
|
success += "\nFollowing URLs are redirected:\n"
|
|
|
|
|
success += ''.join(f' {url}\n' for url in redirects)
|
2022-02-10 03:18:30 -09:00
|
|
|
return success, err
|
2020-01-29 01:34:49 -09:00
|
|
|
|
2021-04-04 11:33:13 -08:00
|
|
|
|
2020-01-29 01:34:49 -09:00
|
|
|
if __name__ == '__main__':
|
|
|
|
|
sys.exit(main())
|