diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87d0a72b26..4b56f428ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -430,6 +430,7 @@ jobs: with: workflow: deploy.yml repo: ietf-tools/infra-k8s + ref: main token: ${{ secrets.GH_INFRA_K8S_TOKEN }} inputs: '{ "environment":"${{ secrets.GHA_K8S_CLUSTER }}", "app":"datatracker", "appVersion":"${{ env.PKG_VERSION }}", "remoteRef":"${{ github.sha }}" }' wait-for-completion: true @@ -455,6 +456,7 @@ jobs: with: workflow: deploy.yml repo: ietf-tools/infra-k8s + ref: main token: ${{ secrets.GH_INFRA_K8S_TOKEN }} inputs: '{ "environment":"${{ secrets.GHA_K8S_CLUSTER }}", "app":"datatracker", "appVersion":"${{ env.PKG_VERSION }}", "remoteRef":"${{ github.sha }}" }' wait-for-completion: true diff --git a/dev/deploy-to-container/package-lock.json b/dev/deploy-to-container/package-lock.json index 550f8f072e..7b86986d56 100644 --- a/dev/deploy-to-container/package-lock.json +++ b/dev/deploy-to-container/package-lock.json @@ -11,7 +11,7 @@ "nanoid": "5.0.7", "nanoid-dictionary": "5.0.0-beta.1", "slugify": "1.6.6", - "tar": "^7.1.0", + "tar": "^7.4.0", "yargs": "^17.7.2" }, "engines": { @@ -496,9 +496,9 @@ } }, "node_modules/minipass": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", - "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -788,13 +788,13 @@ } }, "node_modules/tar": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.1.0.tgz", - "integrity": "sha512-ENhg4W6BmjYxl8GTaE7/h99f0aXiSWv4kikRZ9n2/JRxypZniE84ILZqimAhxxX7Zb8Px6pFdheW3EeHfhnXQQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.0.tgz", + "integrity": "sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", - "minipass": "^7.1.0", + "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" @@ -1311,9 +1311,9 @@ } }, "minipass": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", - "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==" + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" }, "minizlib": { "version": "3.0.1", @@ -1503,13 +1503,13 @@ } }, "tar": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.1.0.tgz", - "integrity": "sha512-ENhg4W6BmjYxl8GTaE7/h99f0aXiSWv4kikRZ9n2/JRxypZniE84ILZqimAhxxX7Zb8Px6pFdheW3EeHfhnXQQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.0.tgz", + "integrity": "sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==", "requires": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", - "minipass": "^7.1.0", + "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" diff --git a/dev/deploy-to-container/package.json b/dev/deploy-to-container/package.json index 6b134e5405..2f582dd2a7 100644 --- a/dev/deploy-to-container/package.json +++ b/dev/deploy-to-container/package.json @@ -7,7 +7,7 @@ "nanoid": "5.0.7", "nanoid-dictionary": "5.0.0-beta.1", "slugify": "1.6.6", - "tar": "^7.1.0", + "tar": "^7.4.0", "yargs": "^17.7.2" }, "engines": { diff --git a/dev/diff/package-lock.json b/dev/diff/package-lock.json index d97649b39e..3500ccec48 100644 --- a/dev/diff/package-lock.json +++ b/dev/diff/package-lock.json @@ -17,7 +17,7 @@ "lodash-es": "^4.17.21", "luxon": "^3.4.4", "pretty-bytes": "^6.1.1", - "tar": "^7.1.0", + "tar": "^7.4.0", "yargs": "^17.7.2" }, "engines": { @@ -1101,9 +1101,9 @@ } }, "node_modules/minipass": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", - "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -1493,13 +1493,13 @@ } }, "node_modules/tar": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.1.0.tgz", - "integrity": "sha512-ENhg4W6BmjYxl8GTaE7/h99f0aXiSWv4kikRZ9n2/JRxypZniE84ILZqimAhxxX7Zb8Px6pFdheW3EeHfhnXQQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.0.tgz", + "integrity": "sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", - "minipass": "^7.1.0", + "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" @@ -2433,9 +2433,9 @@ } }, "minipass": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", - "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==" + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" }, "minizlib": { "version": "3.0.1", @@ -2691,13 +2691,13 @@ } }, "tar": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.1.0.tgz", - "integrity": "sha512-ENhg4W6BmjYxl8GTaE7/h99f0aXiSWv4kikRZ9n2/JRxypZniE84ILZqimAhxxX7Zb8Px6pFdheW3EeHfhnXQQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.0.tgz", + "integrity": "sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==", "requires": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", - "minipass": "^7.1.0", + "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" diff --git a/dev/diff/package.json b/dev/diff/package.json index dcc6e9eb7b..1b5540e346 100644 --- a/dev/diff/package.json +++ b/dev/diff/package.json @@ -13,7 +13,7 @@ "lodash-es": "^4.17.21", "luxon": "^3.4.4", "pretty-bytes": "^6.1.1", - "tar": "^7.1.0", + "tar": "^7.4.0", "yargs": "^17.7.2" }, "engines": { diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index dd32869251..f7302d8098 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -19,6 +19,7 @@ from django.conf import settings from django.contrib import messages +from django.db.models import OuterRef from django.forms import ValidationError from django.http import Http404 from django.template.loader import render_to_string @@ -39,7 +40,7 @@ from ietf.name.models import DocReminderTypeName, DocRelationshipName from ietf.group.models import Role, Group, GroupFeatures from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, is_individual_draft_author, is_bofreq_editor -from ietf.person.models import Person +from ietf.person.models import Email, Person from ietf.review.models import ReviewWish from ietf.utils import draft, log from ietf.utils.mail import parseaddr, send_mail @@ -1301,9 +1302,13 @@ def get_draft_shepherd_email(self, doc): def get_draft_authors_emails(self, doc): """Get list of authors for the given draft.""" author_emails = set() - for author in doc.documentauthor_set.all(): - if author.email and author.email.email_address(): - author_emails.add(author.email.email_address()) + for email in Email.objects.filter(documentauthor__document=doc): + if email.active: + author_emails.add(email.address) + elif email.person: + person_email = email.person.email_address() + if person_email: + author_emails.add(person_email) return author_emails def get_draft_notify_emails(self, doc): @@ -1336,59 +1341,82 @@ def get_draft_notify_emails(self, doc): notify_emails.add(email) return notify_emails + def _yield_aliases_for_draft(self, doc)-> Iterator[tuple[str, list[str]]]: + alias = doc.name + all = set() + + # no suffix and .authors are the same list + emails = self.get_draft_authors_emails(doc) + all.update(emails) + if emails: + yield alias, list(emails) + yield alias + ".authors", list(emails) + + # .chairs = group chairs + emails = self.get_draft_chair_emails(doc) + if emails: + all.update(emails) + yield alias + ".chairs", list(emails) + + # .ad = sponsoring AD / WG AD (WG document) + emails = self.get_draft_ad_emails(doc) + if emails: + all.update(emails) + yield alias + ".ad", list(emails) + + # .notify = notify email list from the Document + emails = self.get_draft_notify_emails(doc) + if emails: + all.update(emails) + yield alias + ".notify", list(emails) + + # .shepherd = shepherd email from the Document + emails = self.get_draft_shepherd_email(doc) + if emails: + all.update(emails) + yield alias + ".shepherd", list(emails) + + # .all = everything from above + if all: + yield alias + ".all", list(all) + def __iter__(self) -> Iterator[tuple[str, list[str]]]: # Internet-Drafts with active status or expired within self.days show_since = timezone.now() - datetime.timedelta(days=self.days) drafts = self.draft_queryset - active_drafts = drafts.filter(states__slug='active') - inactive_recent_drafts = drafts.exclude(states__slug='active').filter(expires__gte=show_since) - interesting_drafts = active_drafts | inactive_recent_drafts - for this_draft in interesting_drafts.distinct().iterator(): + # Look up the draft-active state properly. Doing this with + # states__type_id, states__slug directly in the `filter()` + # works, but it does not work as expected in `exclude()`. + active_state = State.objects.get(type_id="draft", slug="active") + active_drafts = drafts.filter(states=active_state) + for this_draft in active_drafts: + for alias, addresses in self._yield_aliases_for_draft(this_draft): + yield alias, addresses + + # Annotate with the draft state slug so we can check for drafts that + # have become RFCs + inactive_recent_drafts = ( + drafts.exclude(states=active_state) + .filter(expires__gte=show_since) + .annotate( + # Why _default_manager instead of objects? See: + # https://docs.djangoproject.com/en/4.2/topics/db/managers/#django.db.models.Model._default_manager + draft_state_slug=Document.states.through._default_manager.filter( + document__pk=OuterRef("pk"), + state__type_id="draft" + ).values("state__slug"), + ) + ) + for this_draft in inactive_recent_drafts: # Omit drafts that became RFCs, unless they were published in the last DEFAULT_YEARS - if this_draft.get_state_slug() == "rfc": + if this_draft.draft_state_slug == "rfc": rfc = this_draft.became_rfc() log.assertion("rfc is not None") if rfc.latest_event(type='published_rfc').time < show_since: continue - - alias = this_draft.name - all = set() - - # no suffix and .authors are the same list - emails = self.get_draft_authors_emails(this_draft) - all.update(emails) - if emails: - yield alias, list(emails) - yield alias + ".authors", list(emails) - - # .chairs = group chairs - emails = self.get_draft_chair_emails(this_draft) - if emails: - all.update(emails) - yield alias + ".chairs", list(emails) - - # .ad = sponsoring AD / WG AD (WG document) - emails = self.get_draft_ad_emails(this_draft) - if emails: - all.update(emails) - yield alias + ".ad", list(emails) - - # .notify = notify email list from the Document - emails = self.get_draft_notify_emails(this_draft) - if emails: - all.update(emails) - yield alias + ".notify", list(emails) - - # .shepherd = shepherd email from the Document - emails = self.get_draft_shepherd_email(this_draft) - if emails: - all.update(emails) - yield alias + ".shepherd", list(emails) - - # .all = everything from above - if all: - yield alias + ".all", list(all) + for alias, addresses in self._yield_aliases_for_draft(this_draft): + yield alias, addresses def get_doc_email_aliases(name: Optional[str] = None): diff --git a/patch/add-django-cprofile-filter.patch b/patch/add-django-cprofile-filter.patch index 128d5a9f09..bf684a0b33 100644 --- a/patch/add-django-cprofile-filter.patch +++ b/patch/add-django-cprofile-filter.patch @@ -1,15 +1,9 @@ ---- django_cprofile_middleware/middleware.py.old 2018-04-04 06:32:29.282187502 -0700 -+++ django_cprofile_middleware/middleware.py 2018-04-06 10:11:18.936855634 -0700 -@@ -1,4 +1,5 @@ - import pstats -+import re - - try: - import cProfile as profile -@@ -14,6 +15,15 @@ - from django.utils.deprecation import MiddlewareMixin - - +--- django_cprofile_middleware/middleware.py.old 2024-06-27 21:03:56.975128007 +0000 ++++ django_cprofile_middleware/middleware.py 2024-06-27 23:45:59.421683008 +0000 +@@ -19,6 +19,16 @@ + from django_cprofile_middleware.utils import MiddlewareMixin + + +class Stats(pstats.Stats): + def filter_stats(self, regex): + oldstats = self.stats @@ -18,17 +12,64 @@ + for func, (cc, nc, tt, ct, callers) in oldstats.iteritems(): + if filter.search(pstats.func_std_string(func)): + newstats[func] = (cc, nc, tt, ct, callers) ++ + class ProfilerMiddleware(MiddlewareMixin): """ Simple profile middleware to profile django views. To run it, add ?prof to -@@ -62,8 +72,13 @@ +@@ -38,9 +48,11 @@ + ?download => Download profile file suitable for visualization. For example + in snakeviz or RunSnakeRun + +- This is adapted from an example found here: +- http://www.slideshare.net/zeeg/django-con-high-performance-django-presentation. ++ Patched with https://github.com/omarish/django-cprofile-middleware/pull/23 ++ for operation with Django 4.2.5+ + """ ++ PROFILER_REQUEST_ATTR_NAME = '_django_cprofile_middleware_profiler' ++ + def can(self, request): + requires_staff = getattr( + settings, "DJANGO_CPROFILE_MIDDLEWARE_REQUIRE_STAFF", True) +@@ -52,10 +64,11 @@ + + def process_view(self, request, callback, callback_args, callback_kwargs): + if self.can(request): +- self.profiler = profile.Profile() ++ profiler = profile.Profile() ++ setattr(request, self.PROFILER_REQUEST_ATTR_NAME, profiler) + args = (request,) + callback_args + try: +- return self.profiler.runcall( ++ return profiler.runcall( + callback, *args, **callback_kwargs) + except Exception: + # we want the process_exception middleware to fire +@@ -63,12 +76,13 @@ + return + + def process_response(self, request, response): +- if self.can(request): +- self.profiler.create_stats() ++ if hasattr(request, self.PROFILER_REQUEST_ATTR_NAME): ++ profiler = getattr(request, self.PROFILER_REQUEST_ATTR_NAME) ++ profiler.create_stats() + if 'download' in request.GET: + import marshal + +- output = marshal.dumps(self.profiler.stats) ++ output = marshal.dumps(profiler.stats) + response = HttpResponse( + output, content_type='application/octet-stream') + response['Content-Disposition'] = 'attachment;' \ +@@ -76,9 +90,14 @@ response['Content-Length'] = len(output) else: io = StringIO() - stats = pstats.Stats(self.profiler, stream=io) ++ stats = Stats(profiler, stream=io) + - stats.strip_dirs().sort_stats(request.GET.get('sort', 'time')) -+ stats = Stats(self.profiler, stream=io) + if request.GET.get('stripdirs', False): + stats = stats.strip_dirs() + filter = request.GET.get('filter', None) @@ -36,5 +77,5 @@ + stats.filter_stats(filter) + stats.sort_stats(request.GET.get('psort') or 'time') stats.print_stats(int(request.GET.get('count', 100))) + response = HttpResponse('
%s
' % io.getvalue()) - return response