Ver código fonte

Add Docker Hub categories (#2446)

* Add repo metadata, start with categories!

scripts for checking repo categories, updating the canonical set
added categories to push.pl

* Add initial set of semi-acurate categories

* Adjustments following tianon's review

* Simplify metadata.sh use cases (CI or interactive); just diff and check all the time

Update README.md about metadata.sh usage

* Unify the categories checks into one jq expressions

* Update initial categories

* Link to Docker docs for categories; minor categories script adjustments
yosifkit 1 ano atrás
pai
commit
63c9218175
100 arquivos alterados com 823 adições e 0 exclusões
  1. 9 0
      .ci/check-metadata.sh
  2. 5 0
      .github/workflows/ci.yml
  3. 13 0
      README.md
  4. 7 0
      adminer/metadata.json
  5. 7 0
      aerospike/metadata.json
  6. 7 0
      almalinux/metadata.json
  7. 7 0
      alpine/metadata.json
  8. 7 0
      alt/metadata.json
  9. 7 0
      amazoncorretto/metadata.json
  10. 7 0
      amazonlinux/metadata.json
  11. 7 0
      api-firewall/metadata.json
  12. 7 0
      arangodb/metadata.json
  13. 7 0
      archlinux/metadata.json
  14. 7 0
      backdrop/metadata.json
  15. 7 0
      bash/metadata.json
  16. 7 0
      bonita/metadata.json
  17. 7 0
      buildpack-deps/metadata.json
  18. 7 0
      busybox/metadata.json
  19. 7 0
      caddy/metadata.json
  20. 7 0
      cassandra/metadata.json
  21. 5 0
      centos/metadata.json
  22. 7 0
      chronograf/metadata.json
  23. 7 0
      cirros/metadata.json
  24. 7 0
      clearlinux/metadata.json
  25. 7 0
      clefos/metadata.json
  26. 7 0
      clojure/metadata.json
  27. 7 0
      composer/metadata.json
  28. 5 0
      consul/metadata.json
  29. 7 0
      convertigo/metadata.json
  30. 7 0
      couchbase/metadata.json
  31. 7 0
      couchdb/metadata.json
  32. 7 0
      crate/metadata.json
  33. 7 0
      dart/metadata.json
  34. 7 0
      debian/metadata.json
  35. 7 0
      docker/metadata.json
  36. 7 0
      drupal/metadata.json
  37. 7 0
      eclipse-mosquitto/metadata.json
  38. 7 0
      eclipse-temurin/metadata.json
  39. 5 0
      eggdrop/metadata.json
  40. 7 0
      elasticsearch/metadata.json
  41. 7 0
      elixir/metadata.json
  42. 7 0
      emqx/metadata.json
  43. 7 0
      erlang/metadata.json
  44. 5 0
      express-gateway/metadata.json
  45. 7 0
      fedora/metadata.json
  46. 7 0
      flink/metadata.json
  47. 7 0
      fluentd/metadata.json
  48. 5 0
      friendica/metadata.json
  49. 7 0
      gazebo/metadata.json
  50. 7 0
      gcc/metadata.json
  51. 7 0
      geonetwork/metadata.json
  52. 16 0
      get-categories.sh
  53. 7 0
      ghost/metadata.json
  54. 7 0
      golang/metadata.json
  55. 7 0
      gradle/metadata.json
  56. 7 0
      groovy/metadata.json
  57. 7 0
      haproxy/metadata.json
  58. 7 0
      haskell/metadata.json
  59. 7 0
      haxe/metadata.json
  60. 5 0
      hello-world/metadata.json
  61. 7 0
      hitch/metadata.json
  62. 7 0
      httpd/metadata.json
  63. 7 0
      hylang/metadata.json
  64. 7 0
      ibm-semeru-runtimes/metadata.json
  65. 7 0
      ibmjava/metadata.json
  66. 7 0
      influxdb/metadata.json
  67. 5 0
      irssi/metadata.json
  68. 7 0
      jetty/metadata.json
  69. 5 0
      jobber/metadata.json
  70. 7 0
      joomla/metadata.json
  71. 7 0
      jruby/metadata.json
  72. 7 0
      julia/metadata.json
  73. 7 0
      kapacitor/metadata.json
  74. 7 0
      kibana/metadata.json
  75. 7 0
      kong/metadata.json
  76. 7 0
      lightstreamer/metadata.json
  77. 7 0
      liquibase/metadata.json
  78. 7 0
      logstash/metadata.json
  79. 7 0
      mageia/metadata.json
  80. 7 0
      mariadb/metadata.json
  81. 7 0
      matomo/metadata.json
  82. 7 0
      maven/metadata.json
  83. 7 0
      mediawiki/metadata.json
  84. 7 0
      memcached/metadata.json
  85. 22 0
      metadata.json
  86. 118 0
      metadata.sh
  87. 7 0
      mongo-express/metadata.json
  88. 7 0
      mongo/metadata.json
  89. 7 0
      monica/metadata.json
  90. 7 0
      mono/metadata.json
  91. 7 0
      mysql/metadata.json
  92. 5 0
      nats-streaming/metadata.json
  93. 7 0
      nats/metadata.json
  94. 7 0
      neo4j/metadata.json
  95. 7 0
      neurodebian/metadata.json
  96. 7 0
      nextcloud/metadata.json
  97. 7 0
      nginx/metadata.json
  98. 7 0
      node/metadata.json
  99. 7 0
      notary/metadata.json
  100. 7 0
      odoo/metadata.json

+ 9 - 0
.ci/check-metadata.sh

@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+
+cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.."
+
+# metadata.sh takes directories with a 'metadata.json' in them
+# metadata.json is expected in every repo
+# "." so that the canonical source metadata.json is checked too
+./metadata.sh */ .

+ 5 - 0
.github/workflows/ci.yml

@@ -46,3 +46,8 @@ jobs:
           fetch-depth: 0
       - run: .ci/check-pr-no-readme.sh
     if: ${{ github.event_name == 'pull_request' }}
+  metadata:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - run: .ci/check-metadata.sh

+ 13 - 0
README.md

@@ -61,6 +61,7 @@ After opening your Pull Request the changes will be checked by an automated `mar
 -	Create a `license.md` (required)
 -	Create a `maintainer.md` (required)
 -	Create a `github-repo` (required)
+-	Create a `metadata.json` (required)
 -	Add a `logo.png` (recommended)
 
 Optionally:
@@ -128,6 +129,18 @@ The image is automatically scaled to a 120 pixel square for the top of the Docke
 
 This file should contain a link to the maintainers of the Dockerfile.
 
+## `metadata.json`
+
+This file contains data about the repo for Docker Hub. The minimum file is defined below. `./metadata.sh [repo-name]` must be used to correctly format it (use `-w` to apply its suggested format changes). Only three sorted unique Docker Hub categories are allowed. `metadata.json` in the root contains the list of categories to choose from. See descriptions for the categories on the [Docker docs site](https://docs.docker.com/docker-hub/repos/categories/).
+
+```json
+{
+    "hub": {
+         "categories": []
+    }
+}
+```
+
 ## `README-short.txt`
 
 This is the short description for the Docker Hub, limited to 100 characters in a single line.

+ 7 - 0
adminer/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
aerospike/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
almalinux/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
alpine/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
alt/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
amazoncorretto/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
amazonlinux/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
api-firewall/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "api-management"
+    ]
+  }
+}

+ 7 - 0
arangodb/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
archlinux/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
backdrop/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
bash/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 7 - 0
bonita/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
buildpack-deps/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 7 - 0
busybox/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
caddy/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 7 - 0
cassandra/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 5 - 0
centos/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
chronograf/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 7 - 0
cirros/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
clearlinux/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
clefos/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
clojure/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
composer/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 5 - 0
consul/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
convertigo/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
couchbase/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
couchdb/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
crate/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
dart/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
debian/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
docker/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 7 - 0
drupal/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
eclipse-mosquitto/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "message-queues"
+    ]
+  }
+}

+ 7 - 0
eclipse-temurin/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 5 - 0
eggdrop/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
elasticsearch/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
elixir/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
emqx/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "message-queues"
+    ]
+  }
+}

+ 7 - 0
erlang/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 5 - 0
express-gateway/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
fedora/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
flink/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "data-science"
+    ]
+  }
+}

+ 7 - 0
fluentd/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 5 - 0
friendica/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
gazebo/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
gcc/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
geonetwork/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "data-science"
+    ]
+  }
+}

+ 16 - 0
get-categories.sh

@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+
+workdir="$(readlink -f "$BASH_SOURCE")"
+workdir="$(dirname "$workdir")"
+
+jsonFile='metadata.json'
+canonicalMetadataFile="$workdir/$jsonFile"
+
+# add categories slugs to canonicalMetadataFile without losing other keys there
+curl -fsSL https://hub.docker.com/v2/categories | jq -s --sort-keys '
+	.[0] as $allCategories
+	| .[1]
+	| .hub.categories = ( [ $allCategories[].slug ] | sort )
+' - "$canonicalMetadataFile" | tee "$canonicalMetadataFile.new"
+mv "$canonicalMetadataFile.new" "$canonicalMetadataFile"

+ 7 - 0
ghost/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
golang/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
gradle/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 7 - 0
groovy/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
haproxy/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 7 - 0
haskell/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
haxe/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 5 - 0
hello-world/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
hitch/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 7 - 0
httpd/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 7 - 0
hylang/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
ibm-semeru-runtimes/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
ibmjava/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
influxdb/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 5 - 0
irssi/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
jetty/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 5 - 0
jobber/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
joomla/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
jruby/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
julia/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
kapacitor/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 7 - 0
kibana/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 7 - 0
kong/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "api-management"
+    ]
+  }
+}

+ 7 - 0
lightstreamer/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "message-queues"
+    ]
+  }
+}

+ 7 - 0
liquibase/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "developer-tools"
+    ]
+  }
+}

+ 7 - 0
logstash/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 7 - 0
mageia/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "operating-systems"
+    ]
+  }
+}

+ 7 - 0
mariadb/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
matomo/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "monitoring-and-observability"
+    ]
+  }
+}

+ 7 - 0
maven/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
mediawiki/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
memcached/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 22 - 0
metadata.json

@@ -0,0 +1,22 @@
+{
+  "hub": {
+    "categories": [
+      "api-management",
+      "content-management-system",
+      "data-science",
+      "databases-and-storage",
+      "developer-tools",
+      "integration-and-delivery",
+      "internet-of-things",
+      "languages-and-frameworks",
+      "machine-learning-and-ai",
+      "message-queues",
+      "monitoring-and-observability",
+      "networking",
+      "operating-systems",
+      "security",
+      "web-analytics",
+      "web-servers"
+    ]
+  }
+}

+ 118 - 0
metadata.sh

@@ -0,0 +1,118 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+
+workdir="$(readlink -f "$BASH_SOURCE")"
+workdir="$(dirname "$workdir")"
+cd "$workdir"
+
+jsonFile='metadata.json'
+canonicalMetadataFile="./$jsonFile"
+export maxCategories=3
+
+self="$(basename "$0")"
+
+usage() {
+	cat <<EOUSAGE
+
+usage: $self [--write] REPO[...]
+   eg: $self debian
+       $self -w python
+
+This script checks a givens repo's metadata.json. It checks formating (providing a diff), checks categories, and can write the formatting changes.
+
+-h, --help          Print this help output and exit.
+-w, --write         Apply json formatting (run without to see the diff that would be applied).
+
+Arguments are the list of repos with a 'metadata.json' in them. 'metadata.json' is expected in every repo.
+'.' can also be passed to check the format of the canonical './metadata.json' at
+the root of the repo, but the max categories of '-c' is skipped for it.
+EOUSAGE
+}
+
+# arg handling
+opts="$(getopt -o 'hw' --long 'help,write' -- "$@" || { usage >&2 && false; })"
+eval set -- "$opts"
+
+write=
+
+while :; do
+	flag="$1"
+	shift
+	case "$flag" in
+		--help | -h) usage && exit 0 ;;
+		--write | -w) write=1 ;;
+		--) break ;;
+		*)
+			{
+				echo "error: unknown flag: $flag"
+				usage
+			} >&2
+			exit 1
+			;;
+	esac
+done
+
+repos=( "$@" )
+if [ "${#repos[@]}" -eq 0 ]; then
+	repos=( */ )
+fi
+repos=( "${repos[@]%/}" )
+
+failures=0
+for repo in "${repos[@]}"; do
+	repoFile="$repo/$jsonFile"
+	if [ ! -s "$repoFile" ]; then
+		echo >&2 "error: $repoFile does not exist or is empty"
+		(( failures++ )) || :
+		continue
+	fi
+
+	# sort object keys and pretty print with jq as our "cannonical json"
+	# sort categories array, no duplicates
+	if ! repoFileJson="$(jq -s --sort-keys '.[0] | .hub.categories |= unique' "$repoFile")"; then
+		echo >&2 "error parsing '$repoFile'; invalid JSON?"
+		(( failures++ )) || :
+		continue
+	fi
+	if ! filediff="$(diff -u "$repoFile" <(cat <<<"$repoFileJson"))"; then
+		cat <<<"$filediff"
+		if [ -n "$write" ]; then
+			cat <<<"$repoFileJson" > "$repoFile"
+		else
+			(( failures++ )) || :
+		fi
+	fi
+
+	# TODO also check for required keys and/or types?
+	# the canonicalMetadataFile doesn't have too many categories since it is the source of categories
+	# all other metadata.json files must not be more than maxCategories or have categories that aren't in the canonical set
+	if [ "$repoFile" != "$canonicalMetadataFile" ]; then
+		export repoFile
+		if errorText="$(jq -r --slurpfile canonical "$canonicalMetadataFile" '
+			.hub.categories
+			| (
+				length
+				| if . > (env.maxCategories | tonumber) then
+					"error: \(env.repoFile): too many categories: \(.) (max \(env.maxCategories))"
+				else empty end
+			),
+			(
+				. - $canonical[0].hub.categories
+				| if length > 0 then
+					"error: \(env.repoFile): unknown categories \(.)"
+				else empty end
+			)
+		' "$repoFile")"; then
+			if [ -n "$errorText" ]; then
+				echo >&2 "$errorText"
+				(( failures++ )) || :
+			fi
+		else
+			echo >&2 "error parsing '$repoFile'; invalid JSON?"
+			(( failures++ )) || :
+			continue
+		fi
+	fi
+done
+
+exit "$failures"

+ 7 - 0
mongo-express/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
mongo/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
monica/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
mono/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
mysql/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 5 - 0
nats-streaming/metadata.json

@@ -0,0 +1,5 @@
+{
+  "hub": {
+    "categories": []
+  }
+}

+ 7 - 0
nats/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "message-queues"
+    ]
+  }
+}

+ 7 - 0
neo4j/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "databases-and-storage"
+    ]
+  }
+}

+ 7 - 0
neurodebian/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "data-science"
+    ]
+  }
+}

+ 7 - 0
nextcloud/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

+ 7 - 0
nginx/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "web-servers"
+    ]
+  }
+}

+ 7 - 0
node/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "languages-and-frameworks"
+    ]
+  }
+}

+ 7 - 0
notary/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "security"
+    ]
+  }
+}

+ 7 - 0
odoo/metadata.json

@@ -0,0 +1,7 @@
+{
+  "hub": {
+    "categories": [
+      "content-management-system"
+    ]
+  }
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff