gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
73.58k stars 7.39k forks source link

Extend sort function to support semantic version numbers #9402

Open alexlafreniere opened 2 years ago

alexlafreniere commented 2 years ago

We use Hugo to manage our documentation site. On this site, we provide documentation for various versions of software packages that are released according to semantic versioning rules. We list versions of the packages in descending order according to their version number for easy navigation. Today, Hugo has no direct support for sorting by semver, so we settle for sorting by the date the version was released. This is less than desirable in the situation where we are actively maintaining more than one major or minor version at a time, resulting in seemingly unsorted lists of versions for our users.

On a stale PR attempting to implement sorting of semver numbers, @jmooring suggested extending the sort function to support this functionality. Implementing sorting in this way would avoid breaking existing sites and allow sorting any resource by any front matter field according to semver conventions:

{{ range (sort .Pages "Params.version" "semVerAsc" ) }}
{{ range (sort .Pages "Params.version" "semVerDesc" ) }}

I'm unfamiliar with the Hugo codebase, but would be more than happy to perform the implementation once the design proposal is finalized and accepted by the Hugo maintainers. My initial survey of the source suggests most changes would center around tpl/collections/sort.go, but a nudge in the right direction from more experienced contributors would be appreciated.

jmooring commented 2 years ago

The high-level desire is to specify a sort method as well as a sort direction. Whether those are specified together in a single string, in separate args, or in a map is an implementation detail.

In addition to semver sorting, at some point we might want to add a natural sort.

moorereason commented 2 years ago

@alexlafreniere, what do you expect to happen when the input contains an invalid semver string?

I'll throw out a proposal for discussion:

The existing function signature for sort is:

sort SEQUENCE [FIELD [DIRECTION]]

We could add a new optional field for the sort method:

sort SEQUENCE [FIELD [DIRECTION [METHOD]]]

Example usage:

{{ sort .Pages "Params.version" "asc" "semver" }}
{{ sort .Pages "Params.version" "desc" "semver" }}
alexlafreniere commented 2 years ago

semver strings are strictly defined. I'd be okay with throwing an error when an invalid string is encountered.

jmooring commented 2 years ago

Also need to come up with a name for current sort method, which seems to sort numeric things numerically, and alpha things alphabetically. Maybe the method is simply default with an explanation in the docs.

Test data ``` {{ $m := slice (dict "name" "Buddy" "age_int" 3 "age_string" "a3") (dict "name" "Duke" "age_int" 21 "age_string" "a21") (dict "name" "Rover" "age_int" 2 "age_string" "a2") (dict "name" "Spot" "age_int" 1 "age_string" "a1") }} {{ $mSorted := sort $m "age_int" }}
{{ jsonify (dict "indent" "  ") $mSorted }}
{{ $mSorted := sort $m "age_string" }}
{{ jsonify (dict "indent" "  ") $mSorted }}
```
ferdnyc commented 6 days ago

Just to document the workaround we went with for Graphviz, padding out our major version numbers to two digits (or, more generally, the length of the longest major version in use) did the trick. "10.0.1" will sort before "9.0.0", but it sorts after "09.0.0".

ferdnyc commented 6 days ago

(If it's not the major version but some other version component that gets into double digits, you'd want to pad those out as well / instead. Just like with the major versions, "1.10.1" comes before "1.9.1", but should sort after "1.09.1".)