aboutsummaryrefslogtreecommitdiff
path: root/etc/teams.scm
diff options
context:
space:
mode:
Diffstat (limited to 'etc/teams.scm')
-rwxr-xr-xetc/teams.scm855
1 files changed, 855 insertions, 0 deletions
diff --git a/etc/teams.scm b/etc/teams.scm
new file mode 100755
index 0000000000..f3d4c73132
--- /dev/null
+++ b/etc/teams.scm
@@ -0,0 +1,855 @@
+#!/bin/sh
+# Extra care is taken here to ensure this script can run in most environments,
+# since it is invoked by 'git send-email'.
+pre_inst_env_maybe=
+command -v guix > /dev/null || pre_inst_env_maybe=./pre-inst-env
+exec $pre_inst_env_maybe guix repl -- "$0" "$@"
+!#
+
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022, 2023 Ricardo Wurmus <rekado@elephly.net>
+;;; Copyright © 2022 Mathieu Othacehe <othacehe@gnu.org>
+;;; Copyright © 2022, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2022 Simon Tournier <zimon.toutoune@gmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This code defines development teams and team members, as well as their
+;; scope.
+
+;;; Code:
+
+(use-modules (srfi srfi-1)
+ (srfi srfi-9)
+ (srfi srfi-26)
+ (ice-9 format)
+ (ice-9 regex)
+ (ice-9 match)
+ (ice-9 rdelim)
+ (guix ui)
+ (git))
+
+(define-record-type <regexp*>
+ (%make-regexp* pat flag rx)
+ regexp*?
+ (pat regexp*-pattern)
+ (flag regexp*-flag)
+ (rx regexp*-rx))
+
+;;; Work around regexp implementation.
+;;; This record allows to track the regexp pattern and then display it.
+(define* (make-regexp* pat #:optional (flag regexp/extended))
+ "Alternative to `make-regexp' producing annotated <regexp*> objects."
+ (%make-regexp* pat flag (make-regexp pat flag)))
+
+(define (regexp*-exec rx* str)
+ "Execute the RX* regexp, a <regexp*> object."
+ (regexp-exec (regexp*-rx rx*) str))
+
+(define-record-type <team>
+ (make-team id name description members scope)
+ team?
+ (id team-id)
+ (name team-name)
+ (description team-description)
+ (members team-members set-team-members!)
+ (scope team-scope))
+
+(define-record-type <person>
+ (make-person name email)
+ person?
+ (name person-name)
+ (email person-email))
+
+(define* (person name #:optional email)
+ (make-person name email))
+
+(define* (team id #:key name description (members '())
+ (scope '()))
+ (make-team id
+ (or name (symbol->string id))
+ description
+ members
+ scope))
+
+(define %teams
+ (make-hash-table))
+
+(define-syntax define-team
+ (lambda (x)
+ (syntax-case x ()
+ ((_ id value)
+ #`(begin
+ (define-public id value)
+ (hash-set! %teams 'id id))))))
+
+(define-syntax-rule (define-member person teams ...)
+ (let ((p person))
+ (for-each (lambda (team-id)
+ (let ((team
+ (hash-ref %teams team-id
+ (lambda ()
+ (error (format #false
+ "Unknown team ~a for ~a~%"
+ team-id p))))))
+ (set-team-members!
+ team (cons p (team-members team)))))
+ (quote (teams ...)))))
+
+
+(define-team python
+ (team 'python
+ #:name "Python team"
+ #:description
+ "Python, Python packages, the \"pypi\" importer, and the python-build-system."
+ #:scope
+ (list "gnu/packages/django.scm"
+ "gnu/packages/jupyter.scm"
+ ;; Match haskell.scm and haskell-*.scm.
+ (make-regexp* "^gnu/packages/python(-.+|)\\.scm$")
+ "gnu/packages/sphinx.scm"
+ "gnu/packages/tryton.scm"
+ "guix/build/pyproject-build-system.scm"
+ "guix/build-system/pyproject.scm"
+ "guix/build/python-build-system.scm"
+ "guix/build-system/python.scm"
+ "guix/import/pypi.scm"
+ "guix/scripts/import/pypi.scm"
+ "tests/pypi.scm")))
+
+(define-team haskell
+ (team 'haskell
+ #:name "Haskell team"
+ #:description
+ "GHC, Hugs, Haskell packages, the \"hackage\" and \"stackage\" importers, and
+the haskell-build-system."
+ #:scope
+ (list "gnu/packages/dhall.scm"
+ ;; Match haskell.scm and haskell-*.scm.
+ (make-regexp* "^gnu/packages/haskell(-.+|)\\.scm$")
+ "gnu/packages/purescript.scm"
+ "guix/build/haskell-build-system.scm"
+ "guix/build-system/haskell.scm"
+ "guix/import/cabal.scm"
+ "guix/import/hackage.scm"
+ "guix/import/stackage.scm"
+ "guix/scripts/import/hackage.scm")))
+
+(define-team qt
+ (team 'qt
+ #:name "Qt team"
+ #:description
+ "The Qt toolkit/library and the qt-build-system,
+as well as some packages using Qt."
+ #:scope (list "gnu/packages/qt.scm"
+ "guix/build-system/qt.scm"
+ "guix/build/qt-build-system.scm"
+ "guix/build/qt-utils.scm")))
+
+(define-team r
+ (team 'r
+ #:name "R team"
+ #:description
+ "The R language, CRAN and Bioconductor repositories, the \"cran\" importer,
+and the r-build-system."
+ #:scope (list "gnu/packages/bioconductor.scm"
+ "gnu/packages/cran.scm"
+ "guix/build/r-build-system.scm"
+ "guix/build-system/r.scm"
+ "guix/import/cran.scm"
+ "guix/scripts/import/cran.scm"
+ "tests/cran.scm")))
+
+(define-team telephony
+ (team 'telephony
+ #:name "Telephony team"
+ #:description
+ "Telephony packages and services such as Jami, Linphone, etc."
+ #:scope (list "gnu/build/jami-service.scm"
+ "gnu/packages/jami.scm"
+ "gnu/packages/linphone.scm"
+ "gnu/packages/telephony.scm"
+ "gnu/services/telephony.scm"
+ "gnu/tests/data/jami-dummy-account.dat"
+ "gnu/tests/telephony.scm"
+ "tests/services/telephony.scm")))
+
+(define-team tex
+ (team 'tex
+ #:name "TeX team"
+ #:description
+ "TeX, LaTeX, XeLaTeX, LuaTeX, TeXLive, the texlive-build-system, and
+the \"texlive\" importer."
+ #:scope (list "gnu/packages/tex.scm"
+ "gnu/packages/texlive.scm"
+ "guix/build/texlive-build-system.scm"
+ "guix/build-system/texlive.scm"
+ "guix/import/texlive.scm"
+ "guix/scripts/import/texlive.scm"
+ "tests/texlive.scm")))
+
+(define-team julia
+ (team 'julia
+ #:name "Julia team"
+ #:description
+ "The Julia language, Julia packages, and the julia-build-system."
+ #:scope (list (make-regexp* "^gnu/packages/julia(-.+|)\\.scm$")
+ "guix/build/julia-build-system.scm"
+ "guix/build-system/julia.scm")))
+
+(define-team ocaml
+ (team 'ocaml
+ #:name "OCaml and Dune team"
+ #:description
+ "The OCaml language, the Dune build system, OCaml packages, the \"opam\"
+importer, and the ocaml-build-system."
+ #:scope
+ (list "gnu/packages/ocaml.scm"
+ "gnu/packages/coq.scm"
+ "guix/build/ocaml-build-system.scm"
+ "guix/build/dune-build-system.scm"
+ "guix/build-system/ocaml.scm"
+ "guix/build-system/dune.scm"
+ "guix/import/opam.scm"
+ "guix/scripts/import/opam.scm"
+ "tests/opam.scm")))
+
+(define-team java
+ (team 'java
+ #:name "Java and Maven team"
+ #:description
+ "The JDK and JRE, the Maven build system, Java packages, the ant-build-system,
+and the maven-build-system."
+ #:scope
+ (list ;; Match java.scm and java-*.scm.
+ (make-regexp* "^gnu/packages/java(-.+|)\\.scm$")
+ ;; Match maven.scm and maven-*.scm
+ (make-regexp* "^gnu/packages/maven(-.+|)\\.scm$")
+ "guix/build/ant-build-system.scm"
+ "guix/build/java-utils.scm"
+ "guix/build/maven-build-system.scm"
+ ;; The maven directory
+ (make-regexp* "^guix/build/maven/")
+ "guix/build-system/ant.scm"
+ "guix/build-system/maven.scm")))
+
+(define-team science
+ (team 'science
+ #:name "Science team"
+ #:description "The main science disciplines and fields related
+packages (e.g. Astronomy, Chemistry, Math, Physics etc.)"
+ #:scope (list "gnu/packages/algebra.scm"
+ "gnu/packages/astronomy.scm"
+ "gnu/packages/geo.scm"
+ "gnu/packages/chemistry.scm"
+ "gnu/packages/maths.scm")))
+
+(define-team emacs
+ (team 'emacs
+ #:name "Emacs team"
+ #:description "The extensible, customizable text editor and its
+ecosystem."
+ #:scope (list "gnu/packages/aux-files/emacs/guix-emacs.el"
+ (make-regexp* "^gnu/packages/emacs(-.+|)\\.scm$")
+ "gnu/packages/tree-sitter.scm"
+ "guix/build/emacs-build-system.scm"
+ "guix/build/emacs-utils.scm"
+ "guix/build-system/emacs.scm"
+ "guix/import/elpa.scm"
+ "guix/scripts/import/elpa.scm"
+ "tests/elpa.scm")))
+
+(define-team lisp
+ (team 'lisp
+ #:name "Lisp team"
+ #:description
+ "Common Lisp and similar languages, Common Lisp packages and the
+asdf-build-system."
+ #:scope (list (make-regexp* "^gnu/packages/lisp(-.+|)\\.scm$")
+ "guix/build/asdf-build-system.scm"
+ "guix/build/lisp-utils.scm"
+ "guix/build-system/asdf.scm")))
+
+(define-team ruby
+ (team 'ruby
+ #:name "Ruby team"
+ #:scope (list "gnu/packages/ruby.scm"
+ "guix/build/ruby-build-system.scm"
+ "guix/build-system/ruby.scm"
+ "guix/import/gem.scm"
+ "guix/scripts/import/gem.scm"
+ "tests/gem.scm")))
+
+(define-team go
+ (team 'go
+ #:name "Go team"
+ #:scope (list "gnu/packages/golang.scm"
+ "guix/build/go-build-system.scm"
+ "guix/build-system/go.scm"
+ "guix/import/go.scm"
+ "guix/scripts/import/go.scm"
+ "tests/go.scm")))
+
+(define-team bootstrap
+ (team 'bootstrap
+ #:name "Bootstrap"
+ #:scope (list "gnu/packages/mes.scm")))
+
+(define-team embedded
+ (team 'embedded
+ #:name "Embedded"
+ #:scope (list "gnu/packages/bootloaders.scm"
+ "gnu/packages/firmware.scm")))
+
+(define-team rust
+ (team 'rust
+ #:name "Rust"
+ #:scope (list (make-regexp* "^gnu/packages/(crates|rust)(-.+|)\\.scm$")
+ "gnu/packages/sequoia.scm"
+ "guix/build/cargo-build-system.scm"
+ "guix/build/cargo-utils.scm"
+ "guix/build-system/cargo.scm"
+ "guix/import/crate.scm"
+ "guix/scripts/import/crate.scm"
+ "tests/crate.scm")))
+
+(define-team kernel
+ (team 'kernel
+ #:name "Linux-libre kernel team"
+ #:scope (list "gnu/build/linux-modules.scm"
+ "gnu/packages/linux.scm"
+ "gnu/tests/linux-modules.scm"
+ "guix/build/linux-module-build-system.scm"
+ "guix/build-system/linux-module.scm")))
+
+(define-team core
+ (team 'core
+ #:name "Core / Tools / Internals"
+ #:scope
+ (list "guix/avahi.scm"
+ "guix/base16.scm"
+ "guix/base32.scm"
+ "guix/base64.scm"
+ "guix/bzr-download.scm"
+ "guix/cache.scm"
+ "guix/channels.scm"
+ "guix/ci.scm"
+ "guix/colors.scm"
+ "guix/combinators.scm"
+ "guix/config.scm"
+ "guix/cpio.scm"
+ "guix/cpu.scm"
+ "guix/cve.scm"
+ "guix/cvs-download.scm"
+ "guix/deprecation.scm"
+ "guix/derivations.scm"
+ "guix/describe.scm"
+ "guix/diagnostics.scm"
+ "guix/discovery.scm"
+ "guix/docker.scm"
+ "guix/download.scm"
+ "guix/elf.scm"
+ "guix/ftp-client.scm"
+ "guix/gexp.scm"
+ "guix/git-authenticate.scm"
+ "guix/git-download.scm"
+ "guix/git.scm"
+ "guix/glob.scm"
+ "guix/gnu-maintenance.scm"
+ "guix/gnupg.scm"
+ "guix/grafts.scm"
+ "guix/graph.scm"
+ "guix/hash.scm"
+ "guix/hg-download.scm"
+ "guix/http-client.scm"
+ "guix/i18n.scm"
+ "guix/inferior.scm"
+ "guix/ipfs.scm"
+ "guix/least-authority.scm"
+ "guix/licenses.scm"
+ "guix/lint.scm"
+ "guix/man-db.scm"
+ "guix/memoization.scm"
+ "guix/modules.scm"
+ "guix/monad-repl.scm"
+ "guix/monads.scm"
+ "guix/narinfo.scm"
+ "guix/nar.scm"
+ "guix/openpgp.scm"
+ "guix/packages.scm"
+ "guix/pki.scm"
+ "guix/platform.scm"
+ "guix/profiles.scm"
+ "guix/profiling.scm"
+ "guix/progress.scm"
+ "guix/quirks.scm"
+ "guix/read-print.scm"
+ "guix/records.scm"
+ "guix/remote.scm"
+ "guix/repl.scm"
+ "guix/search-paths.scm"
+ "guix/self.scm"
+ "guix/serialization.scm"
+ "guix/sets.scm"
+ "guix/ssh.scm"
+ "guix/status.scm"
+ "guix/store.scm"
+ "guix/substitutes.scm"
+ "guix/svn-download.scm"
+ "guix/swh.scm"
+ "guix/tests.scm"
+ "guix/transformations.scm"
+ "guix/ui.scm"
+ "guix/upstream.scm"
+ "guix/utils.scm"
+ "guix/workers.scm"
+ (make-regexp* "^guix/platforms/")
+ (make-regexp* "^guix/scripts/")
+ (make-regexp* "^guix/store/"))))
+
+(define-team games
+ (team 'games
+ #:name "Games and Toys"
+ #:description "Packaging programs for amusement."
+ #:scope (list "gnu/packages/games.scm"
+ "gnu/packages/game-development.scm"
+ "gnu/packages/minetest.scm"
+ "gnu/packages/esolangs.scm" ; granted, rather niche
+ "gnu/packages/motti.scm"
+ "guix/build/minetest-build-system.scm")))
+
+(define-team localization
+ (team 'localization
+ #:name "Localization (l10n) team"
+ #:description
+ "Localization of your system to specific languages."
+ #:scope (list "gnu/packages/anthy.scm"
+ "gnu/packages/fcitx5.scm"
+ "gnu/packages/fcitx.scm"
+ "gnu/packages/fonts.scm"
+ "gnu/packages/ibus.scm")))
+
+(define-team translations
+ (team 'translations
+ #:name "Translations"
+ #:scope (list "etc/news.scm"
+ (make-regexp* "^po/"))))
+
+(define-team installer
+ (team 'installer
+ #:name "Installer script and system installer"
+ #:scope (list (make-regexp* "^gnu/installer(\\.scm$|/)"))))
+
+(define-team home
+ (team 'home
+ #:name "Team for \"Guix Home\""
+ #:scope (list (make-regexp* "^(gnu|guix/scripts)/home(\\.scm$|/)")
+ "tests/guix-home.sh"
+ "tests/home-import.scm"
+ "tests/home-services.scm")))
+
+(define-team mentors
+ (team 'mentors
+ #:name "Mentors"
+ #:description
+ "A group of mentors who chaperone contributions by newcomers."))
+
+(define-team mozilla
+ (team 'mozilla
+ #:name "Mozilla"
+ #:description
+ "Taking care about Icecat and Icedove, built from Mozilla Firefox
+and Thunderbird."
+ #:scope (list "gnu/packages/gnuzilla.scm")))
+
+(define-team racket
+ (team 'racket
+ #:name "Racket team"
+ #:description
+ "The Racket language and Racket-based languages, Racket packages,
+Racket's variant of Chez Scheme, and development of a Racket build system and
+importer."
+ #:scope (list "gnu/packages/chez.scm"
+ "gnu/packages/racket.scm")))
+
+(define-team reproduciblebuilds
+ (team 'reproduciblebuilds
+ #:name "Reproducible Builds team"
+ #:description
+ "Reproducible Builds tooling and issues that affect any guix packages."
+ #:scope (list "gnu/packages/diffoscope.scm")))
+
+(define-team gnome
+ (team 'gnome
+ #:name "Gnome team"
+ #:description
+ "The Gnome desktop environment, along with core technologies such as
+GLib/GIO, GTK, GStreamer and Webkit."
+ #:scope (list "gnu/packages/glib.scm"
+ "gnu/packages/gstreamer.scm"
+ "gnu/packages/gtk.scm"
+ "gnu/packages/gnome.scm"
+ "gnu/packages/gnome-xyz.scm"
+ "gnu/packages/webkit.scm"
+ "guix/build/glib-or-gtk-build-system.scm"
+ "guix/build/meson-build-system.scm")))
+
+(define-team xfce
+ (team 'xfce
+ #:name "Xfce team"
+ #:description "Xfce desktop environment."
+ #:scope (list "gnu/packages/xfce.scm")))
+
+(define-team lxqt
+ (team 'lxqt
+ #:name "LXQt team"
+ #:description "LXQt desktop environment."
+ #:scope (list "gnu/packages/lxqt.scm"
+ "gnu/packages/qt.scm")))
+
+
+(define-member (person "Eric Bavier"
+ "bavier@posteo.net")
+ science)
+
+(define-member (person "Lars-Dominik Braun"
+ "lars@6xq.net")
+ python haskell)
+
+(define-member (person "Jonathan Brielmaier"
+ "jonathan.brielmaier@web.de")
+ mozilla)
+
+(define-member (person "Ludovic Courtès"
+ "ludo@gnu.org")
+ core home bootstrap installer mentors)
+
+(define-member (person "Andreas Enge"
+ "andreas@enge.fr")
+ lxqt science tex)
+
+(define-member (person "Tobias Geerinckx-Rice"
+ "me@tobias.gr")
+ core kernel mentors)
+
+(define-member (person "Björn Höfling"
+ "bjoern.hoefling@bjoernhoefling.de")
+ java)
+
+(define-member (person "Leo Famulari"
+ "leo@famulari.name")
+ kernel)
+
+(define-member (person "Efraim Flashner"
+ "efraim@flashner.co.il")
+ embedded bootstrap julia rust science)
+
+(define-member (person "jgart"
+ "jgart@dismail.de")
+ python lisp mentors)
+
+(define-member (person "Guillaume Le Vaillant"
+ "glv@posteo.net")
+ lisp)
+
+(define-member (person "Julien Lepiller"
+ "julien@lepiller.eu")
+ java ocaml translations)
+
+(define-member (person "Philip McGrath"
+ "philip@philipmcgrath.com")
+ racket)
+
+(define-member (person "Mathieu Othacehe"
+ "othacehe@gnu.org")
+ core installer mentors)
+
+(define-member (person "Florian Pelz"
+ "pelzflorian@pelzflorian.de")
+ translations)
+
+(define-member (person "Liliana Marie Prikler"
+ "liliana.prikler@gmail.com")
+ emacs games gnome)
+
+(define-member (person "Ricardo Wurmus"
+ "rekado@elephly.net")
+ r core mentors tex)
+
+(define-member (person "Christopher Baines"
+ "guix@cbaines.net")
+ core mentors ruby)
+
+(define-member (person "Andrew Tropin"
+ "andrew@trop.in")
+ home emacs)
+
+(define-member (person "pukkamustard"
+ "pukkamustard@posteo.net")
+ ocaml)
+
+(define-member (person "Josselin Poiret"
+ "dev@jpoiret.xyz")
+ core installer)
+
+(define-member (person "("
+ "paren@disroot.org")
+ home mentors)
+
+(define-member (person "Simon Tournier"
+ "zimon.toutoune@gmail.com")
+ julia core mentors)
+
+(define-member (person "Raghav Gururajan"
+ "rg@raghavgururajan.name")
+ gnome mentors)
+
+(define-member (person "宋文武"
+ "iyzsong@envs.net")
+ games localization lxqt xfce)
+
+(define-member (person "Vagrant Cascadian"
+ "vagrant@debian.org")
+ embedded)
+
+(define-member (person "Vagrant Cascadian"
+ "vagrant@reproducible-builds.org")
+ reproduciblebuilds)
+
+(define-member (person "Zhu Zihao"
+ "all_but_last@163.com")
+ localization xfce)
+
+(define-member (person "Maxim Cournoyer"
+ "maxim.cournoyer@gmail.com")
+ gnome qt telephony)
+
+(define-member (person "Katherine Cox-Buday"
+ "cox.katherine.e+guix@gmail.com")
+ emacs go lisp)
+
+(define-member (person "Marius Bakke"
+ "marius@gnu.org")
+ python)
+
+(define-member (person "Munyoki Kilyungi"
+ "me@bonfacemunyoki.com")
+ python lisp)
+
+
+(define (find-team name)
+ (or (hash-ref %teams (string->symbol name))
+ (error (format #false
+ "no such team: ~a~%" name))))
+
+(define (find-team-by-scope files)
+ "Return the team(s) which scope matches at least one of the FILES, as list
+of file names as string."
+ (hash-fold
+ (lambda (key team acc)
+ (if (any (lambda (file)
+ (any (match-lambda
+ ((? string? scope)
+ (string=? scope file))
+ ((? regexp*? scope)
+ (regexp*-exec scope file)))
+ (team-scope team)))
+ files)
+ (cons team acc)
+ acc))
+ '()
+ %teams))
+
+(define (cc . teams)
+ "Return arguments for `git send-email' to notify the members of the given
+TEAMS when a patch is received by Debbugs."
+ (let ((members (append-map team-members teams)))
+ (unless (null? members)
+ (format #true "--add-header=\"X-Debbugs-Cc: ~{~a~^, ~}\""
+ (map person-email (sort-members members))))))
+
+(define (sort-members members)
+ "Deduplicate and sort MEMBERS alphabetically by their name."
+ (sort (delete-duplicates members equal?)
+ (lambda (m1 m2)
+ (string<? (person-name m1) (person-name m2)))))
+
+(define (member->string member)
+ "Return the 'email <name>' string representation of MEMBER."
+ (let* ((name (person-name member))
+ (quoted-name/maybe (if (string-contains name ",")
+ (string-append "\"" name "\"")
+ name)))
+ (format #false "~a <~a>" quoted-name/maybe (person-email member))))
+
+(define* (list-members team #:key (prefix ""))
+ "Print the members of the given TEAM."
+ (for-each (lambda (member)
+ (format #t "~a~a~%" prefix (member->string member)))
+ (sort-members (team-members team))))
+
+(define (print-team team)
+ "Print TEAM, a <team> record object."
+ (format #t
+ "\
+id: ~a
+name: ~a
+description: ~a
+~amembers:
+"
+ (team-id team)
+ (team-name team)
+ (or (and=> (team-description team)
+ (lambda (text)
+ (string->recutils
+ (fill-paragraph text (%text-width)
+ (string-length "description: ")))))
+ "<none>")
+ (match (team-scope team)
+ (() "")
+ (scope (format #f "scope:~%~{+ ~a~^~%~}~%"
+ (sort (map (match-lambda
+ ((? regexp*? rx)
+ (regexp*-pattern rx))
+ (item item))
+ scope)
+ string<?)))))
+ (list-members team #:prefix "+ ")
+ (newline))
+
+(define (sort-teams teams)
+ "Sort TEAMS, a list of <team> record objects."
+ (sort teams
+ (lambda (team1 team2)
+ (string<? (symbol->string (team-id team1))
+ (symbol->string (team-id team2))))))
+
+(define* (list-teams #:optional team-names)
+ "Print all teams, their scope and their members."
+ (for-each print-team
+ (sort-teams
+ (if team-names
+ (map find-team team-names)
+ (hash-map->list (lambda (_ value) value) %teams)))))
+
+
+(define (diff-revisions rev-start rev-end)
+ "Return the list of added, modified or removed files between REV-START
+and REV-END, two git revision strings."
+ (let* ((repository (repository-open (getcwd)))
+ (commit1 (commit-lookup repository
+ (object-id
+ (revparse-single repository rev-start))))
+ (commit2 (commit-lookup repository
+ (object-id
+ (revparse-single repository rev-end))))
+ (diff (diff-tree-to-tree repository
+ (commit-tree commit1)
+ (commit-tree commit2)))
+ (files '()))
+ (diff-foreach
+ diff
+ (lambda (delta progress)
+ (set! files
+ (cons (diff-file-path (diff-delta-old-file delta)) files))
+ 0)
+ (const 0)
+ (const 0)
+ (const 0))
+ files))
+
+(define (git-patch->commit-id file)
+ "Parse the commit ID from FILE, a patch produced with git."
+ (call-with-input-file file
+ (lambda (port)
+ (let loop ((line (read-line port)))
+ (when (eof-object? line)
+ (error "could not find 'from' commit in patch" file))
+ (let ((m (string-match "^From ([0-9a-f]{40})" line)))
+ (if m
+ (match:substring m 1)
+ (loop (read-line port))))))))
+
+(define (git-patch->revisions file)
+ "Return the start and end revisions of FILE, a patch file produced with git."
+ (let* ((rev-end (git-patch->commit-id file))
+ (rev-start (string-append rev-end "^")))
+ (list rev-start rev-end)))
+
+(define (patch->teams patch-file)
+ "Return the name of the teams in scope for the changes in PATCH-FILE."
+ (map (compose symbol->string team-id)
+ (find-team-by-scope (apply diff-revisions
+ (git-patch->revisions patch-file)))))
+
+
+(define (main . args)
+ (match args
+ (("cc" . team-names)
+ (apply cc (map find-team team-names)))
+ (("cc-members" patch-file)
+ (unless (file-exists? patch-file)
+ (error "patch file does not exist:" patch-file))
+ (apply main "cc-members" (git-patch->revisions patch-file)))
+ (("cc-members" rev-start rev-end)
+ (apply cc (find-team-by-scope
+ (diff-revisions rev-start rev-end))))
+ (("cc-members-header-cmd" patch-file)
+ (let* ((teams (map find-team (patch->teams patch-file)))
+ (members (sort-members (append-map team-members teams))))
+ (unless (null? members)
+ (format #true "X-Debbugs-Cc: ~{~a~^, ~}"
+ (map member->string members)))))
+ (("cc-mentors-header-cmd" patch-file)
+ (format #true "X-Debbugs-Cc: ~{~a~^, ~}"
+ (map member->string
+ (sort-members (team-members (find-team "mentors"))))))
+ (("get-maintainer" patch-file)
+ (apply main "list-members" (patch->teams patch-file)))
+ (("list-teams" . args)
+ (list-teams))
+ (("list-members" . team-names)
+ (for-each
+ (lambda (team-name)
+ (list-members (find-team team-name)))
+ team-names))
+ (("show" . team-names)
+ (list-teams team-names))
+ (anything
+ (format (current-error-port)
+ "Usage: etc/teams.scm <command> [<args>]
+
+Commands:
+ cc <team-name>
+ get git send-email flags for cc-ing <team-name>
+ cc-members <start> <end> | <patch>
+ cc teams related to files changed between revisions or in a patch file
+ cc-members-header-cmd <patch>
+ cc-members variant for use with 'git send-email --header-cmd'
+ cc-mentors-header-cmd <patch>
+ command to use with 'git send-email --header-cmd' to notify mentors
+ list-teams
+ list teams and their members
+ list-members <team-name>
+ list members belonging to <team-name>
+ get-maintainer <patch>
+ compatibility mode with Linux get_maintainer.pl
+ show <team-name>
+ display <team-name> properties~%"))))
+
+(apply main (cdr (command-line)))