diff --git a/flake-info/src/data/export.rs b/flake-info/src/data/export.rs index 932c66ee..853a9d18 100644 --- a/flake-info/src/data/export.rs +++ b/flake-info/src/data/export.rs @@ -68,6 +68,8 @@ pub enum Derivation { package_license_set: Vec, package_maintainers: Vec, package_maintainers_set: Vec, + package_teams: Vec, + package_teams_set: Vec, package_description: Option, package_longDescription: Option, package_hydra: (), @@ -157,6 +159,8 @@ impl TryFrom<(import::FlakeEntry, super::Flake)> for Derivation { package_description: description.clone(), package_maintainers: vec![maintainer.clone()], package_maintainers_set: maintainer.name.map_or(vec![], |n| vec![n]), + package_teams: Vec::new(), + package_teams_set: Vec::new(), package_longDescription: long_description, package_hydra: (), package_system: String::new(), @@ -233,6 +237,19 @@ impl TryFrom for Derivation { .flat_map(|m| m.name.to_owned()) .collect(); + let package_teams: Vec = package + .meta + .teams + .map_or(Default::default(), Flatten::flatten) + .into_iter() + .map(Into::into) + .collect(); + + let package_teams_set = package_teams + .iter() + .flat_map(|m| m.shortName.to_owned()) + .collect(); + let long_description = package .meta .long_description @@ -260,6 +277,8 @@ impl TryFrom for Derivation { package_license_set, package_maintainers, package_maintainers_set, + package_teams, + package_teams_set, package_description: package.meta.description.clone(), package_longDescription: long_description, package_hydra: (), @@ -348,6 +367,50 @@ impl From for Maintainer { } } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[allow(non_snake_case)] +pub struct Team { + members: Vec, + scope: Option, + shortName: Option, + githubTeams: Vec, +} + +impl From for Team { + fn from(import: import::Team) -> Self { + match import { + import::Team::Full { + members, + scope, + shortName, + githubTeams, + } => + Team { + members: members + .map(OneOrMany::into_list) + .unwrap_or_default() + .into_iter() + .map(Maintainer::from) + .collect(), + scope, + shortName, + githubTeams: githubTeams + .map(OneOrMany::into_list) + .unwrap_or_default() + .into_iter() + .collect(), + }, + #[allow(non_snake_case)] + import::Team::Simple(shortName) => Team { + shortName: Some(shortName), + scope: None, + members: Vec::new(), + githubTeams: Vec::new(), + }, + } + } +} + // ----- output type /// Export type that brings together derivation and optional flake info diff --git a/flake-info/src/data/import.rs b/flake-info/src/data/import.rs index ad195d4a..f5931c36 100644 --- a/flake-info/src/data/import.rs +++ b/flake-info/src/data/import.rs @@ -186,6 +186,7 @@ pub enum NixpkgsEntry { pub struct Meta { pub license: Option>>, pub maintainers: Option>, + pub teams: Option>, pub homepage: Option>, pub platforms: Option, #[serde(rename = "badPlatforms")] @@ -207,6 +208,19 @@ pub enum Maintainer { Simple(String), } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum Team { + #[allow(non_snake_case)] + Full { + members: Option>, + scope: Option, + shortName: Option, + githubTeams: Option>, + }, + Simple(String), +} + arg_enum! { /// The type of derivation (placed in packages. or apps.) /// Used to command the extraction script diff --git a/flake-info/src/data/utility.rs b/flake-info/src/data/utility.rs index 0db3295b..68218e63 100644 --- a/flake-info/src/data/utility.rs +++ b/flake-info/src/data/utility.rs @@ -18,6 +18,16 @@ impl OneOrMany { OneOrMany::Many(many) => many, } } + + pub fn map(self, f: F) -> OneOrMany + where + F: Fn(T) -> U, + { + match self { + Self::One(x) => OneOrMany::One(f(x)), + Self::Many(xs) => OneOrMany::Many(xs.into_iter().map(f).collect()), + } + } } /// A utility type that flattens lists of lists as seen with `maintainers` and `platforms` on selected packages diff --git a/flake-info/src/elastic.rs b/flake-info/src/elastic.rs index 5f41c4e2..d5312ad5 100644 --- a/flake-info/src/elastic.rs +++ b/flake-info/src/elastic.rs @@ -126,6 +126,21 @@ lazy_static! { }, }, "package_maintainers_set": {"type": "keyword"}, + "package_teams": { + "type": "nested", + "properties": { + "members": { + "type": "nested", + "properties": { + "name": {"type": "text"}, + "email": {"type": "text"}, + "github": {"type": "text"}, + }, + }, + "githubTeams": {"type": "keyword"}, + }, + }, + "package_teams_set": {"type": "keyword"}, "package_homepage": { "type": "keyword" }, diff --git a/frontend/src/Page/Packages.elm b/frontend/src/Page/Packages.elm index ff9dd78c..8da03a4a 100644 --- a/frontend/src/Page/Packages.elm +++ b/frontend/src/Page/Packages.elm @@ -73,6 +73,7 @@ type alias ResultItemSource = , longDescription : Maybe String , licenses : List ResultPackageLicense , maintainers : List ResultPackageMaintainer + , teams: List ResultPackageTeam , platforms : List String , position : Maybe String , homepage : List String @@ -97,6 +98,14 @@ type alias ResultPackageMaintainer = } +type alias ResultPackageTeam = + { members : Maybe (List ResultPackageMaintainer) + , scope : Maybe String + , shortName: String + , githubTeams : Maybe (List String) + } + + type alias ResultPackageHydra = { build_id : Int , build_status : Int @@ -120,6 +129,7 @@ type alias ResultAggregations = , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation , package_maintainers_set : Search.Aggregation + , package_teams_set : Search.Aggregation , package_license_set : Search.Aggregation } @@ -129,6 +139,7 @@ type alias Aggregations = , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation , package_maintainers_set : Search.Aggregation + , package_teams_set : Search.Aggregation , package_license_set : Search.Aggregation } @@ -137,6 +148,7 @@ type alias Buckets = { packageSets : List String , licenses : List String , maintainers : List String + , teams : List String , platforms : List String } @@ -146,6 +158,7 @@ emptyBuckets = { packageSets = [] , licenses = [] , maintainers = [] + , teams = [] , platforms = [] } @@ -278,6 +291,11 @@ viewBuckets bucketsAsString result = (result.aggregations.package_maintainers_set.buckets |> sortBuckets) (createBucketsMsg .maintainers (\s v -> { s | maintainers = v })) selectedBucket.maintainers + |> viewBucket + "Teams" + (result.aggregations.package_teams_set.buckets |> sortBuckets) + (createBucketsMsg .teams (\s v -> { s | teams = v })) + selectedBucket.teams |> viewBucket "Platforms" (result.aggregations.package_platforms.buckets |> sortBuckets |> filterPlatformsBucket) @@ -462,6 +480,17 @@ viewResultItem nixosChannels channel showInstallDetails show item = ) ] + showTeam team = + let + maybe m d = + Maybe.withDefault d m + + showTeamEntry githubTeam = + (a [ href ((String.append "https://github.com/orgs/NixOS/teams/") githubTeam) ] [ text githubTeam ]) + in + li [] + ([ text team.shortName ] ++ (List.map showTeamEntry (maybe team.githubTeams []))) + mailtoAllMaintainers maintainers = let maintainerMails = @@ -487,7 +516,7 @@ viewResultItem nixosChannels channel showInstallDetails show item = Nothing -> li [] [ text platform ] - maintainersAndPlatforms = + maintainersTeamsAndPlatforms = div [] [ div [] (List.append [ h4 [] [ text "Maintainers" ] ] @@ -503,6 +532,16 @@ viewResultItem nixosChannels channel showInstallDetails show item = ] ) ) + , div [] + (if not (List.isEmpty item.source.teams) then + (List.append [ h4 [] [ text "Teams" ] ] + [ ul [] + (List.map showTeam item.source.teams) + ] + ) + else + [] + ) , div [] (List.append [ h4 [] [ text "Platforms" ] ] (if List.isEmpty item.source.platforms then @@ -748,7 +787,7 @@ viewResultItem nixosChannels channel showInstallDetails show item = Maybe.map Tuple.first item.source.flakeUrl ] :: programs - :: maintainersAndPlatforms + :: maintainersTeamsAndPlatforms :: [] ) ] @@ -878,6 +917,7 @@ makeRequestBody query from size maybeBuckets sort = [ ( "package_attr_set", currentBuckets.packageSets ) , ( "package_license_set", currentBuckets.licenses ) , ( "package_maintainers_set", currentBuckets.maintainers ) + , ( "package_teams_set", currentBuckets.teams ) , ( "package_platforms", currentBuckets.platforms ) ] @@ -931,6 +971,7 @@ makeRequestBody query from size maybeBuckets sort = [ "package_attr_set" , "package_license_set" , "package_maintainers_set" + , "package_teams_set" , "package_platforms" ] filterByBuckets @@ -954,16 +995,18 @@ encodeBuckets options = [ ( "package_attr_set", Json.Encode.list Json.Encode.string options.packageSets ) , ( "package_license_set", Json.Encode.list Json.Encode.string options.licenses ) , ( "package_maintainers_set", Json.Encode.list Json.Encode.string options.maintainers ) + , ( "package_teams_set", Json.Encode.list Json.Encode.string options.teams ) , ( "package_platforms", Json.Encode.list Json.Encode.string options.platforms ) ] decodeBuckets : Json.Decode.Decoder Buckets decodeBuckets = - Json.Decode.map4 Buckets + Json.Decode.map5 Buckets (Json.Decode.field "package_attr_set" (Json.Decode.list Json.Decode.string)) (Json.Decode.field "package_license_set" (Json.Decode.list Json.Decode.string)) (Json.Decode.field "package_maintainers_set" (Json.Decode.list Json.Decode.string)) + (Json.Decode.field "package_teams_set" (Json.Decode.list Json.Decode.string)) (Json.Decode.field "package_platforms" (Json.Decode.list Json.Decode.string)) @@ -980,6 +1023,7 @@ decodeResultItemSource = |> Json.Decode.Pipeline.required "package_longDescription" (Json.Decode.nullable Json.Decode.string) |> Json.Decode.Pipeline.required "package_license" (Json.Decode.list decodeResultPackageLicense) |> Json.Decode.Pipeline.required "package_maintainers" (Json.Decode.list decodeResultPackageMaintainer) + |> Json.Decode.Pipeline.required "package_teams" (Json.Decode.list decodeResultPackageTeam) |> Json.Decode.Pipeline.required "package_platforms" (Json.Decode.map filterPlatforms (Json.Decode.list Json.Decode.string)) |> Json.Decode.Pipeline.required "package_position" (Json.Decode.nullable Json.Decode.string) |> Json.Decode.Pipeline.required "package_homepage" decodeHomepage @@ -1089,6 +1133,15 @@ decodeResultPackageMaintainer = (Json.Decode.field "github" (Json.Decode.nullable Json.Decode.string)) +decodeResultPackageTeam : Json.Decode.Decoder ResultPackageTeam +decodeResultPackageTeam = + Json.Decode.map4 ResultPackageTeam + (Json.Decode.field "members" (Json.Decode.nullable (Json.Decode.list decodeResultPackageMaintainer))) + (Json.Decode.field "scope" (Json.Decode.nullable Json.Decode.string)) + (Json.Decode.field "shortName" Json.Decode.string) + (Json.Decode.field "githubTeams" (Json.Decode.nullable (Json.Decode.list Json.Decode.string))) + + decodeResultPackageHydra : Json.Decode.Decoder ResultPackageHydra decodeResultPackageHydra = Json.Decode.succeed ResultPackageHydra @@ -1111,19 +1164,21 @@ decodeResultPackageHydraPath = decodeResultAggregations : Json.Decode.Decoder ResultAggregations decodeResultAggregations = - Json.Decode.map5 ResultAggregations + Json.Decode.map6 ResultAggregations (Json.Decode.field "all" decodeAggregations) (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) + (Json.Decode.field "package_teams_set" Search.decodeAggregation) (Json.Decode.field "package_license_set" Search.decodeAggregation) decodeAggregations : Json.Decode.Decoder Aggregations decodeAggregations = - Json.Decode.map5 Aggregations + Json.Decode.map6 Aggregations (Json.Decode.field "doc_count" Json.Decode.int) (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) + (Json.Decode.field "package_teams_set" Search.decodeAggregation) (Json.Decode.field "package_license_set" Search.decodeAggregation)