This is an automated email from the ASF dual-hosted git repository. sebb pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/attic.git
The following commit(s) were added to refs/heads/main by this push: new 8b614d8 No longer needed [skip ci] 8b614d8 is described below commit 8b614d8ba217a93129f4f0a9eace080a93254c86 Author: Sebb <s...@apache.org> AuthorDate: Thu May 1 22:17:57 2025 +0100 No longer needed [skip ci] --- infrajiratext.py | 128 ----------------------------------- retire.py | 202 ------------------------------------------------------- urlutils.py | 84 ----------------------- 3 files changed, 414 deletions(-) diff --git a/infrajiratext.py b/infrajiratext.py deleted file mode 100755 index 7289009..0000000 --- a/infrajiratext.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 - -""" - -Find project resources for retired projects and generate JIRA issue text for INFRA: -- source control system (SVN or Git) -- dist.apache.org releases and dev -- mailing lists (Whimsy) -- JIRA https://issues.apache.org/jira/rest/api/2/project.json - detect if present -- LDAP project -- https://cwiki.apache.org/confluence/display/VXQUERY (e.g.) - -""" - -import sys -import subprocess -from urlutils import loadjson, urlexists - -DEV="https://dist.apache.org/repos/dist/dev/" -REL="https://dist.apache.org/repos/dist/release/" -JIRA='https://issues.apache.org/jira/rest/api/2/project' -CWIKI='https://cwiki.apache.org/confluence/display/' -EMAIL='https://whimsy.apache.org/public/committee-retired.json' -GITBOX='https://gitbox.apache.org/repositories.json' # ['projects'] - - -# ===================================== - -retired = loadjson(EMAIL)['retired'] -gitbox = loadjson(GITBOX)['projects'] - -def check_wiki(pid): - url = CWIKI + pid.upper() - if urlexists(url): - print("Make CWIKI readonly: %s" % url) - -def check_mail(pid): - try: - mlists = retired[pid]['mlists'] - if len(mlists) > 0: - print("Mailing lists https://lists.apache.org/list.html?%s.apache.org :" % pid) - for mlist in mlists: - print("- %s@%s.apache.org" % (mlist,pid)) - print() - except KeyError: - pass - -def svnfiles(path): - res = subprocess.run(["svn", "ls", path], capture_output=True) - if res.returncode == 0: - stdout = res.stdout - if len(stdout) == 0: - return 0 - if stdout == b'.htaccess\n': # just the htaccess file is OK - return 0 - return 1 - else: - stderr = res.stderr - if b"W160013" in stderr: # no such item - return -1 - print(stderr) - return -1 - -def check_dist(pid): - dev = svnfiles(DEV + pid) - rel = svnfiles(REL + pid) - if dev > 0 or rel > 0: - if dev > 0: - print("Remove the distribution SVN developer directory:") - print('- ' + DEV + pid) - if rel > 0: - print("Remove the distribution SVN release directory contents, and") - print("set up redirection to https://attic.apache.org/projects/%s.html" % pid) - print('- ' + REL + pid) - print() - -# There is no anonymous access to LDAP now -# Assume there is a project LDAP group -def check_ldap(pid): - BASE='ou=project,ou=groups,dc=apache,dc=org' - print("Remove the LDAP project group: dn: cn=%s,%s" % (pid, BASE)) - -def check_jira(pid): - jira = loadjson(JIRA) - for project in jira: - key = project['key'] - catname = '' - if 'projectCategory' in project: - catname = project['projectCategory']['name'] - if pid.upper() == key: - if '(Retired)' in project['name'] or catname == 'Retired': - pass - else: - print("Make the JIRA project https://issues.apache.org/jira/projects/%s read-only and flag as retired" % key) - elif catname.lower() == pid: - print("Make the JIRA project https://issues.apache.org/jira/projects/%s read-only and flag as retired" % key) - -# check for SVN and Git -def check_scs(pid): - svn = "https://svn.apache.org/repos/asf/%s" % pid - if svnfiles(svn) > 0: - print("Make SVN readonly: %s" % svn) # TODO what if this is the site SVN? -# print("Update config file so commits go to attic ???") - if pid in gitbox: - print("Make the following git repos read-only https://gitbox.apache.org/repos/asf#%s :" % pid) - for repo in gitbox[pid]['repositories']: - print("- %s" % repo) - print("Please do NOT rename the repos.") - -if len(sys.argv) == 1: - print("Please provide a list of project ids") - sys.exit(1) - -for arg in sys.argv[1:]: - if not arg in retired: - print("%s is not listed as a retired TLP" % arg) - continue -# with open('jira_%s.tmp' % arg, 'w') as f: -# with redirect_stdout(f): - print("%s project has retired." % retired[arg]['display_name']) - print("The following resources were detected for archive/removal please:\n") - check_dist(arg) - check_mail(arg) - check_jira(arg) - check_scs(arg) - check_wiki(arg) - check_ldap(arg) - print("Turn off any automated builds") diff --git a/retire.py b/retire.py deleted file mode 100755 index fdb2970..0000000 --- a/retire.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python3 - -""" - -Script to create retirement files for a project. - -Input: -- https://whimsy.apache.org/public/committee-retired.json -- https://lists.apache.org/api/preferences.lua -- xdocs/projects/_template.xml -- _template.jira - -Output: -- xdocs/flagged/<pid> (created) -- xdocs/projects/<pid>.xml (created) -- xdocs/stylesheets/project.xml (updated) -- cwiki_retired/<wiki_id>.txt (created) -- <pid>.jira.tmp (created) - this is for pasting into an Attic JIRA issue - -N.B. The generated pid.xml file may need tweaking - -""" - -import sys -import os.path -from os.path import dirname, abspath, join -from inspect import getsourcefile -from string import Template -import os -import re -import subprocess -from collections import defaultdict -from urlutils import loadyaml, loadjson - -if len(sys.argv) == 1: - print("Please provide a list of project ids") - sys.exit(1) - -CWIKI='https://cwiki.apache.org/confluence/display/' - -CWIKI_INFO='https://cwiki.apache.org/confluence/rest/api/space/?type=global&limit=1000' -WIKI_KEYS = defaultdict(list) # key = project name, value = list of possible spaces -for entry in loadjson(CWIKI_INFO)['results']: - WIKI_KEYS[entry['name'].lower().replace('apache ', '')].append(entry['key']) - -JIRA='https://issues.apache.org/jira/rest/api/2/project' - -SVNURL='https://svn.apache.org/repos/asf/' -SVNKEYS = {} -svnargs = ['svn', 'ls', SVNURL] -output = subprocess.run(svnargs, stdout=subprocess.PIPE, check=True).stdout.decode("us-ascii") -for name in output.split('\n'): - if len(name) > 0: - SVNKEYS[name.rstrip('/')] = '' - -GITREPOS='https://gitbox.apache.org/repositories.json' -GITKEYS = {} -for entry in loadjson(GITREPOS)['projects']: - GITKEYS[entry] = '' - -MYHOME = dirname(abspath(getsourcefile(lambda:0))) -PROJECTS = join((MYHOME), 'xdocs', 'projects') -SYLESHEETS = join((MYHOME), 'xdocs', 'stylesheets') -FLAGGED = join((MYHOME), 'xdocs', 'flagged') -CWIKI_RETIRED = join((MYHOME), 'cwiki_retired') - -# get details of the retired projects -RETIREES = loadyaml('https://whimsy.apache.org/public/committee-retired.json')['retired'] -lists = {} -for host,names in loadyaml('https://lists.apache.org/api/preferences.lua')['lists'].items(): - proj = host.replace('.apache.org','') - if proj in RETIREES: - lists[proj] = list(names.keys()) - -def list_jira(pid): - jira = loadjson(JIRA) - jiras = [] - for project in jira: - key = project['key'] - catname = '' - if 'projectCategory' in project: - catname = project['projectCategory']['name'] - if pid.upper() == key: - jiras.append(key) - elif catname.lower() == pid: - jiras.append(key) - return jiras - -# updates xdocs/stylesheets/project.xml -# <li><a href="/projects/abdera.html">Abdera</a></li> -def update_stylesheet(pid): - xmlfile = join(SYLESHEETS,'project.xml') - xmlfilet = join(SYLESHEETS,'project.xml.t') - print("Updating %s" % xmlfile) - found = False - with open(xmlfile,'r', encoding='utf-8') as r, open(xmlfilet,'w', encoding='utf-8') as w: - for l in r: - if not found: - m = re.search(r'^(\s+<li><a href="/projects/)([^.]+)(.html">)[^<]+(</a></li>)', l) - if m: - stem = m.group(2) - if stem == pid: - print("Already present in projects.xml") - print(l) - w.close() - os.remove(xmlfilet) - return - if stem > pid: # Found insertion point - found = True - name = RETIREES[pid]['display_name'] - w.write("%s%s%s%s%s\n" % (m.group(1), pid, m.group(3), name, m.group(4))) - w.write(l) # write the original line - if found: - print("Successfully added %s to %s" % (pid, xmlfile)) - os.system("diff %s %s" % (xmlfile, xmlfilet)) - os.rename(xmlfilet, xmlfile) - else: - print("Could not find where to add %s" % pid) - -# create JIRA template -def create_jira_template(pid): - outfile = join(MYHOME, "%s.jira.tmp" % pid) - print("Creating %s" % outfile) - with open(join(MYHOME, '_template.jira'), 'r', encoding='utf-8') as t: - template = Template(t.read()) - out = template.substitute(tlpid = pid) - with open(outfile, 'w', encoding='utf-8') as o: - o.write(out) - -# create the project.xml file from the template -def create_project(pid): - outfile = join(PROJECTS, "%s.xml" % pid) - print("Creating %s" % outfile) - with open(join(PROJECTS, '_template.xml'), 'r', encoding='utf-8') as t: - template = Template(t.read()) - meta = RETIREES[pid] - mnames = lists[pid] - mnames.remove('dev') - jiras = list_jira(pid) - # Is there a Wiki? - cwikis = find_wiki(pid) - if len(cwikis) > 0: - if len(cwikis) == 1 and cwikis[0].lower() == pid: - cwiki ='<cwiki/>' - else: - keys = ','.join(sorted(cwikis)) - cwiki =f'<cwiki keys="{keys}"/>' - else: - cwiki = '' - svn = git = '' - # May have both SVN and Git - if pid in SVNKEYS: - svn = '<svn/>' - if pid in GITKEYS: - git = '<git/>' - out = template.substitute(tlpid = pid, - FullName = meta['display_name'], - Month_Year = meta['retired'], - mail_names = ",".join(sorted(mnames)), - jira_names = ",".join(sorted(jiras)), - cwiki = cwiki, - svn = svn, - git = git, - description = meta.get('description', 'TBA')) - with open(outfile, 'w', encoding='utf-8') as o: - o.write(out) - os.system("git add %s" % outfile) - print("Check XML file for customisations such as JIRA and mailing lists") - -def find_wiki(pid): - found = [] - for k, v in WIKI_KEYS.items(): - if k == pid or (k.startswith(pid)): # and not k == valid project name - found += v - return found - -def check_wiki(pid): - for wiki in find_wiki(pid): - flagname = wiki.lower() - flagfile = join(CWIKI_RETIRED, f"{flagname}.txt") - with open(flagfile, 'w', encoding='utf-8') as w: - if not flagname == pid: - w.write(pid) - w.write("\n") - os.system("git add %s" % flagfile) - -for arg in sys.argv[1:]: - print("Processing "+arg) - if not arg in RETIREES: - print("%s does not appear to be a retired project" % arg) - continue - flagdir = join(FLAGGED, arg) - if os.path.exists(flagdir): - print("flagged/%s already exists" % arg) - continue - create_jira_template(arg) - os.mkdir(flagdir) - open(join(flagdir, "git.keep"), 'a').close() - os.system("git add %s" % flagdir) - create_project(arg) - update_stylesheet(arg) - check_wiki(arg) diff --git a/urlutils.py b/urlutils.py deleted file mode 100644 index 8b3775a..0000000 --- a/urlutils.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python3 - -""" - Utility URL methods -""" - -import hashlib -import sys -import errno -import time -import yaml -import json -from os import environ -from os.path import getmtime, join -from urllib.request import urlopen, Request -from urllib.error import HTTPError - -MAXAGE=int(environ.get('CACHE_AGE', '600')) # 5 min in seconds -CACHE=environ.get('CACHE') -DEBUG=environ.get('CACHE_DEBUG') - -def isFileStale(filename): - """ is file older than max age (default 5 minutes) """ - try: - t = getmtime(filename) - except OSError as e: - if not e.errno == errno.ENOENT: - raise e - return True - diff = time.time() - t - return diff > MAXAGE - -def hashurl(url): - """ create hash from url """ - return hashlib.sha224(url.encode()).hexdigest() - -def geturl(url): - """ Get url contents -- no caching """ - req = Request(url) - resp = urlopen(req) - return resp.read() - -def urlexists(url): - """ Does URL exist? """ - req = Request(url) - try: - urlopen(req) - return True - except HTTPError: - return False - -def urlcache(url): - """ Get url contents -- optional caching """ - if CACHE: - # basename seems to work OK with URLs - cache = join(CACHE, hashurl(url)+".tmp") - if isFileStale(cache): - if DEBUG: - print("Caching %s" % url, file=sys.stderr) - data = geturl(url) - with open(cache,'wb') as w: - w.write(data) - return data - else: - if DEBUG: - print("Using cache for %s" % url, file=sys.stderr) - with open(cache,'r') as r: - return r.read() - else: - if DEBUG: - print("Fetching %s" % url, file=sys.stderr) - return geturl(url) - -def loadyaml(url): - return yaml.safe_load(urlcache(url)) - -def loadjson(url): - return json.loads(urlcache(url)) - -# Test code -if __name__ == '__main__': - for arg in sys.argv[1:]: - print(arg) - print(loadyaml(arg))