Skip to content

[Tracking] Nixpkgs GitHub teams should match lib.teams #456365

@infinisil

Description

@infinisil

With #450864 it's now possible to sync the member list from a GitHub team to lib.teams.*.members by:

  • Ensuring that the team has explicit Nixpkgs access and waiting for the weekly sync to run and be merged
  • Setting the github property in team-list.nix:
    some-team.github = "some-team";

Ideally we should have:

  • GitHub teams1 should have an entry in lib.teams, which ensures that each team member has a contactable maintainers entry
  • lib.teams should have a matching GitHub team, so they can be requested for review without needing to look up individual members
  • lib.teams.${name} == @NixOS/${name} so we don't need to guess
  • GitHub teams should have a maintainer, so the members can be self-managed, and people know who to ask

All of this together allows removing the ability to create teams via team-list.nix and using self-serviced GitHub teams as the source of truth, mirrored directly to lib.teams. This means lib.teams will always match the GitHub teams, which is great for transparency and consistency.

To get to that point, we have some manual work to do though. This issue tracks progress towards that. Below is an automatically generated listing of all the remaining issues2. It was generated with the following script on #456345. If we really want to go for this, we should ping everybody for crowd-sourcing this.

Cc @NixOS/nixpkgs-core

Details
let
  lib = import ./lib;
  githubTeams = lib.importJSON ./maintainers/github-teams.json;
  expectedPairs = [
    "acme"
    "bazel"
    "coq"
    "dotnet"
    "emacs"
    "gitlab"
    "kodi"
    "lxc"
    "mate"
    { github = "nix-formatting"; libTeam = "formatter"; }
    { github = "nix-team"; libTeam = "nix"; }
    "node"
    "xfce"
  ];

  pairsLibToGh = lib.listToAttrs (lib.map (v: lib.nameValuePair (v.libTeam or v) (v.github or v)) expectedPairs);
  libToGh = lib.mapAttrs (ln: lv: lv.github or pairsLibToGh.${ln} or null) lib.teams;

  ghMap = lib.mapAttrs' (ln: lv: lib.nameValuePair lv.github ln) (lib.filterAttrs (ln: lv: lv ? github) lib.teams);
  pairsGhToLib = lib.listToAttrs (lib.map (v: lib.nameValuePair (v.github or v) (v.libTeam or v)) expectedPairs);
  ghToLib = lib.mapAttrs (ghn: ghv: ghMap.${ghn} or pairsGhToLib.${ghn} or null) githubTeams;

  ghCommon = lib.filterAttrs (ghn: ln: ln != null) ghToLib;

  ghMembers = ghn: lib.map lib.toLower (lib.attrNames (githubTeams.${ghn}.maintainers // githubTeams.${ghn}.members));
  #idToHandle = lib.mapAttrs' (_: v: lib.nameValuePair (toString v.githubId) v.github) lib.maintainers;
  libMembers = ln: lib.map (m: lib.toLower m.github) lib.teams.${ln}.members;

  memberMismatch = lib.filterAttrs (ghn: { ln, onlyGh, onlyLib }: onlyGh != [] || onlyLib != []) (lib.mapAttrs (ghn: ln:
    let
      gh = lib.sort builtins.lessThan (ghMembers ghn);
      l = lib.sort builtins.lessThan (libMembers ln);
    in
    {
      inherit ln;
      onlyGh = lib.subtractLists l gh;
      onlyLib = lib.subtractLists gh l;
    }
  ) ghCommon);

  ghWithoutLib = lib.attrNames (lib.filterAttrs (gn: ln: ln == null) ghToLib);

  nameMismatch = lib.filterAttrs (ghn: ln: ghn != ln) ghCommon;

  maintainerLess = lib.attrNames (lib.filterAttrs (ghn: ghv: ghv.maintainers == {}) githubTeams);

  libWithoutGh = lib.attrNames (lib.filterAttrs (ln: gn: gn == null) libToGh);

  teamsWithPos = import ./maintainers/team-list.nix { inherit lib; };
  libPos = ln: "https://github.com/NixOS/nixpkgs/blob/8f87d1d5160c7a28197f78064cc9a5b3a05e908c/maintainers/team-list.nix#L${toString (builtins.unsafeGetAttrPos ln teamsWithPos).line}";
  #res = lib.filterAttrs (ghn: _: (lib.attrNames githubTeams)
  res = {
    inherit ghWithoutLib memberMismatch nameMismatch maintainerLess libWithoutGh;
    #inherit ghToLib libToGh memberMismatch;
    #x = ghToMembers "haskell";
  };

  prettyGhTeam = ghn: "[`@NixOS/${ghn}`](https://github.com/orgs/NixOS/teams/${ghn})";
  prettyGhHandle = handle: "[`@${handle}`](https://github.com/${handle})";
  prettyLibTeam = ln: "[`lib.teams.${ln}`](${libPos ln})";

  string = ''
    GitHub teams that don't have an corresponding entry in `lib.teams`:
    ${lib.concatStrings (lib.map (ghn: "- [ ] [`@NixOS/${ghn}`](https://github.com/orgs/NixOS/teams/${ghn})\n") ghWithoutLib)}
    `lib.teams` that don't have a corresponding GitHub team:
    ${lib.concatStrings (lib.map (ln: "- [ ] ${prettyLibTeam ln}\n") libWithoutGh)}
    Corresponding teams whose members don't match between GitHub and `lib.teams`:
    ${lib.concatStrings (lib.mapAttrsToList (ghn: { ln, onlyGh, onlyLib }: ''
      - [ ] ${prettyGhTeam ghn}/${prettyLibTeam ln}:
      ${lib.optionalString (onlyGh != []) "  - Only on GitHub: ${lib.concatMapStringsSep ", " prettyGhHandle onlyGh}\n"}${lib.optionalString (onlyLib != []) "  - Only in `lib.teams`: ${lib.concatMapStringsSep ", " prettyGhHandle onlyLib}\n"}'') memberMismatch)}
    GitHub teams whose name doesn't match the corresponding `lib.teams`:
    ${lib.concatStrings (lib.mapAttrsToList (ghn: ln: ''
      - [ ] ${prettyGhTeam ghn} <-> ${prettyLibTeam ln}
    '') nameMismatch)}
    GitHub teams that don't have a maintainer:
    ${lib.concatStrings (lib.map (ghn: "- [ ] ${prettyGhTeam ghn}\n") maintainerLess)}
  '';
in
builtins.trace (lib.generators.toPretty {} res)
builtins.trace string null

GitHub teams that don't have an corresponding entry in lib.teams:

lib.teams that don't have a corresponding GitHub team:

Corresponding teams whose members don't match between GitHub and lib.teams:

GitHub teams whose name doesn't match the corresponding lib.teams:

GitHub teams that don't have a maintainer:

Footnotes

  1. Only GitHub teams with explicit Nixpkgs access, see the synced github-teams.json

  2. With https://github.com/NixOS/nixpkgs/pull/456345 already taken into account

Metadata

Metadata

Assignees

No one assigned

    Labels

    5.scope: trackingLong-lived issue tracking long-term fixes or multiple sub-problems
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions